diff --git a/mod/quiz/amd/build/edit_multiple_grades.min.js b/mod/quiz/amd/build/edit_multiple_grades.min.js index 43a7ddbb34c..152c8998a46 100644 --- a/mod/quiz/amd/build/edit_multiple_grades.min.js +++ b/mod/quiz/amd/build/edit_multiple_grades.min.js @@ -5,6 +5,6 @@ define("mod_quiz/edit_multiple_grades",["exports","core/ajax","core/config","cor * @module mod_quiz/edit_multiple_grades * @copyright 2023 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_config=_interopRequireDefault(_config),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const SELECTORS_addGradeItemButton="#mod_quiz-add_grade_item",SELECTORS_autoSetupButton="#mod_quiz-grades_auto_setup",SELECTORS_editingPageContents="#edit_grading_page-contents",SELECTORS_gradeItemList="table#mod_quiz-grade-item-list",SELECTORS_gradeItemSelect="select[data-slot-id]",SELECTORS_gradeItemSelectId=id=>"select#grade-item-choice-"+id,SELECTORS_gradeItemTr="table#mod_quiz-grade-item-list tr[data-quiz-grade-item-id]",SELECTORS_inplaceEditable="span.inplaceeditable",SELECTORS_inplaceEditableOn="span.inplaceeditable.inplaceeditingon",SELECTORS_resetAllButton="#mod_quiz-grades_reset_all",SELECTORS_slotList="table#mod_quiz-slot-list",SELECTORS_updateGradeItemLink=id=>'tr[data-quiz-grade-item-id="'+id+'"] .quickeditlink',callServiceAndReturnRenderingData=methodCall=>callServicesAndReturnRenderingData([methodCall]),callServicesAndReturnRenderingData=methodCalls=>(methodCalls.push({methodname:"mod_quiz_get_edit_grading_page_data",args:{quizid:methodCalls[0].args.quizid}}),Promise.all((0,_ajax.call)(methodCalls)).then((results=>JSON.parse(results.at(-1))))),handleGradeItemDelete=e=>{e.preventDefault();const pending=new _pending.default("delete-quiz-grade-item"),tableCell=e.target.closest("td");(0,_loadingicon.addIconToContainer)(tableCell,pending);const tableRow=tableCell.closest("tr"),quizId=tableRow.closest("table").dataset.quizId,gradeItemId=tableRow.dataset.quizGradeItemId;let nextItemToFocus;nextItemToFocus=tableRow.nextElementSibling?SELECTORS_updateGradeItemLink(tableRow.nextElementSibling.dataset.quizGradeItemId):SELECTORS_addGradeItemButton,((quizId,gradeItemId)=>callServiceAndReturnRenderingData({methodname:"mod_quiz_delete_grade_items",args:{quizid:quizId,quizgradeitems:[{id:gradeItemId}]}}))(quizId,gradeItemId).then(reRenderPage).then((()=>{pending.resolve(),document.querySelector(nextItemToFocus).focus()})).catch(_notification.default.exception)},stopEditingGadeItem=editableSpan=>{editableSpan.innerHTML=editableSpan.dataset.oldContent,delete editableSpan.dataset.oldContent,editableSpan.classList.remove("inplaceeditingon"),editableSpan.querySelector("[data-action-edit]").focus()},handleGradeItemKeyDown=e=>{if(13!==e.keyCode)return;const editableSpan=e.target.closest(SELECTORS_inplaceEditableOn);if(!editableSpan||!editableSpan.closest(SELECTORS_gradeItemList))return;e.preventDefault();const pending=new _pending.default("edit-quiz-grade-item-save"),newName=editableSpan.querySelector("input").value,tableCell=e.target.closest("th");(0,_loadingicon.addIconToContainer)(tableCell);const tableRow=tableCell.closest("tr"),quizId=tableRow.closest("table").dataset.quizId,gradeItemId=tableRow.dataset.quizGradeItemId;((quizId,gradeItemId,newName)=>callServiceAndReturnRenderingData({methodname:"mod_quiz_update_grade_items",args:{quizid:quizId,quizgradeitems:[{id:gradeItemId,name:newName}]}}))(quizId,gradeItemId,newName).then(reRenderPage).then((()=>{pending.resolve(),document.querySelector(SELECTORS_updateGradeItemLink(gradeItemId)).focus({focusVisible:!0})})).catch(_notification.default.exception)},reRenderPage=editGradingPageData=>(0,_templates.render)("mod_quiz/edit_grading_page",editGradingPageData).then(((html,js)=>(0,_templates.replaceNode)(document.querySelector(SELECTORS_editingPageContents),html,js||""))),handleGradeItemKeyUp=e=>{if(27!==e.keyCode)return;const editableSpan=e.target.closest(SELECTORS_inplaceEditableOn);editableSpan&&editableSpan.closest(SELECTORS_gradeItemList)&&(e.preventDefault(),stopEditingGadeItem(editableSpan))},handleGradeItemFocusOut=e=>{if(_config.default.behatsiterunning)return;const editableSpan=e.target.closest(SELECTORS_inplaceEditableOn);editableSpan&&editableSpan.closest(SELECTORS_gradeItemList)&&(e.preventDefault(),stopEditingGadeItem(editableSpan))},handleSlotGradeItemChanged=e=>{const select=e.target.closest(SELECTORS_gradeItemSelect);if(!select||!select.closest(SELECTORS_slotList))return;e.preventDefault();const pending=new _pending.default("edit-slot-grade-item-updated"),slotId=select.dataset.slotId,newGradeItemId=select.value?select.value:null,tableCell=e.target.closest("td");(0,_loadingicon.addIconToContainer)(tableCell,pending);((quizId,slotId,gradeItemId)=>callServiceAndReturnRenderingData({methodname:"mod_quiz_update_slots",args:{quizid:quizId,slots:[{id:slotId,quizgradeitemid:gradeItemId}]}}))(tableCell.closest("table").dataset.quizId,slotId,newGradeItemId).then(reRenderPage).then((()=>{pending.resolve(),document.querySelector(SELECTORS_gradeItemSelectId(slotId)).focus()})).catch(_notification.default.exception)},handleGradeItemClick=e=>{const link=e.target.closest("a");link&&link.closest(SELECTORS_gradeItemList)&&(link.dataset.actionDelete&&handleGradeItemDelete(e),link.dataset.actionEdit&&(e=>{e.preventDefault();const pending=new _pending.default("edit-quiz-grade-item-start"),editableSpan=e.target.closest(SELECTORS_inplaceEditable);document.querySelectorAll(SELECTORS_inplaceEditableOn).forEach(stopEditingGadeItem),editableSpan.dataset.oldContent=editableSpan.innerHTML,(0,_str.get_string)("edittitleinstructions").then((instructions=>{const uniqueId="gi-edit-input-"+editableSpan.closest("tr").dataset.quizGradeItemId;editableSpan.innerHTML=''+instructions+'';const inputElement=editableSpan.querySelector("input");return inputElement.focus(),inputElement.select(),editableSpan.classList.add("inplaceeditingon"),pending.resolve(),null})).catch(_notification.default.exception)})(e))},handleButtonClick=e=>{e.target.closest(SELECTORS_addGradeItemButton)&&handleAddGradeItemClick(e),e.target.closest(SELECTORS_autoSetupButton)&&handleAutoSetup(e),e.target.closest(SELECTORS_resetAllButton)&&handleResetAllClick(e)},handleAddGradeItemClick=e=>{e.preventDefault();const pending=new _pending.default("create-quiz-grade-item");(0,_loadingicon.addIconToContainer)(e.target.parentNode,pending);const quizId=e.target.dataset.quizId;(0,_str.get_string)("gradeitemdefaultname","quiz").then((name=>((quizId,name)=>callServiceAndReturnRenderingData({methodname:"mod_quiz_create_grade_items",args:{quizid:quizId,quizgradeitems:[{name:name}]}}))(quizId,name))).then(reRenderPage).then((()=>{pending.resolve(),document.querySelector(SELECTORS_addGradeItemButton).focus()})).catch(_notification.default.exception)},handleAutoSetup=e=>{e.preventDefault();const pending=new _pending.default("setup-quiz-grade-items");(quizId=>callServiceAndReturnRenderingData({methodname:"mod_quiz_create_grade_item_per_section",args:{quizid:quizId}}))(e.target.dataset.quizId).then(reRenderPage).then((()=>{pending.resolve(),document.querySelector(SELECTORS_resetAllButton).focus()})).catch(_notification.default.exception)},handleResetAllClick=e=>{e.preventDefault();const button=e.target;_notification.default.deleteCancelPromise((0,_str.get_string)("gradeitemsremoveallconfirm","quiz"),(0,_str.get_string)("gradeitemsremoveallmessage","quiz"),(0,_str.get_string)("reset"),button).then((()=>reallyResetAll(button))).catch((()=>button.focus()))},reallyResetAll=button=>{const pending=new _pending.default("reset-quiz-grading");(0,_loadingicon.addIconToContainer)(button.parentNode,pending);const quizId=button.dataset.quizId;let methodCalls=[];const slotResets=[...document.querySelectorAll(SELECTORS_gradeItemSelect)].map((select=>({id:select.dataset.slotId,quizgradeitemid:0})));slotResets.length&&methodCalls.push({methodname:"mod_quiz_update_slots",args:{quizid:quizId,slots:slotResets}}),methodCalls.push({methodname:"mod_quiz_delete_grade_items",args:{quizid:quizId,quizgradeitems:[...document.querySelectorAll(SELECTORS_gradeItemTr)].map((tr=>({id:tr.dataset.quizGradeItemId})))}}),callServicesAndReturnRenderingData(methodCalls).then(reRenderPage).then((()=>{pending.resolve(),document.querySelector(SELECTORS_addGradeItemButton).focus()})).catch(_notification.default.exception)};_exports.init=()=>{document.body.addEventListener("click",handleGradeItemClick),document.body.addEventListener("keydown",handleGradeItemKeyDown),document.body.addEventListener("keyup",handleGradeItemKeyUp),document.body.addEventListener("focusout",handleGradeItemFocusOut),document.body.addEventListener("click",handleButtonClick),document.body.addEventListener("change",handleSlotGradeItemChanged)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_config=_interopRequireDefault(_config),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const SELECTORS_addGradeItemButton="#mod_quiz-add_grade_item",SELECTORS_autoSetupButton="#mod_quiz-grades_auto_setup",SELECTORS_editingPageContents="#edit_grading_page-contents",SELECTORS_gradeItemList="table#mod_quiz-grade-item-list",SELECTORS_gradeItemSelect="select[data-slot-id]",SELECTORS_gradeItemSelectId=id=>"select#grade-item-choice-"+id,SELECTORS_gradeItemTr="table#mod_quiz-grade-item-list tr[data-quiz-grade-item-id]",SELECTORS_inplaceEditable="span.inplaceeditable",SELECTORS_inplaceEditableOn="span.inplaceeditable.inplaceeditingon",SELECTORS_resetAllButton="#mod_quiz-grades_reset_all",SELECTORS_slotList="table#mod_quiz-slot-list",SELECTORS_updateGradeItemLink=id=>'tr[data-quiz-grade-item-id="'+id+'"] .quickeditlink',callServiceAndReturnRenderingData=methodCall=>callServicesAndReturnRenderingData([methodCall]),callServicesAndReturnRenderingData=methodCalls=>(methodCalls.push({methodname:"mod_quiz_get_edit_grading_page_data",args:{quizid:methodCalls[0].args.quizid}}),Promise.all((0,_ajax.call)(methodCalls)).then((results=>JSON.parse(results.at(-1))))),handleGradeItemDelete=e=>{e.preventDefault();const pending=new _pending.default("delete-quiz-grade-item"),tableCell=e.target.closest("td");(0,_loadingicon.addIconToContainer)(tableCell,pending);const tableRow=tableCell.closest("tr"),quizId=tableRow.closest("table").dataset.quizId,gradeItemId=tableRow.dataset.quizGradeItemId;let nextItemToFocus;nextItemToFocus=tableRow.nextElementSibling?SELECTORS_updateGradeItemLink(tableRow.nextElementSibling.dataset.quizGradeItemId):SELECTORS_addGradeItemButton,((quizId,gradeItemId)=>callServiceAndReturnRenderingData({methodname:"mod_quiz_delete_grade_items",args:{quizid:quizId,quizgradeitems:[{id:gradeItemId}]}}))(quizId,gradeItemId).then(reRenderPage).then((()=>{pending.resolve(),document.querySelector(nextItemToFocus).focus()})).catch(_notification.default.exception)},stopEditingGadeItem=editableSpan=>{editableSpan.innerHTML=editableSpan.dataset.oldContent,delete editableSpan.dataset.oldContent,editableSpan.classList.remove("inplaceeditingon"),editableSpan.querySelector("[data-action-edit]").focus()},handleGradeItemKeyDown=e=>{if(13!==e.keyCode)return;const editableSpan=e.target.closest(SELECTORS_inplaceEditableOn);if(!editableSpan||!editableSpan.closest(SELECTORS_gradeItemList))return;e.preventDefault();const pending=new _pending.default("edit-quiz-grade-item-save"),newName=editableSpan.querySelector("input").value,tableCell=e.target.closest("th");(0,_loadingicon.addIconToContainer)(tableCell);const tableRow=tableCell.closest("tr"),quizId=tableRow.closest("table").dataset.quizId,gradeItemId=tableRow.dataset.quizGradeItemId;((quizId,gradeItemId,newName)=>callServiceAndReturnRenderingData({methodname:"mod_quiz_update_grade_items",args:{quizid:quizId,quizgradeitems:[{id:gradeItemId,name:newName}]}}))(quizId,gradeItemId,newName).then(reRenderPage).then((()=>{pending.resolve(),document.querySelector(SELECTORS_updateGradeItemLink(gradeItemId)).focus({focusVisible:!0})})).catch(_notification.default.exception)},reRenderPage=editGradingPageData=>(0,_templates.render)("mod_quiz/edit_grading_page",editGradingPageData).then(((html,js)=>(0,_templates.replaceNode)(document.querySelector(SELECTORS_editingPageContents),html,js||""))),handleGradeItemKeyUp=e=>{if(27!==e.keyCode)return;const editableSpan=e.target.closest(SELECTORS_inplaceEditableOn);editableSpan&&editableSpan.closest(SELECTORS_gradeItemList)&&(e.preventDefault(),stopEditingGadeItem(editableSpan))},handleGradeItemFocusOut=e=>{if(_config.default.behatsiterunning)return;const editableSpan=e.target.closest(SELECTORS_inplaceEditableOn);editableSpan&&editableSpan.closest(SELECTORS_gradeItemList)&&(e.preventDefault(),stopEditingGadeItem(editableSpan))},handleSlotGradeItemChanged=e=>{const select=e.target.closest(SELECTORS_gradeItemSelect);if(!select||!select.closest(SELECTORS_slotList))return;e.preventDefault();const pending=new _pending.default("edit-slot-grade-item-updated"),slotId=select.dataset.slotId,newGradeItemId=select.value?select.value:null,tableCell=e.target.closest("td");(0,_loadingicon.addIconToContainer)(tableCell,pending);((quizId,slotId,gradeItemId)=>callServiceAndReturnRenderingData({methodname:"mod_quiz_update_slots",args:{quizid:quizId,slots:[{id:slotId,quizgradeitemid:gradeItemId}]}}))(tableCell.closest("table").dataset.quizId,slotId,newGradeItemId).then(reRenderPage).then((()=>{pending.resolve(),document.querySelector(SELECTORS_gradeItemSelectId(slotId)).focus()})).catch(_notification.default.exception)},handleGradeItemClick=e=>{const link=e.target.closest("a");link&&link.closest(SELECTORS_gradeItemList)&&(link.dataset.actionDelete&&handleGradeItemDelete(e),link.dataset.actionEdit&&(e=>{e.preventDefault();const pending=new _pending.default("edit-quiz-grade-item-start"),editableSpan=e.target.closest(SELECTORS_inplaceEditable);document.querySelectorAll(SELECTORS_inplaceEditableOn).forEach(stopEditingGadeItem),editableSpan.dataset.oldContent=editableSpan.innerHTML,(0,_str.get_string)("edittitleinstructions").then((instructions=>{const uniqueId="gi-edit-input-"+editableSpan.closest("tr").dataset.quizGradeItemId;editableSpan.innerHTML=''+instructions+'';const inputElement=editableSpan.querySelector("input");return inputElement.focus(),inputElement.select(),editableSpan.classList.add("inplaceeditingon"),pending.resolve(),null})).catch(_notification.default.exception)})(e))},handleButtonClick=e=>{e.target.closest(SELECTORS_addGradeItemButton)&&handleAddGradeItemClick(e),e.target.closest(SELECTORS_autoSetupButton)&&handleAutoSetup(e),e.target.closest(SELECTORS_resetAllButton)&&handleResetAllClick(e)},handleAddGradeItemClick=e=>{e.preventDefault();const pending=new _pending.default("create-quiz-grade-item");(0,_loadingicon.addIconToContainer)(e.target.parentNode,pending);(quizId=>callServiceAndReturnRenderingData({methodname:"mod_quiz_create_grade_items",args:{quizid:quizId,quizgradeitems:[{name:""}]}}))(e.target.dataset.quizId).then(reRenderPage).then((()=>{pending.resolve(),document.querySelector(SELECTORS_addGradeItemButton).focus()})).catch(_notification.default.exception)},handleAutoSetup=e=>{e.preventDefault();const pending=new _pending.default("setup-quiz-grade-items");(quizId=>callServiceAndReturnRenderingData({methodname:"mod_quiz_create_grade_item_per_section",args:{quizid:quizId}}))(e.target.dataset.quizId).then(reRenderPage).then((()=>{pending.resolve(),document.querySelector(SELECTORS_resetAllButton).focus()})).catch(_notification.default.exception)},handleResetAllClick=e=>{e.preventDefault();const button=e.target;_notification.default.deleteCancelPromise((0,_str.get_string)("gradeitemsremoveallconfirm","quiz"),(0,_str.get_string)("gradeitemsremoveallmessage","quiz"),(0,_str.get_string)("reset"),button).then((()=>reallyResetAll(button))).catch((()=>button.focus()))},reallyResetAll=button=>{const pending=new _pending.default("reset-quiz-grading");(0,_loadingicon.addIconToContainer)(button.parentNode,pending);const quizId=button.dataset.quizId;let methodCalls=[];const slotResets=[...document.querySelectorAll(SELECTORS_gradeItemSelect)].map((select=>({id:select.dataset.slotId,quizgradeitemid:0})));slotResets.length&&methodCalls.push({methodname:"mod_quiz_update_slots",args:{quizid:quizId,slots:slotResets}}),methodCalls.push({methodname:"mod_quiz_delete_grade_items",args:{quizid:quizId,quizgradeitems:[...document.querySelectorAll(SELECTORS_gradeItemTr)].map((tr=>({id:tr.dataset.quizGradeItemId})))}}),callServicesAndReturnRenderingData(methodCalls).then(reRenderPage).then((()=>{pending.resolve(),document.querySelector(SELECTORS_addGradeItemButton).focus()})).catch(_notification.default.exception)};_exports.init=()=>{document.body.addEventListener("click",handleGradeItemClick),document.body.addEventListener("keydown",handleGradeItemKeyDown),document.body.addEventListener("keyup",handleGradeItemKeyUp),document.body.addEventListener("focusout",handleGradeItemFocusOut),document.body.addEventListener("click",handleButtonClick),document.body.addEventListener("change",handleSlotGradeItemChanged)}})); //# sourceMappingURL=edit_multiple_grades.min.js.map \ No newline at end of file diff --git a/mod/quiz/amd/build/edit_multiple_grades.min.js.map b/mod/quiz/amd/build/edit_multiple_grades.min.js.map index 8014faa91c8..23ba44fb830 100644 --- a/mod/quiz/amd/build/edit_multiple_grades.min.js.map +++ b/mod/quiz/amd/build/edit_multiple_grades.min.js.map @@ -1 +1 @@ -{"version":3,"file":"edit_multiple_grades.min.js","sources":["../src/edit_multiple_grades.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * JavaScript for managing multiple grade items for a quiz.\n *\n * @module mod_quiz/edit_multiple_grades\n * @copyright 2023 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call as fetchMany} from 'core/ajax';\nimport MoodleConfig from 'core/config';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {get_string as getString} from 'core/str';\nimport {render as renderTemplate} from 'core/templates';\nimport {replaceNode} from 'core/templates';\n\n/**\n * @type {Object} selectors used in this code.\n */\nconst SELECTORS = {\n 'addGradeItemButton': '#mod_quiz-add_grade_item',\n 'autoSetupButton': '#mod_quiz-grades_auto_setup',\n 'editingPageContents': '#edit_grading_page-contents',\n 'gradeItemList': 'table#mod_quiz-grade-item-list',\n 'gradeItemSelect': 'select[data-slot-id]',\n 'gradeItemSelectId': (id) => 'select#grade-item-choice-' + id,\n 'gradeItemTr': 'table#mod_quiz-grade-item-list tr[data-quiz-grade-item-id]',\n 'inplaceEditable': 'span.inplaceeditable',\n 'inplaceEditableOn': 'span.inplaceeditable.inplaceeditingon',\n 'resetAllButton': '#mod_quiz-grades_reset_all',\n 'slotList': 'table#mod_quiz-slot-list',\n 'updateGradeItemLink': (id) => 'tr[data-quiz-grade-item-id=\"' + id + '\"] .quickeditlink',\n};\n\n/**\n * Call the Ajax service to create a quiz grade item.\n *\n * @param {Number} quizId id of the quiz to update.\n * @param {String} name name of the grade item to create.\n * @returns {Promise} a promise that resolves to the template context required to re-render the page.\n */\nconst createGradeItem = (\n quizId,\n name\n) => callServiceAndReturnRenderingData({\n methodname: 'mod_quiz_create_grade_items',\n args: {\n quizid: quizId,\n quizgradeitems: [{name: name}],\n }\n});\n\n/**\n * Call the Ajax service to update a quiz grade item.\n *\n * @param {Number} quizId id of the quiz to update.\n * @param {Number} gradeItemId id of the grade item to update.\n * @param {String} newName the new name to set.\n * @return {Promise} Promise that resolves to the context required to re-render the page.\n */\nconst updateGradeItem = (\n quizId,\n gradeItemId,\n newName\n) => callServiceAndReturnRenderingData({\n methodname: 'mod_quiz_update_grade_items',\n args: {\n quizid: quizId,\n quizgradeitems: [{id: gradeItemId, name: newName}],\n }\n});\n\n/**\n * Call the Ajax service to delete a quiz grade item.\n *\n * @param {Number} quizId id of the quiz to update.\n * @param {Number} gradeItemId id of the grade item to delete.\n * @return {Promise} Promise that resolves to the context required to re-render the page.\n */\nconst deleteGradeItem = (\n quizId,\n gradeItemId\n) => callServiceAndReturnRenderingData({\n methodname: 'mod_quiz_delete_grade_items',\n args: {\n quizid: quizId,\n quizgradeitems: [{id: gradeItemId}],\n }\n});\n\n/**\n * Call the Ajax service to update the quiz grade item used by a slot.\n *\n * @param {Number} quizId id of the quiz to update.\n * @param {Number} slotId id of the slot to update.\n * @param {Number|null} gradeItemId new grade item ot set, or null to un-set.\n * @return {Promise} Promise that resolves to the context required to re-render the page.\n */\nconst updateSlotGradeItem = (\n quizId,\n slotId,\n gradeItemId\n) => callServiceAndReturnRenderingData({\n methodname: 'mod_quiz_update_slots',\n args: {\n quizid: quizId,\n slots: [{id: slotId, quizgradeitemid: gradeItemId}],\n }\n});\n\n/**\n * Call the Ajax service to setup one grade item for each quiz section.\n *\n * @param {Number} quizId id of the quiz to update.\n * @return {Promise} Promise that resolves to the context required to re-render the page.\n */\nconst autoSetupGradeItems = (\n quizId\n) => callServiceAndReturnRenderingData({\n methodname: 'mod_quiz_create_grade_item_per_section',\n args: {\n quizid: quizId\n }\n});\n\n/**\n * Make a web service call, and also call mod_quiz_get_edit_grading_page_data to get the date to re-render the page.\n *\n * @param {Object} methodCall a web service call to pass to fetchMany. Must include methodCall.args.quizid.\n * @returns {Promise} a promise that resolves to the template context required to re-render the page.\n */\nconst callServiceAndReturnRenderingData = (methodCall) => callServicesAndReturnRenderingData([methodCall]);\n\n/**\n * Make a web service call, and also call mod_quiz_get_edit_grading_page_data to get the date to re-render the page.\n *\n * @param {Object[]} methodCalls web service calls to pass to fetchMany. Must include methodCalls[0].args.quizid.\n * @returns {Promise} a promise that resolves to the template context required to re-render the page.\n */\nconst callServicesAndReturnRenderingData = (methodCalls) => {\n methodCalls.push({\n methodname: 'mod_quiz_get_edit_grading_page_data',\n args: {\n quizid: methodCalls[0].args.quizid,\n }\n });\n return Promise.all(fetchMany(methodCalls))\n .then(results => JSON.parse(results.at(-1)));\n};\n\n/**\n * Handle click events on the delete icon.\n *\n * @param {Event} e click event.\n */\nconst handleGradeItemDelete = (e) => {\n e.preventDefault();\n const pending = new Pending('delete-quiz-grade-item');\n\n const tableCell = e.target.closest('td');\n addIconToContainer(tableCell, pending);\n\n const tableRow = tableCell.closest('tr');\n const quizId = tableRow.closest('table').dataset.quizId;\n const gradeItemId = tableRow.dataset.quizGradeItemId;\n\n let nextItemToFocus;\n if (tableRow.nextElementSibling) {\n nextItemToFocus = SELECTORS.updateGradeItemLink(tableRow.nextElementSibling.dataset.quizGradeItemId);\n } else {\n nextItemToFocus = SELECTORS.addGradeItemButton;\n }\n\n deleteGradeItem(quizId, gradeItemId)\n .then(reRenderPage)\n .then(() => {\n pending.resolve();\n document.querySelector(nextItemToFocus).focus();\n })\n .catch(Notification.exception);\n};\n\n/**\n *\n * @param {HTMLElement} editableSpan the editable to turn off.\n */\nconst stopEditingGadeItem = (editableSpan) => {\n editableSpan.innerHTML = editableSpan.dataset.oldContent;\n delete editableSpan.dataset.oldContent;\n\n editableSpan.classList.remove('inplaceeditingon');\n editableSpan.querySelector('[data-action-edit]').focus();\n};\n\n/**\n * Handle click events on the start rename icon.\n *\n * @param {Event} e click event.\n */\nconst handleGradeItemEditStart = (e) => {\n e.preventDefault();\n const pending = new Pending('edit-quiz-grade-item-start');\n const editableSpan = e.target.closest(SELECTORS.inplaceEditable);\n\n document.querySelectorAll(SELECTORS.inplaceEditableOn).forEach(stopEditingGadeItem);\n\n editableSpan.dataset.oldContent = editableSpan.innerHTML;\n getString('edittitleinstructions')\n .then((instructions) => {\n const uniqueId = 'gi-edit-input-' + editableSpan.closest('tr').dataset.quizGradeItemId;\n editableSpan.innerHTML = '' + instructions + '' +\n '' +\n '';\n\n const inputElement = editableSpan.querySelector('input');\n inputElement.focus();\n inputElement.select();\n editableSpan.classList.add('inplaceeditingon');\n pending.resolve();\n return null;\n })\n .catch(Notification.exception);\n};\n\n/**\n * Handle key down in the editable.\n *\n * @param {Event} e key event.\n */\nconst handleGradeItemKeyDown = (e) => {\n if (e.keyCode !== 13) {\n return;\n }\n\n const editableSpan = e.target.closest(SELECTORS.inplaceEditableOn);\n\n // Check this click is on a relevant element.\n if (!editableSpan || !editableSpan.closest(SELECTORS.gradeItemList)) {\n return;\n }\n\n e.preventDefault();\n const pending = new Pending('edit-quiz-grade-item-save');\n\n const newName = editableSpan.querySelector('input').value;\n const tableCell = e.target.closest('th');\n addIconToContainer(tableCell);\n\n const tableRow = tableCell.closest('tr');\n const quizId = tableRow.closest('table').dataset.quizId;\n const gradeItemId = tableRow.dataset.quizGradeItemId;\n\n updateGradeItem(quizId, gradeItemId, newName)\n .then(reRenderPage)\n .then(() => {\n pending.resolve();\n document.querySelector(SELECTORS.updateGradeItemLink(gradeItemId)).focus({'focusVisible': true});\n })\n .catch(Notification.exception);\n};\n\n/**\n * Replace the contents of the page with the page re-rendered from the provided data, once that promise resolves.\n *\n * @param {Object} editGradingPageData the template context data required to re-render the page.\n * @returns {Promise} a promise that will resolve when the page is updated.\n */\nconst reRenderPage = (editGradingPageData) =>\n renderTemplate('mod_quiz/edit_grading_page', editGradingPageData)\n .then((html, js) => replaceNode(document.querySelector(SELECTORS.editingPageContents), html, js || ''));\n\n/**\n * Handle key up in the editable.\n *\n * @param {Event} e key event.\n */\nconst handleGradeItemKeyUp = (e) => {\n if (e.keyCode !== 27) {\n return;\n }\n\n const editableSpan = e.target.closest(SELECTORS.inplaceEditableOn);\n\n // Check this click is on a relevant element.\n if (!editableSpan || !editableSpan.closest(SELECTORS.gradeItemList)) {\n return;\n }\n\n e.preventDefault();\n stopEditingGadeItem(editableSpan);\n};\n\n/**\n * Handle focus out of the editable.\n *\n * @param {Event} e event.\n */\nconst handleGradeItemFocusOut = (e) => {\n if (MoodleConfig.behatsiterunning) {\n // Behat triggers focusout too often so ignore.\n return;\n }\n\n const editableSpan = e.target.closest(SELECTORS.inplaceEditableOn);\n\n // Check this click is on a relevant element.\n if (!editableSpan || !editableSpan.closest(SELECTORS.gradeItemList)) {\n return;\n }\n\n e.preventDefault();\n stopEditingGadeItem(editableSpan);\n};\n\n/**\n * Handle when the selected grade item for a slot is changed.\n *\n * @param {Event} e event.\n */\nconst handleSlotGradeItemChanged = (e) => {\n const select = e.target.closest(SELECTORS.gradeItemSelect);\n\n // Check this click is on a relevant element.\n if (!select || !select.closest(SELECTORS.slotList)) {\n return;\n }\n\n e.preventDefault();\n const pending = new Pending('edit-slot-grade-item-updated');\n\n const slotId = select.dataset.slotId;\n const newGradeItemId = select.value ? select.value : null;\n const tableCell = e.target.closest('td');\n addIconToContainer(tableCell, pending);\n\n const quizId = tableCell.closest('table').dataset.quizId;\n\n updateSlotGradeItem(quizId, slotId, newGradeItemId)\n .then(reRenderPage)\n .then(() => {\n pending.resolve();\n document.querySelector(SELECTORS.gradeItemSelectId(slotId)).focus();\n })\n .catch(Notification.exception);\n};\n\n/**\n * Handle clicks in the table the shows the grade items.\n *\n * @param {Event} e click event.\n */\nconst handleGradeItemClick = (e) => {\n const link = e.target.closest('a');\n\n // Check this click is on a relevant element.\n if (!link || !link.closest(SELECTORS.gradeItemList)) {\n return;\n }\n\n if (link.dataset.actionDelete) {\n handleGradeItemDelete(e);\n }\n\n if (link.dataset.actionEdit) {\n handleGradeItemEditStart(e);\n }\n};\n\n/**\n * Handle clicks on the buttons.\n *\n * @param {Event} e click event.\n */\n\nconst handleButtonClick = (e) => {\n if (e.target.closest(SELECTORS.addGradeItemButton)) {\n handleAddGradeItemClick(e);\n }\n if (e.target.closest(SELECTORS.autoSetupButton)) {\n handleAutoSetup(e);\n }\n if (e.target.closest(SELECTORS.resetAllButton)) {\n handleResetAllClick(e);\n }\n};\n\n/**\n * Handle clicks on the 'Add grade item' button.\n *\n * @param {Event} e click event.\n */\nconst handleAddGradeItemClick = (e) => {\n e.preventDefault();\n const pending = new Pending('create-quiz-grade-item');\n addIconToContainer(e.target.parentNode, pending);\n\n const quizId = e.target.dataset.quizId;\n\n getString('gradeitemdefaultname', 'quiz')\n .then((name) => createGradeItem(quizId, name))\n .then(reRenderPage)\n .then(() => {\n pending.resolve();\n document.querySelector(SELECTORS.addGradeItemButton).focus();\n })\n .catch(Notification.exception);\n};\n\n/**\n * Handle clicks on the reset button - show a confirmation.\n *\n * @param {Event} e click event.\n */\nconst handleAutoSetup = (e) => {\n e.preventDefault();\n const pending = new Pending('setup-quiz-grade-items');\n\n const quizId = e.target.dataset.quizId;\n\n autoSetupGradeItems(quizId)\n .then(reRenderPage)\n .then(() => {\n pending.resolve();\n document.querySelector(SELECTORS.resetAllButton).focus();\n })\n .catch(Notification.exception);\n};\n\n/**\n * Handle clicks on the reset button - show a confirmation.\n *\n * @param {Event} e click event.\n */\nconst handleResetAllClick = (e) => {\n e.preventDefault();\n const button = e.target;\n\n Notification.deleteCancelPromise(\n getString('gradeitemsremoveallconfirm', 'quiz'),\n getString('gradeitemsremoveallmessage', 'quiz'),\n getString('reset'),\n button\n ).then(() => reallyResetAll(button))\n .catch(() => button.focus());\n};\n\n/**\n * Really reset all if the confirmation is OKed.\n *\n * @param {HTMLElement} button the reset button.\n */\nconst reallyResetAll = (button) => {\n const pending = new Pending('reset-quiz-grading');\n addIconToContainer(button.parentNode, pending);\n\n const quizId = button.dataset.quizId;\n\n let methodCalls = [];\n\n // Call to clear any assignments of grade items to slots (if required).\n const slotResets = [...document.querySelectorAll(SELECTORS.gradeItemSelect)].map(\n (select) => ({\n id: select.dataset.slotId,\n quizgradeitemid: 0,\n }));\n if (slotResets.length) {\n methodCalls.push({\n methodname: 'mod_quiz_update_slots',\n args: {\n quizid: quizId,\n slots: slotResets\n }\n });\n }\n\n // Request to delete all the grade items.\n methodCalls.push({\n methodname: 'mod_quiz_delete_grade_items',\n args: {\n quizid: quizId,\n quizgradeitems: [...document.querySelectorAll(SELECTORS.gradeItemTr)].map((tr) => {\n return {id: tr.dataset.quizGradeItemId};\n })\n }\n });\n\n callServicesAndReturnRenderingData(methodCalls)\n .then(reRenderPage)\n .then(() => {\n pending.resolve();\n document.querySelector(SELECTORS.addGradeItemButton).focus();\n })\n .catch(Notification.exception);\n};\n\n/**\n * Replace the container with a new version.\n */\nconst registerEventListeners = () => {\n document.body.addEventListener('click', handleGradeItemClick);\n document.body.addEventListener('keydown', handleGradeItemKeyDown);\n document.body.addEventListener('keyup', handleGradeItemKeyUp);\n document.body.addEventListener('focusout', handleGradeItemFocusOut);\n\n document.body.addEventListener('click', handleButtonClick);\n\n document.body.addEventListener('change', handleSlotGradeItemChanged);\n};\n\n/**\n * Entry point.\n */\nexport const init = () => {\n registerEventListeners();\n};\n"],"names":["SELECTORS","id","callServiceAndReturnRenderingData","methodCall","callServicesAndReturnRenderingData","methodCalls","push","methodname","args","quizid","Promise","all","then","results","JSON","parse","at","handleGradeItemDelete","e","preventDefault","pending","Pending","tableCell","target","closest","tableRow","quizId","dataset","gradeItemId","quizGradeItemId","nextItemToFocus","nextElementSibling","quizgradeitems","deleteGradeItem","reRenderPage","resolve","document","querySelector","focus","catch","Notification","exception","stopEditingGadeItem","editableSpan","innerHTML","oldContent","classList","remove","handleGradeItemKeyDown","keyCode","newName","value","name","updateGradeItem","editGradingPageData","html","js","handleGradeItemKeyUp","handleGradeItemFocusOut","MoodleConfig","behatsiterunning","handleSlotGradeItemChanged","select","slotId","newGradeItemId","slots","quizgradeitemid","updateSlotGradeItem","handleGradeItemClick","link","actionDelete","actionEdit","querySelectorAll","forEach","instructions","uniqueId","editLabel","rawName","inputElement","add","handleGradeItemEditStart","handleButtonClick","handleAddGradeItemClick","handleAutoSetup","handleResetAllClick","parentNode","createGradeItem","autoSetupGradeItems","button","deleteCancelPromise","reallyResetAll","slotResets","map","length","tr","body","addEventListener"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"edit_multiple_grades.min.js","sources":["../src/edit_multiple_grades.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * JavaScript for managing multiple grade items for a quiz.\n *\n * @module mod_quiz/edit_multiple_grades\n * @copyright 2023 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call as fetchMany} from 'core/ajax';\nimport MoodleConfig from 'core/config';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {get_string as getString} from 'core/str';\nimport {render as renderTemplate} from 'core/templates';\nimport {replaceNode} from 'core/templates';\n\n/**\n * @type {Object} selectors used in this code.\n */\nconst SELECTORS = {\n 'addGradeItemButton': '#mod_quiz-add_grade_item',\n 'autoSetupButton': '#mod_quiz-grades_auto_setup',\n 'editingPageContents': '#edit_grading_page-contents',\n 'gradeItemList': 'table#mod_quiz-grade-item-list',\n 'gradeItemSelect': 'select[data-slot-id]',\n 'gradeItemSelectId': (id) => 'select#grade-item-choice-' + id,\n 'gradeItemTr': 'table#mod_quiz-grade-item-list tr[data-quiz-grade-item-id]',\n 'inplaceEditable': 'span.inplaceeditable',\n 'inplaceEditableOn': 'span.inplaceeditable.inplaceeditingon',\n 'resetAllButton': '#mod_quiz-grades_reset_all',\n 'slotList': 'table#mod_quiz-slot-list',\n 'updateGradeItemLink': (id) => 'tr[data-quiz-grade-item-id=\"' + id + '\"] .quickeditlink',\n};\n\n/**\n * Call the Ajax service to create a quiz grade item.\n *\n * @param {Number} quizId id of the quiz to update.\n * @returns {Promise} a promise that resolves to the template context required to re-render the page.\n */\nconst createGradeItem = (\n quizId,\n) => callServiceAndReturnRenderingData({\n methodname: 'mod_quiz_create_grade_items',\n args: {\n quizid: quizId,\n quizgradeitems: [{name: ''}],\n }\n});\n\n/**\n * Call the Ajax service to update a quiz grade item.\n *\n * @param {Number} quizId id of the quiz to update.\n * @param {Number} gradeItemId id of the grade item to update.\n * @param {String} newName the new name to set.\n * @return {Promise} Promise that resolves to the context required to re-render the page.\n */\nconst updateGradeItem = (\n quizId,\n gradeItemId,\n newName\n) => callServiceAndReturnRenderingData({\n methodname: 'mod_quiz_update_grade_items',\n args: {\n quizid: quizId,\n quizgradeitems: [{id: gradeItemId, name: newName}],\n }\n});\n\n/**\n * Call the Ajax service to delete a quiz grade item.\n *\n * @param {Number} quizId id of the quiz to update.\n * @param {Number} gradeItemId id of the grade item to delete.\n * @return {Promise} Promise that resolves to the context required to re-render the page.\n */\nconst deleteGradeItem = (\n quizId,\n gradeItemId\n) => callServiceAndReturnRenderingData({\n methodname: 'mod_quiz_delete_grade_items',\n args: {\n quizid: quizId,\n quizgradeitems: [{id: gradeItemId}],\n }\n});\n\n/**\n * Call the Ajax service to update the quiz grade item used by a slot.\n *\n * @param {Number} quizId id of the quiz to update.\n * @param {Number} slotId id of the slot to update.\n * @param {Number|null} gradeItemId new grade item ot set, or null to un-set.\n * @return {Promise} Promise that resolves to the context required to re-render the page.\n */\nconst updateSlotGradeItem = (\n quizId,\n slotId,\n gradeItemId\n) => callServiceAndReturnRenderingData({\n methodname: 'mod_quiz_update_slots',\n args: {\n quizid: quizId,\n slots: [{id: slotId, quizgradeitemid: gradeItemId}],\n }\n});\n\n/**\n * Call the Ajax service to setup one grade item for each quiz section.\n *\n * @param {Number} quizId id of the quiz to update.\n * @return {Promise} Promise that resolves to the context required to re-render the page.\n */\nconst autoSetupGradeItems = (\n quizId\n) => callServiceAndReturnRenderingData({\n methodname: 'mod_quiz_create_grade_item_per_section',\n args: {\n quizid: quizId\n }\n});\n\n/**\n * Make a web service call, and also call mod_quiz_get_edit_grading_page_data to get the date to re-render the page.\n *\n * @param {Object} methodCall a web service call to pass to fetchMany. Must include methodCall.args.quizid.\n * @returns {Promise} a promise that resolves to the template context required to re-render the page.\n */\nconst callServiceAndReturnRenderingData = (methodCall) => callServicesAndReturnRenderingData([methodCall]);\n\n/**\n * Make a web service call, and also call mod_quiz_get_edit_grading_page_data to get the date to re-render the page.\n *\n * @param {Object[]} methodCalls web service calls to pass to fetchMany. Must include methodCalls[0].args.quizid.\n * @returns {Promise} a promise that resolves to the template context required to re-render the page.\n */\nconst callServicesAndReturnRenderingData = (methodCalls) => {\n methodCalls.push({\n methodname: 'mod_quiz_get_edit_grading_page_data',\n args: {\n quizid: methodCalls[0].args.quizid,\n }\n });\n return Promise.all(fetchMany(methodCalls))\n .then(results => JSON.parse(results.at(-1)));\n};\n\n/**\n * Handle click events on the delete icon.\n *\n * @param {Event} e click event.\n */\nconst handleGradeItemDelete = (e) => {\n e.preventDefault();\n const pending = new Pending('delete-quiz-grade-item');\n\n const tableCell = e.target.closest('td');\n addIconToContainer(tableCell, pending);\n\n const tableRow = tableCell.closest('tr');\n const quizId = tableRow.closest('table').dataset.quizId;\n const gradeItemId = tableRow.dataset.quizGradeItemId;\n\n let nextItemToFocus;\n if (tableRow.nextElementSibling) {\n nextItemToFocus = SELECTORS.updateGradeItemLink(tableRow.nextElementSibling.dataset.quizGradeItemId);\n } else {\n nextItemToFocus = SELECTORS.addGradeItemButton;\n }\n\n deleteGradeItem(quizId, gradeItemId)\n .then(reRenderPage)\n .then(() => {\n pending.resolve();\n document.querySelector(nextItemToFocus).focus();\n })\n .catch(Notification.exception);\n};\n\n/**\n *\n * @param {HTMLElement} editableSpan the editable to turn off.\n */\nconst stopEditingGadeItem = (editableSpan) => {\n editableSpan.innerHTML = editableSpan.dataset.oldContent;\n delete editableSpan.dataset.oldContent;\n\n editableSpan.classList.remove('inplaceeditingon');\n editableSpan.querySelector('[data-action-edit]').focus();\n};\n\n/**\n * Handle click events on the start rename icon.\n *\n * @param {Event} e click event.\n */\nconst handleGradeItemEditStart = (e) => {\n e.preventDefault();\n const pending = new Pending('edit-quiz-grade-item-start');\n const editableSpan = e.target.closest(SELECTORS.inplaceEditable);\n\n document.querySelectorAll(SELECTORS.inplaceEditableOn).forEach(stopEditingGadeItem);\n\n editableSpan.dataset.oldContent = editableSpan.innerHTML;\n getString('edittitleinstructions')\n .then((instructions) => {\n const uniqueId = 'gi-edit-input-' + editableSpan.closest('tr').dataset.quizGradeItemId;\n editableSpan.innerHTML = '' + instructions + '' +\n '' +\n '';\n\n const inputElement = editableSpan.querySelector('input');\n inputElement.focus();\n inputElement.select();\n editableSpan.classList.add('inplaceeditingon');\n pending.resolve();\n return null;\n })\n .catch(Notification.exception);\n};\n\n/**\n * Handle key down in the editable.\n *\n * @param {Event} e key event.\n */\nconst handleGradeItemKeyDown = (e) => {\n if (e.keyCode !== 13) {\n return;\n }\n\n const editableSpan = e.target.closest(SELECTORS.inplaceEditableOn);\n\n // Check this click is on a relevant element.\n if (!editableSpan || !editableSpan.closest(SELECTORS.gradeItemList)) {\n return;\n }\n\n e.preventDefault();\n const pending = new Pending('edit-quiz-grade-item-save');\n\n const newName = editableSpan.querySelector('input').value;\n const tableCell = e.target.closest('th');\n addIconToContainer(tableCell);\n\n const tableRow = tableCell.closest('tr');\n const quizId = tableRow.closest('table').dataset.quizId;\n const gradeItemId = tableRow.dataset.quizGradeItemId;\n\n updateGradeItem(quizId, gradeItemId, newName)\n .then(reRenderPage)\n .then(() => {\n pending.resolve();\n document.querySelector(SELECTORS.updateGradeItemLink(gradeItemId)).focus({'focusVisible': true});\n })\n .catch(Notification.exception);\n};\n\n/**\n * Replace the contents of the page with the page re-rendered from the provided data, once that promise resolves.\n *\n * @param {Object} editGradingPageData the template context data required to re-render the page.\n * @returns {Promise} a promise that will resolve when the page is updated.\n */\nconst reRenderPage = (editGradingPageData) =>\n renderTemplate('mod_quiz/edit_grading_page', editGradingPageData)\n .then((html, js) => replaceNode(document.querySelector(SELECTORS.editingPageContents), html, js || ''));\n\n/**\n * Handle key up in the editable.\n *\n * @param {Event} e key event.\n */\nconst handleGradeItemKeyUp = (e) => {\n if (e.keyCode !== 27) {\n return;\n }\n\n const editableSpan = e.target.closest(SELECTORS.inplaceEditableOn);\n\n // Check this click is on a relevant element.\n if (!editableSpan || !editableSpan.closest(SELECTORS.gradeItemList)) {\n return;\n }\n\n e.preventDefault();\n stopEditingGadeItem(editableSpan);\n};\n\n/**\n * Handle focus out of the editable.\n *\n * @param {Event} e event.\n */\nconst handleGradeItemFocusOut = (e) => {\n if (MoodleConfig.behatsiterunning) {\n // Behat triggers focusout too often so ignore.\n return;\n }\n\n const editableSpan = e.target.closest(SELECTORS.inplaceEditableOn);\n\n // Check this click is on a relevant element.\n if (!editableSpan || !editableSpan.closest(SELECTORS.gradeItemList)) {\n return;\n }\n\n e.preventDefault();\n stopEditingGadeItem(editableSpan);\n};\n\n/**\n * Handle when the selected grade item for a slot is changed.\n *\n * @param {Event} e event.\n */\nconst handleSlotGradeItemChanged = (e) => {\n const select = e.target.closest(SELECTORS.gradeItemSelect);\n\n // Check this click is on a relevant element.\n if (!select || !select.closest(SELECTORS.slotList)) {\n return;\n }\n\n e.preventDefault();\n const pending = new Pending('edit-slot-grade-item-updated');\n\n const slotId = select.dataset.slotId;\n const newGradeItemId = select.value ? select.value : null;\n const tableCell = e.target.closest('td');\n addIconToContainer(tableCell, pending);\n\n const quizId = tableCell.closest('table').dataset.quizId;\n\n updateSlotGradeItem(quizId, slotId, newGradeItemId)\n .then(reRenderPage)\n .then(() => {\n pending.resolve();\n document.querySelector(SELECTORS.gradeItemSelectId(slotId)).focus();\n })\n .catch(Notification.exception);\n};\n\n/**\n * Handle clicks in the table the shows the grade items.\n *\n * @param {Event} e click event.\n */\nconst handleGradeItemClick = (e) => {\n const link = e.target.closest('a');\n\n // Check this click is on a relevant element.\n if (!link || !link.closest(SELECTORS.gradeItemList)) {\n return;\n }\n\n if (link.dataset.actionDelete) {\n handleGradeItemDelete(e);\n }\n\n if (link.dataset.actionEdit) {\n handleGradeItemEditStart(e);\n }\n};\n\n/**\n * Handle clicks on the buttons.\n *\n * @param {Event} e click event.\n */\n\nconst handleButtonClick = (e) => {\n if (e.target.closest(SELECTORS.addGradeItemButton)) {\n handleAddGradeItemClick(e);\n }\n if (e.target.closest(SELECTORS.autoSetupButton)) {\n handleAutoSetup(e);\n }\n if (e.target.closest(SELECTORS.resetAllButton)) {\n handleResetAllClick(e);\n }\n};\n\n/**\n * Handle clicks on the 'Add grade item' button.\n *\n * @param {Event} e click event.\n */\nconst handleAddGradeItemClick = (e) => {\n e.preventDefault();\n const pending = new Pending('create-quiz-grade-item');\n addIconToContainer(e.target.parentNode, pending);\n\n const quizId = e.target.dataset.quizId;\n\n createGradeItem(quizId)\n .then(reRenderPage)\n .then(() => {\n pending.resolve();\n document.querySelector(SELECTORS.addGradeItemButton).focus();\n })\n .catch(Notification.exception);\n};\n\n/**\n * Handle clicks on the reset button - show a confirmation.\n *\n * @param {Event} e click event.\n */\nconst handleAutoSetup = (e) => {\n e.preventDefault();\n const pending = new Pending('setup-quiz-grade-items');\n\n const quizId = e.target.dataset.quizId;\n\n autoSetupGradeItems(quizId)\n .then(reRenderPage)\n .then(() => {\n pending.resolve();\n document.querySelector(SELECTORS.resetAllButton).focus();\n })\n .catch(Notification.exception);\n};\n\n/**\n * Handle clicks on the reset button - show a confirmation.\n *\n * @param {Event} e click event.\n */\nconst handleResetAllClick = (e) => {\n e.preventDefault();\n const button = e.target;\n\n Notification.deleteCancelPromise(\n getString('gradeitemsremoveallconfirm', 'quiz'),\n getString('gradeitemsremoveallmessage', 'quiz'),\n getString('reset'),\n button\n ).then(() => reallyResetAll(button))\n .catch(() => button.focus());\n};\n\n/**\n * Really reset all if the confirmation is OKed.\n *\n * @param {HTMLElement} button the reset button.\n */\nconst reallyResetAll = (button) => {\n const pending = new Pending('reset-quiz-grading');\n addIconToContainer(button.parentNode, pending);\n\n const quizId = button.dataset.quizId;\n\n let methodCalls = [];\n\n // Call to clear any assignments of grade items to slots (if required).\n const slotResets = [...document.querySelectorAll(SELECTORS.gradeItemSelect)].map(\n (select) => ({\n id: select.dataset.slotId,\n quizgradeitemid: 0,\n }));\n if (slotResets.length) {\n methodCalls.push({\n methodname: 'mod_quiz_update_slots',\n args: {\n quizid: quizId,\n slots: slotResets\n }\n });\n }\n\n // Request to delete all the grade items.\n methodCalls.push({\n methodname: 'mod_quiz_delete_grade_items',\n args: {\n quizid: quizId,\n quizgradeitems: [...document.querySelectorAll(SELECTORS.gradeItemTr)].map((tr) => {\n return {id: tr.dataset.quizGradeItemId};\n })\n }\n });\n\n callServicesAndReturnRenderingData(methodCalls)\n .then(reRenderPage)\n .then(() => {\n pending.resolve();\n document.querySelector(SELECTORS.addGradeItemButton).focus();\n })\n .catch(Notification.exception);\n};\n\n/**\n * Replace the container with a new version.\n */\nconst registerEventListeners = () => {\n document.body.addEventListener('click', handleGradeItemClick);\n document.body.addEventListener('keydown', handleGradeItemKeyDown);\n document.body.addEventListener('keyup', handleGradeItemKeyUp);\n document.body.addEventListener('focusout', handleGradeItemFocusOut);\n\n document.body.addEventListener('click', handleButtonClick);\n\n document.body.addEventListener('change', handleSlotGradeItemChanged);\n};\n\n/**\n * Entry point.\n */\nexport const init = () => {\n registerEventListeners();\n};\n"],"names":["SELECTORS","id","callServiceAndReturnRenderingData","methodCall","callServicesAndReturnRenderingData","methodCalls","push","methodname","args","quizid","Promise","all","then","results","JSON","parse","at","handleGradeItemDelete","e","preventDefault","pending","Pending","tableCell","target","closest","tableRow","quizId","dataset","gradeItemId","quizGradeItemId","nextItemToFocus","nextElementSibling","quizgradeitems","deleteGradeItem","reRenderPage","resolve","document","querySelector","focus","catch","Notification","exception","stopEditingGadeItem","editableSpan","innerHTML","oldContent","classList","remove","handleGradeItemKeyDown","keyCode","newName","value","name","updateGradeItem","editGradingPageData","html","js","handleGradeItemKeyUp","handleGradeItemFocusOut","MoodleConfig","behatsiterunning","handleSlotGradeItemChanged","select","slotId","newGradeItemId","slots","quizgradeitemid","updateSlotGradeItem","handleGradeItemClick","link","actionDelete","actionEdit","querySelectorAll","forEach","instructions","uniqueId","editLabel","rawName","inputElement","add","handleGradeItemEditStart","handleButtonClick","handleAddGradeItemClick","handleAutoSetup","handleResetAllClick","parentNode","createGradeItem","autoSetupGradeItems","button","deleteCancelPromise","reallyResetAll","slotResets","map","length","tr","body","addEventListener"],"mappings":"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"} \ No newline at end of file diff --git a/mod/quiz/amd/src/edit_multiple_grades.js b/mod/quiz/amd/src/edit_multiple_grades.js index bc54037b70f..6b707996dff 100644 --- a/mod/quiz/amd/src/edit_multiple_grades.js +++ b/mod/quiz/amd/src/edit_multiple_grades.js @@ -52,17 +52,15 @@ const SELECTORS = { * Call the Ajax service to create a quiz grade item. * * @param {Number} quizId id of the quiz to update. - * @param {String} name name of the grade item to create. * @returns {Promise} a promise that resolves to the template context required to re-render the page. */ const createGradeItem = ( quizId, - name ) => callServiceAndReturnRenderingData({ methodname: 'mod_quiz_create_grade_items', args: { quizid: quizId, - quizgradeitems: [{name: name}], + quizgradeitems: [{name: ''}], } }); @@ -413,8 +411,7 @@ const handleAddGradeItemClick = (e) => { const quizId = e.target.dataset.quizId; - getString('gradeitemdefaultname', 'quiz') - .then((name) => createGradeItem(quizId, name)) + createGradeItem(quizId) .then(reRenderPage) .then(() => { pending.resolve(); diff --git a/mod/quiz/classes/external/create_grade_items.php b/mod/quiz/classes/external/create_grade_items.php index 178a7bd0b3c..acf7ccbce87 100644 --- a/mod/quiz/classes/external/create_grade_items.php +++ b/mod/quiz/classes/external/create_grade_items.php @@ -49,7 +49,8 @@ class create_grade_items extends external_api { 'quizid' => new external_value(PARAM_INT, 'The quiz to update slots for.'), 'quizgradeitems' => new external_multiple_structure( new external_single_structure([ - 'name' => new external_value(PARAM_TEXT, 'The name for the grade item to create.'), + 'name' => new external_value(PARAM_TEXT, + 'The name for the grade item to create. If empty string, a sensible default is used.'), ]) ), ]); diff --git a/mod/quiz/classes/output/edit_grading_page.php b/mod/quiz/classes/output/edit_grading_page.php index f4ee5342077..f3c6e16833f 100644 --- a/mod/quiz/classes/output/edit_grading_page.php +++ b/mod/quiz/classes/output/edit_grading_page.php @@ -90,6 +90,9 @@ class edit_grading_page implements renderable, templatable { // Add the relevant slots ot each section. foreach ($this->structure->get_slots() as $slot) { + if (!$this->structure->is_real_question($slot->slot)) { + continue; + } // Mark the right choice as selected. $choices = $gradeitemchoices; if ($slot->quizgradeitemid) { diff --git a/mod/quiz/classes/output/edit_nav_actions.php b/mod/quiz/classes/output/edit_nav_actions.php index 0e7b7bf9d6f..88f3090ba08 100644 --- a/mod/quiz/classes/output/edit_nav_actions.php +++ b/mod/quiz/classes/output/edit_nav_actions.php @@ -57,16 +57,16 @@ class edit_nav_actions implements renderable, templatable { // Build the navigation drop-down. $questionsurl = new moodle_url('/mod/quiz/edit.php', ['cmid' => $this->cmid]); - $complexgradingurl = new moodle_url('/mod/quiz/editgrading.php', ['cmid' => $this->cmid]); + $gradeitemsetupurl = new moodle_url('/mod/quiz/editgrading.php', ['cmid' => $this->cmid]); $menu = [ $questionsurl->out(false) => get_string('questions', 'quiz'), - $complexgradingurl->out(false) => get_string('complexgrading', 'quiz'), + $gradeitemsetupurl->out(false) => get_string('gradeitemsetup', 'quiz'), ]; $overridesnav = new url_select( $menu, - $this->whichpage === self::SUMMARY ? $questionsurl->out(false) : $complexgradingurl->out(false), + $this->whichpage === self::SUMMARY ? $questionsurl->out(false) : $gradeitemsetupurl->out(false), null ); $overridesnav->set_label(get_string('quizsetupnavigation', 'quiz'), ['class' => 'sr-only']); diff --git a/mod/quiz/classes/structure.php b/mod/quiz/classes/structure.php index 993a135a5ea..15a72d372d1 100644 --- a/mod/quiz/classes/structure.php +++ b/mod/quiz/classes/structure.php @@ -1514,6 +1514,16 @@ class structure { 'COALESCE(MAX(sortorder) + 1, 1)', ['quizid' => $this->get_quizid()]); + // If name is blank, supply a default. + if ((string) $gradeitemdata->name === '') { + $count = 0; + do { + $count += 1; + $gradeitemdata->name = get_string('gradeitemdefaultname', 'quiz', $count); + } while ($DB->record_exists('quiz_grade_items', + ['quizid' => $this->get_quizid(), 'name' => $gradeitemdata->name])); + } + $transaction = $DB->start_delegated_transaction(); // Create the grade item. diff --git a/mod/quiz/editgrading.php b/mod/quiz/editgrading.php index a8b6917a508..e3614720e2c 100644 --- a/mod/quiz/editgrading.php +++ b/mod/quiz/editgrading.php @@ -55,7 +55,6 @@ $quizhasattempts = quiz_has_attempts($quiz->id); // Initialise output. $PAGE->set_pagelayout('incourse'); -$PAGE->set_pagetype('mod-quiz-edit'); $output = $PAGE->get_renderer('mod_quiz', 'edit'); diff --git a/mod/quiz/lang/en/quiz.php b/mod/quiz/lang/en/quiz.php index 6abe234cc4c..e01fd8ecc9d 100644 --- a/mod/quiz/lang/en/quiz.php +++ b/mod/quiz/lang/en/quiz.php @@ -191,7 +191,6 @@ $string['completionminattemptserror'] = 'Minimum number of attempts must be lowe $string['completionpassorattemptsexhausteddesc'] = 'Student must achieve a passing grade, or exhaust all available attempts to complete this activity'; $string['completionattemptsexhausted'] = 'Passing grade or all available attempts completed'; $string['completionattemptsexhausted_help'] = 'Mark quiz complete when the student has exhausted the maximum number of attempts.'; -$string['complexgrading'] = 'Multiple grade setup'; $string['configadaptive'] = 'If you choose Yes for this option then the student will be allowed multiple responses to a question even within the same attempt at the quiz.'; $string['configattemptsallowed'] = 'Restriction on the number of attempts students are allowed at the quiz.'; $string['configdecimaldigits'] = 'Number of digits that should be shown after the decimal point when displaying grades.'; @@ -440,20 +439,21 @@ $string['gradeaverage'] = 'Average grade'; $string['gradeboundary'] = 'Grade boundary'; $string['gradeessays'] = 'Grade essays'; $string['gradehighest'] = 'Highest grade'; -$string['gradeitemdefaultname'] = 'New grade item'; +$string['gradeitemdefaultname'] = 'New grade item {$a}'; $string['gradeitemdelete'] = 'Delete grade item {$a}'; $string['gradeitemedit'] = 'Edit name of grade item {$a}'; -$string['gradeitemmarkscheme'] = 'Mark scheme'; +$string['gradeitemmarkscheme'] = 'Assign grade items'; $string['gradeitemnewname'] = 'New name for grade item {$a}'; $string['gradeitemnoneselected'] = '[none]'; -$string['gradeitemnoslots'] = 'No questions have been added to the quiz yet. Please add the questions to the quiz before setting up grading.'; +$string['gradeitemnoslots'] = 'This quiz has no questions yet. Please add questions first, then return here to set up grade items.'; $string['gradeitems'] = 'Grade items'; $string['gradeitemsautosetup'] = 'Setup a grade for each section'; -$string['gradeitemsetup'] = 'Quiz grading setup (advanced view)'; -$string['gradeitemsnoneyet'] = 'This quiz does not yet have any grade items defined, just a simple overall score will be used.'; -$string['gradeitemsremoveall'] = 'Reset advanced grading setup'; -$string['gradeitemsremoveallconfirm'] = 'Reset grading setup?'; -$string['gradeitemsremoveallmessage'] = 'This will only clear the advanced grading setup. It will not change which questions are in the quiz.'; +$string['gradeitemsetup'] = 'Grade items setup'; +$string['gradeitemsnoneyet'] = 'Create grade items within your quiz. Allocate questions or quiz sections to these grade items to break down grade results into different areas.'; +$string['gradeitemsremoveall'] = 'Reset setup'; +$string['gradeitemsremoveallconfirm'] = 'Reset grade items setup?'; +$string['gradeitemsremoveallmessage'] = 'This will delete all grade items and unassign questions and sections from them.

This action will not affect the questions and sections themselves, nor existing attempts within the quiz.'; +$string['gradeitemsremovealltitle'] = 'Reset grade items setup'; $string['grademethod'] = 'Grading method'; $string['grademethod_help'] = 'When multiple attempts are allowed, the following methods are available for calculating the final quiz grade: @@ -756,7 +756,6 @@ $string['publish'] = 'Publish'; $string['publishedit'] = 'You must have permission in the publishing course to add or edit questions in this category'; $string['qbrief'] = 'Q. {$a}'; $string['qname'] = 'name'; -$string['qnumberbrief'] = 'No.'; $string['qti'] = 'IMS QTI format'; $string['qtypename'] = 'type, name'; $string['question'] = 'Question'; diff --git a/mod/quiz/styles.css b/mod/quiz/styles.css index 12853b40a4b..4868f7140e6 100644 --- a/mod/quiz/styles.css +++ b/mod/quiz/styles.css @@ -1090,6 +1090,9 @@ table.quizreviewsummary td.cell { color: #000; text-align: right; } +#page-mod-quiz-editgrading .inplaceeditable .quickeditlink { + color: var(--primary); +} #page-mod-quiz-edit .inplaceeditable .quickeditlink { color: var(--primary); text-decoration: none; diff --git a/mod/quiz/templates/edit_grading_page.mustache b/mod/quiz/templates/edit_grading_page.mustache index 5268b4cfbde..62da5e685f9 100644 --- a/mod/quiz/templates/edit_grading_page.mustache +++ b/mod/quiz/templates/edit_grading_page.mustache @@ -81,8 +81,6 @@

{{#str}} gradeitemsetup, quiz {{/str}}

-

{{#str}} gradeitems, grades {{/str}}

- {{^hasgradeitems}} {{#nogradeitems}} {{> core/notification_info}} @@ -91,17 +89,18 @@ {{#hasgradeitems}} + - + {{#gradeitems}} - + - {{/gradeitems}} @@ -131,10 +130,6 @@
- {{#hasgradeitems}} - - {{/hasgradeitems}} {{^hasgradeitems}} {{#hasmultiplesections}}
- + @@ -188,4 +184,11 @@
{{#str}} gradeitems, grades {{/str}}
{{#str}} gradeitem, grades {{/str}}{{#str}} actions {{/str}} {{#str}} totalmarks, quiz {{/str}}{{#str}} actions {{/str}}
+ @@ -112,6 +111,9 @@ + {{summarks}} + {{^isused}} @@ -119,9 +121,6 @@ {{/isused}} - {{summarks}} -
{{#str}} gradeitemmarkscheme, quiz {{/str}}
{{#str}} qnumberbrief, quiz {{/str}}{{#str}} numberabbr, quiz {{/str}} {{#str}} question, quiz {{/str}} {{#str}} marks, quiz {{/str}} {{#str}} gradeitem, grades {{/str}}
{{/hasslots}} +
+ {{#hasgradeitems}} +

{{#str}} gradeitemsremovealltitle, quiz {{/str}}

+ + {{/hasgradeitems}} +
diff --git a/mod/quiz/tests/behat/behat_mod_quiz.php b/mod/quiz/tests/behat/behat_mod_quiz.php index f75589385cd..202800e90a8 100644 --- a/mod/quiz/tests/behat/behat_mod_quiz.php +++ b/mod/quiz/tests/behat/behat_mod_quiz.php @@ -322,7 +322,7 @@ class behat_mod_quiz extends behat_question_base { } // Grade item. - if (array_key_exists('grade item', $questiondata)) { + if (array_key_exists('grade item', $questiondata) && trim($questiondata['grade item']) !== '') { $extraslotproperties['quizgradeitemid'] = $DB->get_field('quiz_grade_items', 'id', ['quizid' => $quiz->id, 'name' => $questiondata['grade item']], MUST_EXIST); diff --git a/mod/quiz/tests/behat/editing_multiple_grades.feature b/mod/quiz/tests/behat/editing_multiple_grades.feature index fdb5bf17aa1..73b6d7520db 100644 --- a/mod/quiz/tests/behat/editing_multiple_grades.feature +++ b/mod/quiz/tests/behat/editing_multiple_grades.feature @@ -18,10 +18,11 @@ Feature: Setup multiple grades for a quiz | contextlevel | reference | name | | Course | C1 | Test questions | And the following "questions" exist: - | questioncategory | qtype | name | questiontext | - | Test questions | truefalse | Question A | This is question 01 | - | Test questions | truefalse | Question B | This is question 02 | - | Test questions | truefalse | Question C | This is question 03 | + | questioncategory | qtype | name | questiontext | + | Test questions | description | Info | Some information | + | Test questions | truefalse | Question A | This is question 01 | + | Test questions | truefalse | Question B | This is question 02 | + | Test questions | truefalse | Question C | This is question 03 | And the following "activities" exist: | activity | name | course | | quiz | Quiz 1 | C1 | @@ -35,6 +36,7 @@ Feature: Setup multiple grades for a quiz | Quiz 1 | Unused grade item | And quiz "Quiz 1" contains the following questions: | question | page | grade item | + | Info | 1 | | | Question A | 1 | Intuition | | Question B | 1 | Intelligence | | Question C | 2 | Intuition | @@ -56,19 +58,23 @@ Feature: Setup multiple grades for a quiz And the field "Question C" matches value "Intuition" And "3" row "Marks" column of "mod_quiz-slot-list" table should contain "1.00" + And I should not see "Info" + @javascript Scenario: A grade item can be created and renamed Given quiz "Quiz 1" contains the following questions: | question | page | | Question A | 1 | When I am on the "Quiz 1" "mod_quiz > multiple grades setup" page logged in as teacher - And I should see "This quiz does not yet have any grade items defined" + And I should see "Create grade items within your quiz. Allocate questions or quiz sections to these grade items to break down grade results into different areas." And I press "Add grade item" - Then "New grade item" "table_row" should exist - And I click on "Edit" "link" in the "New grade item" "table_row" + Then "New grade item 1" "table_row" should exist + And I press "Add grade item" + Then "New grade item 2" "table_row" should exist + And I click on "Edit" "link" in the "New grade item 1" "table_row" And I set the field "New name for grade item" to "Intelligence" And I press enter - And I should not see "New grade item" + And I should not see "New grade item 1" And "Intelligence" "table_row" should exist @javascript @@ -97,7 +103,7 @@ Feature: Setup multiple grades for a quiz When I am on the "Quiz 1" "mod_quiz > multiple grades setup" page logged in as teacher And I follow "Delete grade item Unused grade item" Then I should not see "Unused grade item" - And I should see "This quiz does not yet have any grade items defined" + And I should see "Create grade items within your quiz. Allocate questions or quiz sections to these grade items to break down grade results into different areas." @javascript Scenario: Grade item for a slot can be changed @@ -130,14 +136,14 @@ Feature: Setup multiple grades for a quiz | Question C | 2 | Intuition | When I am on the "Quiz 1" "mod_quiz > multiple grades setup" page logged in as teacher - And I press "Reset advanced grading setup" - And I click on "Reset" "button" in the "Reset grading setup?" "dialogue" + And I press "Reset setup" + And I click on "Reset" "button" in the "Reset grade items setup?" "dialogue" - Then I should see "This quiz does not yet have any grade items defined, just a simple overall score will be used." + Then I should see "Create grade items within your quiz. Allocate questions or quiz sections to these grade items to break down grade results into different areas." And the field "Question A" matches value "[none]" And the field "Question B" matches value "[none]" And the field "Question C" matches value "[none]" - And I should not see "Reset advanced grading setup" + And I should not see "Reset grade items setup" @javascript Scenario: Reset all can be cancelled @@ -145,8 +151,8 @@ Feature: Setup multiple grades for a quiz | quiz | name | | Quiz 1 | Intuition | When I am on the "Quiz 1" "mod_quiz > multiple grades setup" page logged in as teacher - And I press "Reset advanced grading setup" - And I click on "Cancel" "button" in the "Reset grading setup?" "dialogue" + And I press "Reset setup" + And I click on "Cancel" "button" in the "Reset grade items setup?" "dialogue" Then I should see "Intuition" @javascript