From dbd195ded041b82c1ef1de370edcf7641c4e6406 Mon Sep 17 00:00:00 2001 From: Jeremy FitzPatrick Date: Thu, 17 Nov 2022 15:34:01 +1300 Subject: [PATCH 1/3] show dates that are past the course end --- amd/build/datehelper.min.js | 3 + amd/build/datehelper.min.js.map | 1 + amd/src/datehelper.js | 155 ++++++++++++++++++++++++++++++++ form.php | 3 +- index.php | 1 + lang/en/report_editdates.php | 2 + styles.css | 10 +++ 7 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 amd/build/datehelper.min.js create mode 100644 amd/build/datehelper.min.js.map create mode 100644 amd/src/datehelper.js diff --git a/amd/build/datehelper.min.js b/amd/build/datehelper.min.js new file mode 100644 index 0000000..6084ec9 --- /dev/null +++ b/amd/build/datehelper.min.js @@ -0,0 +1,3 @@ +define("report_editdates/datehelper",["exports","core/modal_factory","core/str"],(function(_exports,ModalFactory,_str){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,ModalFactory=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalFactory);let datetimeSelectors={},endDate={};_exports.init=()=>{datetimeSelectors=document.querySelectorAll("div[data-fieldtype='date_time_selector']");let courseEnd=datetimeSelectors.item(1);endDate=getDate(courseEnd),courseEnd.addEventListener("change",endDateChanged);for(const date of datetimeSelectors.entries())date[0]>1&&date[1].addEventListener("change",modDateChanged);M.form.dateselector.calendar.on("selectionChange",updateDates,!1)};const updateDates=()=>{let datepicker=M.form.dateselector.currentowner;null!==datepicker&&setTimeout((function(){let datetimeSelector=datepicker.calendarimage.getDOMNode().closest("div[data-fieldtype='date_time_selector']");datetimeSelector===datetimeSelectors.item(1)?endDateChanged():checkEndDate(datetimeSelector)}),100)},endDateChanged=()=>{let datesPastEnd=0;endDate=getDate(datetimeSelectors.item(1));for(const date of datetimeSelectors.entries())if(date[0]>1){let optional=date[1].querySelector("input[type='checkbox']");(null===optional||optional.checked)&&(datesPastEnd+=checkEndDate(date[1]))}if(datesPastEnd>0)return ModalFactory.create({type:ModalFactory.types.DEFAULT,title:(0,_str.get_string)("datesafterend_title","report_editdates"),body:(0,_str.get_string)("datesafterend_body","report_editdates")}).then((modal=>(modal.show(),modal)))},modDateChanged=ev=>{let container=ev.target.closest("div[data-fieldtype='date_time_selector']");checkEndDate(container)},getDate=dateEl=>{let year=dateEl.querySelector(".fdate_time_selector > div:nth-child(3) select"),month=dateEl.querySelector(".fdate_time_selector > div:nth-child(2) select"),day=dateEl.querySelector(".fdate_time_selector > div:nth-child(1) select"),hour=dateEl.querySelector(".fdate_time_selector > div:nth-child(4) select"),minute=dateEl.querySelector(".fdate_time_selector > div:nth-child(5) select");return new Date(year.options[year.selectedIndex].value,month.options[month.selectedIndex].value-1,day.options[day.selectedIndex].value,hour.options[hour.selectedIndex].value,minute.options[minute.selectedIndex].value)},checkEndDate=el=>{let afterEndDate=!1;if(getDate(el)>endDate){el.parentNode.classList.add("afterend");for(const input of el.querySelectorAll(".form-group").values())input.classList.add("afterend");afterEndDate=!0}else{el.parentNode.classList.remove("afterend");for(const input of el.querySelectorAll(".form-group").values())input.classList.remove("afterend")}return afterEndDate}})); + +//# sourceMappingURL=datehelper.min.js.map \ No newline at end of file diff --git a/amd/build/datehelper.min.js.map b/amd/build/datehelper.min.js.map new file mode 100644 index 0000000..8c8a870 --- /dev/null +++ b/amd/build/datehelper.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"datehelper.min.js","sources":["../src/datehelper.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 * AMD module to help editing course dates\n *\n * @module report_editdates/datehelper\n * @copyright 2022 Te Wānanga o Aotearoa\n */\n\nimport * as ModalFactory from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\n\nlet datetimeSelectors = {};\nlet endDate = {};\nconst classEnd = \"afterend\";\n\n/**\n * Initialiser function.\n */\nexport const init = () => {\n datetimeSelectors = document.querySelectorAll(\"div[data-fieldtype='date_time_selector']\");\n let courseEnd = datetimeSelectors.item(1);\n endDate = getDate(courseEnd);\n\n // Event Listeners.\n courseEnd.addEventListener(\"change\", endDateChanged);\n // Activity module date time selector events.\n for (const date of datetimeSelectors.entries()) {\n if (date[0] > 1) {\n date[1].addEventListener(\"change\", modDateChanged);\n }\n }\n // Date picker event.\n M.form.dateselector.calendar.on('selectionChange', updateDates, false);\n};\n\n/**\n * A date has been changed using the date selector calendar.\n */\nconst updateDates = () => {\n let datepicker = M.form.dateselector.currentowner;\n if (null === datepicker) {\n return;\n }\n // Todo: is there a better way to ensure this runs after the selects have been updated?\n setTimeout(\n function() {\n let el = datepicker.calendarimage.getDOMNode();\n let datetimeSelector = el.closest(\"div[data-fieldtype='date_time_selector']\");\n if (datetimeSelector === datetimeSelectors.item(1)) {\n endDateChanged();\n } else {\n checkEndDate(datetimeSelector);\n }\n },\n 100\n );\n};\n\n/**\n * The end date of the course has been changed.\n * Check if any activity module dates are after this date.\n * @returns {*}\n */\nconst endDateChanged = () => {\n let datesPastEnd = 0;\n endDate = getDate(datetimeSelectors.item(1));\n for (const date of datetimeSelectors.entries()) {\n if (date[0] > 1) {\n let optional = date[1].querySelector(\"input[type='checkbox']\");\n if (optional === null || optional.checked) {\n datesPastEnd += checkEndDate(date[1]);\n }\n }\n }\n\n if (datesPastEnd > 0) {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: getString('datesafterend_title', 'report_editdates'),\n body: getString('datesafterend_body', 'report_editdates')\n })\n .then(modal => {\n modal.show();\n return modal;\n });\n }\n};\n\n/**\n * An activity module date has been changed.\n * Check and warn if it is now after the course end date.\n * @param {Event} ev\n */\nconst modDateChanged = (ev) => {\n // Check against course end date.\n let container = ev.target.closest(\"div[data-fieldtype='date_time_selector']\");\n\n checkEndDate(container);\n};\n\n/**\n * Get a Date object from the select DOM elements.\n * @param {HTMLElement} dateEl\n * @returns {Date}\n */\nconst getDate = (dateEl) => {\n let year = dateEl.querySelector(\".fdate_time_selector > div:nth-child(3) select\");\n let month = dateEl.querySelector(\".fdate_time_selector > div:nth-child(2) select\");\n let day = dateEl.querySelector(\".fdate_time_selector > div:nth-child(1) select\");\n let hour = dateEl.querySelector(\".fdate_time_selector > div:nth-child(4) select\");\n let minute = dateEl.querySelector(\".fdate_time_selector > div:nth-child(5) select\");\n return new Date(\n year.options[year.selectedIndex].value,\n month.options[month.selectedIndex].value - 1,\n day.options[day.selectedIndex].value,\n hour.options[hour.selectedIndex].value,\n minute.options[minute.selectedIndex].value,\n );\n};\n\n/**\n * Compare the date with the course end date. Toggle CSS class of element.\n * @param {HTMLElement} el\n * @returns {boolean}\n */\nconst checkEndDate = (el) => {\n let afterEndDate = false;\n if (getDate(el) > endDate) {\n el.parentNode.classList.add(classEnd);\n for (const input of el.querySelectorAll(\".form-group\").values()) {\n input.classList.add(classEnd);\n }\n afterEndDate = true;\n } else {\n el.parentNode.classList.remove(classEnd);\n for (const input of el.querySelectorAll(\".form-group\").values()) {\n input.classList.remove(classEnd);\n }\n }\n return afterEndDate;\n};\n"],"names":["datetimeSelectors","endDate","document","querySelectorAll","courseEnd","item","getDate","addEventListener","endDateChanged","date","entries","modDateChanged","M","form","dateselector","calendar","on","updateDates","datepicker","currentowner","setTimeout","datetimeSelector","calendarimage","getDOMNode","closest","checkEndDate","datesPastEnd","optional","querySelector","checked","ModalFactory","create","type","types","DEFAULT","title","body","then","modal","show","ev","container","target","dateEl","year","month","day","hour","minute","Date","options","selectedIndex","value","el","afterEndDate","parentNode","classList","add","input","values","remove"],"mappings":"moCAyBIA,kBAAoB,GACpBC,QAAU,iBAMM,KAChBD,kBAAoBE,SAASC,iBAAiB,gDAC1CC,UAAYJ,kBAAkBK,KAAK,GACvCJ,QAAUK,QAAQF,WAGlBA,UAAUG,iBAAiB,SAAUC,oBAEhC,MAAMC,QAAQT,kBAAkBU,UAC7BD,KAAK,GAAK,GACVA,KAAK,GAAGF,iBAAiB,SAAUI,gBAI3CC,EAAEC,KAAKC,aAAaC,SAASC,GAAG,kBAAmBC,aAAa,UAM9DA,YAAc,SACZC,WAAaN,EAAEC,KAAKC,aAAaK,aACjC,OAASD,YAIbE,YACI,eAEQC,iBADKH,WAAWI,cAAcC,aACRC,QAAQ,4CAC9BH,mBAAqBrB,kBAAkBK,KAAK,GAC5CG,iBAEAiB,aAAaJ,oBAGrB,MASFb,eAAiB,SACfkB,aAAe,EACnBzB,QAAUK,QAAQN,kBAAkBK,KAAK,QACpC,MAAMI,QAAQT,kBAAkBU,aAC7BD,KAAK,GAAK,EAAG,KACTkB,SAAWlB,KAAK,GAAGmB,cAAc,2BACpB,OAAbD,UAAqBA,SAASE,WAC9BH,cAAgBD,aAAahB,KAAK,QAK1CiB,aAAe,SACRI,aAAaC,OAAO,CACvBC,KAAMF,aAAaG,MAAMC,QACzBC,OAAO,mBAAU,sBAAuB,oBACxCC,MAAM,mBAAU,qBAAsB,sBAErCC,MAAKC,QACFA,MAAMC,OACCD,UAUjB3B,eAAkB6B,SAEhBC,UAAYD,GAAGE,OAAOlB,QAAQ,4CAElCC,aAAagB,YAQXnC,QAAWqC,aACTC,KAAOD,OAAOf,cAAc,kDAC5BiB,MAAQF,OAAOf,cAAc,kDAC7BkB,IAAMH,OAAOf,cAAc,kDAC3BmB,KAAOJ,OAAOf,cAAc,kDAC5BoB,OAASL,OAAOf,cAAc,yDAC3B,IAAIqB,KACPL,KAAKM,QAAQN,KAAKO,eAAeC,MACjCP,MAAMK,QAAQL,MAAMM,eAAeC,MAAQ,EAC3CN,IAAII,QAAQJ,IAAIK,eAAeC,MAC/BL,KAAKG,QAAQH,KAAKI,eAAeC,MACjCJ,OAAOE,QAAQF,OAAOG,eAAeC,QASvC3B,aAAgB4B,SACdC,cAAe,KACfhD,QAAQ+C,IAAMpD,QAAS,CACvBoD,GAAGE,WAAWC,UAAUC,IAnHf,gBAoHJ,MAAMC,SAASL,GAAGlD,iBAAiB,eAAewD,SACnDD,MAAMF,UAAUC,IArHX,YAuHTH,cAAe,MACZ,CACHD,GAAGE,WAAWC,UAAUI,OAzHf,gBA0HJ,MAAMF,SAASL,GAAGlD,iBAAiB,eAAewD,SACnDD,MAAMF,UAAUI,OA3HX,mBA8HNN"} \ No newline at end of file diff --git a/amd/src/datehelper.js b/amd/src/datehelper.js new file mode 100644 index 0000000..090c5b4 --- /dev/null +++ b/amd/src/datehelper.js @@ -0,0 +1,155 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * AMD module to help editing course dates + * + * @module report_editdates/datehelper + * @copyright 2022 Te Wānanga o Aotearoa + */ + +import * as ModalFactory from 'core/modal_factory'; +import {get_string as getString} from 'core/str'; + +let datetimeSelectors = {}; +let endDate = {}; +const classEnd = "afterend"; + +/** + * Initialiser function. + */ +export const init = () => { + datetimeSelectors = document.querySelectorAll("div[data-fieldtype='date_time_selector']"); + let courseEnd = datetimeSelectors.item(1); + endDate = getDate(courseEnd); + + // Event Listeners. + courseEnd.addEventListener("change", endDateChanged); + // Activity module date time selector events. + for (const date of datetimeSelectors.entries()) { + if (date[0] > 1) { + date[1].addEventListener("change", modDateChanged); + } + } + // Date picker event. + M.form.dateselector.calendar.on('selectionChange', updateDates, false); +}; + +/** + * A date has been changed using the date selector calendar. + */ +const updateDates = () => { + let datepicker = M.form.dateselector.currentowner; + if (null === datepicker) { + return; + } + // Todo: is there a better way to ensure this runs after the selects have been updated? + setTimeout( + function() { + let el = datepicker.calendarimage.getDOMNode(); + let datetimeSelector = el.closest("div[data-fieldtype='date_time_selector']"); + if (datetimeSelector === datetimeSelectors.item(1)) { + endDateChanged(); + } else { + checkEndDate(datetimeSelector); + } + }, + 100 + ); +}; + +/** + * The end date of the course has been changed. + * Check if any activity module dates are after this date. + * @returns {*} + */ +const endDateChanged = () => { + let datesPastEnd = 0; + endDate = getDate(datetimeSelectors.item(1)); + for (const date of datetimeSelectors.entries()) { + if (date[0] > 1) { + let optional = date[1].querySelector("input[type='checkbox']"); + if (optional === null || optional.checked) { + datesPastEnd += checkEndDate(date[1]); + } + } + } + + if (datesPastEnd > 0) { + return ModalFactory.create({ + type: ModalFactory.types.DEFAULT, + title: getString('datesafterend_title', 'report_editdates'), + body: getString('datesafterend_body', 'report_editdates') + }) + .then(modal => { + modal.show(); + return modal; + }); + } +}; + +/** + * An activity module date has been changed. + * Check and warn if it is now after the course end date. + * @param {Event} ev + */ +const modDateChanged = (ev) => { + // Check against course end date. + let container = ev.target.closest("div[data-fieldtype='date_time_selector']"); + + checkEndDate(container); +}; + +/** + * Get a Date object from the select DOM elements. + * @param {HTMLElement} dateEl + * @returns {Date} + */ +const getDate = (dateEl) => { + let year = dateEl.querySelector(".fdate_time_selector > div:nth-child(3) select"); + let month = dateEl.querySelector(".fdate_time_selector > div:nth-child(2) select"); + let day = dateEl.querySelector(".fdate_time_selector > div:nth-child(1) select"); + let hour = dateEl.querySelector(".fdate_time_selector > div:nth-child(4) select"); + let minute = dateEl.querySelector(".fdate_time_selector > div:nth-child(5) select"); + return new Date( + year.options[year.selectedIndex].value, + month.options[month.selectedIndex].value - 1, + day.options[day.selectedIndex].value, + hour.options[hour.selectedIndex].value, + minute.options[minute.selectedIndex].value, + ); +}; + +/** + * Compare the date with the course end date. Toggle CSS class of element. + * @param {HTMLElement} el + * @returns {boolean} + */ +const checkEndDate = (el) => { + let afterEndDate = false; + if (getDate(el) > endDate) { + el.parentNode.classList.add(classEnd); + for (const input of el.querySelectorAll(".form-group").values()) { + input.classList.add(classEnd); + } + afterEndDate = true; + } else { + el.parentNode.classList.remove(classEnd); + for (const input of el.querySelectorAll(".form-group").values()) { + input.classList.remove(classEnd); + } + } + return afterEndDate; +}; diff --git a/form.php b/form.php index aa6a258..05cdd13 100644 --- a/form.php +++ b/form.php @@ -190,11 +190,12 @@ public function definition() { if ($mod && ($cmdatesettings = $mod->get_settings($cm))) { // Added activity name on the form. foreach ($cmdatesettings as $cmdatetype => $cmdatesetting) { + $aferendclass = $cmdatesetting->currentvalue > $course->enddate ? 'afterend' : ''; $elname = 'date_mod_'.$cm->id.'_'.$cmdatetype; $mform->addElement($cmdatesetting->type, $elname, $cmdatesetting->label, array( 'optional' => $cmdatesetting->isoptional, - 'step' => $cmdatesetting->getstep)); + 'step' => $cmdatesetting->getstep), ['class' => $aferendclass]); $mform->setDefault($elname, $cmdatesetting->currentvalue); $timeline[$cm->id] = array_merge($timeline[$cm->id], array($cmdatesetting->label => $cmdatesetting->currentvalue)); diff --git a/index.php b/index.php index edef980..a799cea 100644 --- a/index.php +++ b/index.php @@ -239,4 +239,5 @@ $mform->display(); +$PAGE->requires->js_call_amd('report_editdates/datehelper', 'init'); echo $OUTPUT->footer(); diff --git a/lang/en/report_editdates.php b/lang/en/report_editdates.php index 8970b88..fefa735 100644 --- a/lang/en/report_editdates.php +++ b/lang/en/report_editdates.php @@ -56,3 +56,5 @@ $string['editrestrictedaccess'] = 'Edit restricted access (opens a new window)'; $string['event:reportviewed'] = 'Edit dates report viewed'; $string['privacy:metadata'] = 'The Dates plugin does not store any personal data.'; +$string['datesafterend_title'] = 'Uh oh'; +$string['datesafterend_body'] = 'There are one or more dates that are after the end of the course. You amy want to check the dates on this page.'; diff --git a/styles.css b/styles.css index 054bbb5..4cc3e35 100644 --- a/styles.css +++ b/styles.css @@ -75,3 +75,13 @@ border-radius: 5px; padding: 2px; } + +.path-report-editdates .afterend select { + color: red; +} + +.path-report-editdates div.row.afterend .col-form-label::after { + content: "\f071"; + font-family: "fontawesome"; + color: orange; +} \ No newline at end of file From 832afb9b34e1a4b7dfb1a5ef4c3e00f34b94816c Mon Sep 17 00:00:00 2001 From: Jeremy FitzPatrick Date: Fri, 18 Nov 2022 08:34:21 +1300 Subject: [PATCH 2/3] show dates that are before the course start --- amd/build/datehelper.min.js | 2 +- amd/build/datehelper.min.js.map | 2 +- amd/src/datehelper.js | 82 +++++++++++++++++++++++++-------- form.php | 7 ++- lang/en/report_editdates.php | 5 +- styles.css | 4 +- 6 files changed, 74 insertions(+), 28 deletions(-) diff --git a/amd/build/datehelper.min.js b/amd/build/datehelper.min.js index 6084ec9..01dec4e 100644 --- a/amd/build/datehelper.min.js +++ b/amd/build/datehelper.min.js @@ -1,3 +1,3 @@ -define("report_editdates/datehelper",["exports","core/modal_factory","core/str"],(function(_exports,ModalFactory,_str){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,ModalFactory=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalFactory);let datetimeSelectors={},endDate={};_exports.init=()=>{datetimeSelectors=document.querySelectorAll("div[data-fieldtype='date_time_selector']");let courseEnd=datetimeSelectors.item(1);endDate=getDate(courseEnd),courseEnd.addEventListener("change",endDateChanged);for(const date of datetimeSelectors.entries())date[0]>1&&date[1].addEventListener("change",modDateChanged);M.form.dateselector.calendar.on("selectionChange",updateDates,!1)};const updateDates=()=>{let datepicker=M.form.dateselector.currentowner;null!==datepicker&&setTimeout((function(){let datetimeSelector=datepicker.calendarimage.getDOMNode().closest("div[data-fieldtype='date_time_selector']");datetimeSelector===datetimeSelectors.item(1)?endDateChanged():checkEndDate(datetimeSelector)}),100)},endDateChanged=()=>{let datesPastEnd=0;endDate=getDate(datetimeSelectors.item(1));for(const date of datetimeSelectors.entries())if(date[0]>1){let optional=date[1].querySelector("input[type='checkbox']");(null===optional||optional.checked)&&(datesPastEnd+=checkEndDate(date[1]))}if(datesPastEnd>0)return ModalFactory.create({type:ModalFactory.types.DEFAULT,title:(0,_str.get_string)("datesafterend_title","report_editdates"),body:(0,_str.get_string)("datesafterend_body","report_editdates")}).then((modal=>(modal.show(),modal)))},modDateChanged=ev=>{let container=ev.target.closest("div[data-fieldtype='date_time_selector']");checkEndDate(container)},getDate=dateEl=>{let year=dateEl.querySelector(".fdate_time_selector > div:nth-child(3) select"),month=dateEl.querySelector(".fdate_time_selector > div:nth-child(2) select"),day=dateEl.querySelector(".fdate_time_selector > div:nth-child(1) select"),hour=dateEl.querySelector(".fdate_time_selector > div:nth-child(4) select"),minute=dateEl.querySelector(".fdate_time_selector > div:nth-child(5) select");return new Date(year.options[year.selectedIndex].value,month.options[month.selectedIndex].value-1,day.options[day.selectedIndex].value,hour.options[hour.selectedIndex].value,minute.options[minute.selectedIndex].value)},checkEndDate=el=>{let afterEndDate=!1;if(getDate(el)>endDate){el.parentNode.classList.add("afterend");for(const input of el.querySelectorAll(".form-group").values())input.classList.add("afterend");afterEndDate=!0}else{el.parentNode.classList.remove("afterend");for(const input of el.querySelectorAll(".form-group").values())input.classList.remove("afterend")}return afterEndDate}})); +define("report_editdates/datehelper",["exports","core/modal_factory","core/str"],(function(_exports,ModalFactory,_str){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,ModalFactory=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalFactory);let datetimeSelectors={},startDate={},endDate=new Date("December 31, 2099 23:59:59");_exports.init=()=>{datetimeSelectors=document.querySelectorAll("div[data-fieldtype='date_time_selector']");let courseStart=datetimeSelectors.item(0),courseEnd=datetimeSelectors.item(1);startDate=getDate(courseStart),courseEnd.querySelector("input[type='checkbox']").checked&&(endDate=getDate(courseEnd)),courseStart.addEventListener("change",startDateChanged),courseEnd.addEventListener("change",endDateChanged);for(const date of datetimeSelectors.entries())date[0]>1&&date[1].addEventListener("change",modDateChanged);M.form.dateselector.calendar.on("selectionChange",updateDates,!1)};const updateDates=()=>{let datepicker=M.form.dateselector.currentowner;null!==datepicker&&setTimeout((function(){let datetimeSelector=datepicker.calendarimage.getDOMNode().closest("div[data-fieldtype='date_time_selector']");datetimeSelector===datetimeSelectors.item(0)?startDateChanged():datetimeSelector===datetimeSelectors.item(1)?endDateChanged():checkDateRange(datetimeSelector)}),100)},startDateChanged=()=>{let datesOutOfRange=0;startDate=getDate(datetimeSelectors.item(0));for(const date of datetimeSelectors.entries())if(date[0]>1){let optional=date[1].querySelector("input[type='checkbox']");(null===optional||optional.checked)&&(datesOutOfRange+=checkDateRange(date[1]))}return datesOutOfRange>0&&ModalFactory.create({type:ModalFactory.types.DEFAULT,title:(0,_str.get_string)("datesoutofrange_title","report_editdates"),body:(0,_str.get_string)("datesoutofrange_body","report_editdates")}).then((modal=>(modal.show(),modal)))},endDateChanged=()=>{let datesOutOfRange=0;endDate=getDate(datetimeSelectors.item(1));for(const date of datetimeSelectors.entries())if(date[0]>1){let optional=date[1].querySelector("input[type='checkbox']");(null===optional||optional.checked)&&(datesOutOfRange+=checkDateRange(date[1]))}if(datesOutOfRange>0)return ModalFactory.create({type:ModalFactory.types.DEFAULT,title:(0,_str.get_string)("datesoutofrange_title","report_editdates"),body:(0,_str.get_string)("datesoutofrange_body","report_editdates")}).then((modal=>(modal.show(),modal)))},modDateChanged=ev=>{let container=ev.target.closest("div[data-fieldtype='date_time_selector']");checkDateRange(container)},getDate=dateEl=>{let year=dateEl.querySelector(".fdate_time_selector > div:nth-child(3) select"),month=dateEl.querySelector(".fdate_time_selector > div:nth-child(2) select"),day=dateEl.querySelector(".fdate_time_selector > div:nth-child(1) select"),hour=dateEl.querySelector(".fdate_time_selector > div:nth-child(4) select"),minute=dateEl.querySelector(".fdate_time_selector > div:nth-child(5) select");return new Date(year.options[year.selectedIndex].value,month.options[month.selectedIndex].value-1,day.options[day.selectedIndex].value,hour.options[hour.selectedIndex].value,minute.options[minute.selectedIndex].value)},checkDateRange=el=>{let outOfRange=!1,modDate=getDate(el);if(modDate>endDate||modDate.\n\n/**\n * AMD module to help editing course dates\n *\n * @module report_editdates/datehelper\n * @copyright 2022 Te Wānanga o Aotearoa\n */\n\nimport * as ModalFactory from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\n\nlet datetimeSelectors = {};\nlet endDate = {};\nconst classEnd = \"afterend\";\n\n/**\n * Initialiser function.\n */\nexport const init = () => {\n datetimeSelectors = document.querySelectorAll(\"div[data-fieldtype='date_time_selector']\");\n let courseEnd = datetimeSelectors.item(1);\n endDate = getDate(courseEnd);\n\n // Event Listeners.\n courseEnd.addEventListener(\"change\", endDateChanged);\n // Activity module date time selector events.\n for (const date of datetimeSelectors.entries()) {\n if (date[0] > 1) {\n date[1].addEventListener(\"change\", modDateChanged);\n }\n }\n // Date picker event.\n M.form.dateselector.calendar.on('selectionChange', updateDates, false);\n};\n\n/**\n * A date has been changed using the date selector calendar.\n */\nconst updateDates = () => {\n let datepicker = M.form.dateselector.currentowner;\n if (null === datepicker) {\n return;\n }\n // Todo: is there a better way to ensure this runs after the selects have been updated?\n setTimeout(\n function() {\n let el = datepicker.calendarimage.getDOMNode();\n let datetimeSelector = el.closest(\"div[data-fieldtype='date_time_selector']\");\n if (datetimeSelector === datetimeSelectors.item(1)) {\n endDateChanged();\n } else {\n checkEndDate(datetimeSelector);\n }\n },\n 100\n );\n};\n\n/**\n * The end date of the course has been changed.\n * Check if any activity module dates are after this date.\n * @returns {*}\n */\nconst endDateChanged = () => {\n let datesPastEnd = 0;\n endDate = getDate(datetimeSelectors.item(1));\n for (const date of datetimeSelectors.entries()) {\n if (date[0] > 1) {\n let optional = date[1].querySelector(\"input[type='checkbox']\");\n if (optional === null || optional.checked) {\n datesPastEnd += checkEndDate(date[1]);\n }\n }\n }\n\n if (datesPastEnd > 0) {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: getString('datesafterend_title', 'report_editdates'),\n body: getString('datesafterend_body', 'report_editdates')\n })\n .then(modal => {\n modal.show();\n return modal;\n });\n }\n};\n\n/**\n * An activity module date has been changed.\n * Check and warn if it is now after the course end date.\n * @param {Event} ev\n */\nconst modDateChanged = (ev) => {\n // Check against course end date.\n let container = ev.target.closest(\"div[data-fieldtype='date_time_selector']\");\n\n checkEndDate(container);\n};\n\n/**\n * Get a Date object from the select DOM elements.\n * @param {HTMLElement} dateEl\n * @returns {Date}\n */\nconst getDate = (dateEl) => {\n let year = dateEl.querySelector(\".fdate_time_selector > div:nth-child(3) select\");\n let month = dateEl.querySelector(\".fdate_time_selector > div:nth-child(2) select\");\n let day = dateEl.querySelector(\".fdate_time_selector > div:nth-child(1) select\");\n let hour = dateEl.querySelector(\".fdate_time_selector > div:nth-child(4) select\");\n let minute = dateEl.querySelector(\".fdate_time_selector > div:nth-child(5) select\");\n return new Date(\n year.options[year.selectedIndex].value,\n month.options[month.selectedIndex].value - 1,\n day.options[day.selectedIndex].value,\n hour.options[hour.selectedIndex].value,\n minute.options[minute.selectedIndex].value,\n );\n};\n\n/**\n * Compare the date with the course end date. Toggle CSS class of element.\n * @param {HTMLElement} el\n * @returns {boolean}\n */\nconst checkEndDate = (el) => {\n let afterEndDate = false;\n if (getDate(el) > endDate) {\n el.parentNode.classList.add(classEnd);\n for (const input of el.querySelectorAll(\".form-group\").values()) {\n input.classList.add(classEnd);\n }\n afterEndDate = true;\n } else {\n el.parentNode.classList.remove(classEnd);\n for (const input of el.querySelectorAll(\".form-group\").values()) {\n input.classList.remove(classEnd);\n }\n }\n return afterEndDate;\n};\n"],"names":["datetimeSelectors","endDate","document","querySelectorAll","courseEnd","item","getDate","addEventListener","endDateChanged","date","entries","modDateChanged","M","form","dateselector","calendar","on","updateDates","datepicker","currentowner","setTimeout","datetimeSelector","calendarimage","getDOMNode","closest","checkEndDate","datesPastEnd","optional","querySelector","checked","ModalFactory","create","type","types","DEFAULT","title","body","then","modal","show","ev","container","target","dateEl","year","month","day","hour","minute","Date","options","selectedIndex","value","el","afterEndDate","parentNode","classList","add","input","values","remove"],"mappings":"moCAyBIA,kBAAoB,GACpBC,QAAU,iBAMM,KAChBD,kBAAoBE,SAASC,iBAAiB,gDAC1CC,UAAYJ,kBAAkBK,KAAK,GACvCJ,QAAUK,QAAQF,WAGlBA,UAAUG,iBAAiB,SAAUC,oBAEhC,MAAMC,QAAQT,kBAAkBU,UAC7BD,KAAK,GAAK,GACVA,KAAK,GAAGF,iBAAiB,SAAUI,gBAI3CC,EAAEC,KAAKC,aAAaC,SAASC,GAAG,kBAAmBC,aAAa,UAM9DA,YAAc,SACZC,WAAaN,EAAEC,KAAKC,aAAaK,aACjC,OAASD,YAIbE,YACI,eAEQC,iBADKH,WAAWI,cAAcC,aACRC,QAAQ,4CAC9BH,mBAAqBrB,kBAAkBK,KAAK,GAC5CG,iBAEAiB,aAAaJ,oBAGrB,MASFb,eAAiB,SACfkB,aAAe,EACnBzB,QAAUK,QAAQN,kBAAkBK,KAAK,QACpC,MAAMI,QAAQT,kBAAkBU,aAC7BD,KAAK,GAAK,EAAG,KACTkB,SAAWlB,KAAK,GAAGmB,cAAc,2BACpB,OAAbD,UAAqBA,SAASE,WAC9BH,cAAgBD,aAAahB,KAAK,QAK1CiB,aAAe,SACRI,aAAaC,OAAO,CACvBC,KAAMF,aAAaG,MAAMC,QACzBC,OAAO,mBAAU,sBAAuB,oBACxCC,MAAM,mBAAU,qBAAsB,sBAErCC,MAAKC,QACFA,MAAMC,OACCD,UAUjB3B,eAAkB6B,SAEhBC,UAAYD,GAAGE,OAAOlB,QAAQ,4CAElCC,aAAagB,YAQXnC,QAAWqC,aACTC,KAAOD,OAAOf,cAAc,kDAC5BiB,MAAQF,OAAOf,cAAc,kDAC7BkB,IAAMH,OAAOf,cAAc,kDAC3BmB,KAAOJ,OAAOf,cAAc,kDAC5BoB,OAASL,OAAOf,cAAc,yDAC3B,IAAIqB,KACPL,KAAKM,QAAQN,KAAKO,eAAeC,MACjCP,MAAMK,QAAQL,MAAMM,eAAeC,MAAQ,EAC3CN,IAAII,QAAQJ,IAAIK,eAAeC,MAC/BL,KAAKG,QAAQH,KAAKI,eAAeC,MACjCJ,OAAOE,QAAQF,OAAOG,eAAeC,QASvC3B,aAAgB4B,SACdC,cAAe,KACfhD,QAAQ+C,IAAMpD,QAAS,CACvBoD,GAAGE,WAAWC,UAAUC,IAnHf,gBAoHJ,MAAMC,SAASL,GAAGlD,iBAAiB,eAAewD,SACnDD,MAAMF,UAAUC,IArHX,YAuHTH,cAAe,MACZ,CACHD,GAAGE,WAAWC,UAAUI,OAzHf,gBA0HJ,MAAMF,SAASL,GAAGlD,iBAAiB,eAAewD,SACnDD,MAAMF,UAAUI,OA3HX,mBA8HNN"} \ No newline at end of file +{"version":3,"file":"datehelper.min.js","sources":["../src/datehelper.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 * AMD module to help editing course dates\n *\n * @module report_editdates/datehelper\n * @copyright 2022 Te Wānanga o Aotearoa\n */\n\nimport * as ModalFactory from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\n\nlet datetimeSelectors = {};\nlet startDate = {};\nlet endDate = new Date(\"December 31, 2099 23:59:59\");\nconst classOutOfRange = \"outofrange\";\n\n/**\n * Initialiser function.\n */\nexport const init = () => {\n datetimeSelectors = document.querySelectorAll(\"div[data-fieldtype='date_time_selector']\");\n let courseStart = datetimeSelectors.item(0);\n let courseEnd = datetimeSelectors.item(1);\n startDate = getDate(courseStart);\n let optional = courseEnd.querySelector(\"input[type='checkbox']\");\n if (optional.checked) {\n endDate = getDate(courseEnd);\n }\n\n // Event Listeners.\n courseStart.addEventListener(\"change\", startDateChanged);\n courseEnd.addEventListener(\"change\", endDateChanged);\n // Activity module date time selector events.\n for (const date of datetimeSelectors.entries()) {\n if (date[0] > 1) {\n date[1].addEventListener(\"change\", modDateChanged);\n }\n }\n // Date picker event.\n M.form.dateselector.calendar.on('selectionChange', updateDates, false);\n};\n\n/**\n * A date has been changed using the date selector calendar.\n */\nconst updateDates = () => {\n let datepicker = M.form.dateselector.currentowner;\n if (null === datepicker) {\n return;\n }\n // Todo: is there a better way to ensure this runs after the selects have been updated?\n setTimeout(\n function() {\n let el = datepicker.calendarimage.getDOMNode();\n let datetimeSelector = el.closest(\"div[data-fieldtype='date_time_selector']\");\n if (datetimeSelector === datetimeSelectors.item(0)) {\n startDateChanged();\n } else if (datetimeSelector === datetimeSelectors.item(1)) {\n endDateChanged();\n } else {\n checkDateRange(datetimeSelector);\n }\n },\n 100\n );\n};\n\n/**\n * The start date of the course has been changed.\n * Adjust all other dates accordingly if desired.\n * @returns {*}\n */\nconst startDateChanged = () => {\n let datesOutOfRange = 0;\n startDate = getDate(datetimeSelectors.item(0));\n for (const date of datetimeSelectors.entries()) {\n if (date[0] > 1) {\n let optional = date[1].querySelector(\"input[type='checkbox']\");\n if (optional === null || optional.checked) {\n datesOutOfRange += checkDateRange(date[1]);\n }\n }\n }\n\n if (datesOutOfRange > 0) {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: getString('datesoutofrange_title', 'report_editdates'),\n body: getString('datesoutofrange_body', 'report_editdates')\n })\n .then(modal => {\n modal.show();\n return modal;\n });\n }\n\n return false;\n};\n\n/**\n * The end date of the course has been changed.\n * Check if any activity module dates are after this date.\n * @returns {*}\n */\nconst endDateChanged = () => {\n let datesOutOfRange = 0;\n endDate = getDate(datetimeSelectors.item(1));\n for (const date of datetimeSelectors.entries()) {\n if (date[0] > 1) {\n let optional = date[1].querySelector(\"input[type='checkbox']\");\n if (optional === null || optional.checked) {\n datesOutOfRange += checkDateRange(date[1]);\n }\n }\n }\n\n if (datesOutOfRange > 0) {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: getString('datesoutofrange_title', 'report_editdates'),\n body: getString('datesoutofrange_body', 'report_editdates')\n })\n .then(modal => {\n modal.show();\n return modal;\n });\n }\n};\n\n/**\n * An activity module date has been changed.\n * Check and warn if it is now after the course end date.\n * @param {Event} ev\n */\nconst modDateChanged = (ev) => {\n // Check against course end date.\n let container = ev.target.closest(\"div[data-fieldtype='date_time_selector']\");\n\n checkDateRange(container);\n};\n\n/**\n * Get a Date object from the select DOM elements.\n * @param {HTMLElement} dateEl\n * @returns {Date}\n */\nconst getDate = (dateEl) => {\n let year = dateEl.querySelector(\".fdate_time_selector > div:nth-child(3) select\");\n let month = dateEl.querySelector(\".fdate_time_selector > div:nth-child(2) select\");\n let day = dateEl.querySelector(\".fdate_time_selector > div:nth-child(1) select\");\n let hour = dateEl.querySelector(\".fdate_time_selector > div:nth-child(4) select\");\n let minute = dateEl.querySelector(\".fdate_time_selector > div:nth-child(5) select\");\n return new Date(\n year.options[year.selectedIndex].value,\n month.options[month.selectedIndex].value - 1,\n day.options[day.selectedIndex].value,\n hour.options[hour.selectedIndex].value,\n minute.options[minute.selectedIndex].value,\n );\n};\n\n/**\n * Compare the date with the course end date. Toggle CSS class of element.\n * @param {HTMLElement} el\n * @returns {boolean}\n */\nconst checkDateRange = (el) => {\n let outOfRange = false;\n let modDate = getDate(el);\n if (modDate > endDate || modDate < startDate) {\n el.parentNode.classList.add(classOutOfRange);\n for (const input of el.querySelectorAll(\".form-group\").values()) {\n input.classList.add(classOutOfRange);\n }\n outOfRange = true;\n } else {\n el.parentNode.classList.remove(classOutOfRange);\n for (const input of el.querySelectorAll(\".form-group\").values()) {\n input.classList.remove(classOutOfRange);\n }\n }\n return outOfRange;\n};\n"],"names":["datetimeSelectors","startDate","endDate","Date","document","querySelectorAll","courseStart","item","courseEnd","getDate","querySelector","checked","addEventListener","startDateChanged","endDateChanged","date","entries","modDateChanged","M","form","dateselector","calendar","on","updateDates","datepicker","currentowner","setTimeout","datetimeSelector","calendarimage","getDOMNode","closest","checkDateRange","datesOutOfRange","optional","ModalFactory","create","type","types","DEFAULT","title","body","then","modal","show","ev","container","target","dateEl","year","month","day","hour","minute","options","selectedIndex","value","el","outOfRange","modDate","parentNode","classList","add","input","values","remove"],"mappings":"moCAyBIA,kBAAoB,GACpBC,UAAY,GACZC,QAAU,IAAIC,KAAK,4CAMH,KAChBH,kBAAoBI,SAASC,iBAAiB,gDAC1CC,YAAcN,kBAAkBO,KAAK,GACrCC,UAAYR,kBAAkBO,KAAK,GACvCN,UAAYQ,QAAQH,aACLE,UAAUE,cAAc,0BAC1BC,UACTT,QAAUO,QAAQD,YAItBF,YAAYM,iBAAiB,SAAUC,kBACvCL,UAAUI,iBAAiB,SAAUE,oBAEhC,MAAMC,QAAQf,kBAAkBgB,UAC7BD,KAAK,GAAK,GACVA,KAAK,GAAGH,iBAAiB,SAAUK,gBAI3CC,EAAEC,KAAKC,aAAaC,SAASC,GAAG,kBAAmBC,aAAa,UAM9DA,YAAc,SACZC,WAAaN,EAAEC,KAAKC,aAAaK,aACjC,OAASD,YAIbE,YACI,eAEQC,iBADKH,WAAWI,cAAcC,aACRC,QAAQ,4CAC9BH,mBAAqB3B,kBAAkBO,KAAK,GAC5CM,mBACOc,mBAAqB3B,kBAAkBO,KAAK,GACnDO,iBAEAiB,eAAeJ,oBAGvB,MASFd,iBAAmB,SACjBmB,gBAAkB,EACtB/B,UAAYQ,QAAQT,kBAAkBO,KAAK,QACtC,MAAMQ,QAAQf,kBAAkBgB,aAC7BD,KAAK,GAAK,EAAG,KACTkB,SAAWlB,KAAK,GAAGL,cAAc,2BACpB,OAAbuB,UAAqBA,SAAStB,WAC9BqB,iBAAmBD,eAAehB,KAAK,YAK/CiB,gBAAkB,GACXE,aAAaC,OAAO,CACvBC,KAAMF,aAAaG,MAAMC,QACzBC,OAAO,mBAAU,wBAAyB,oBAC1CC,MAAM,mBAAU,uBAAwB,sBAEvCC,MAAKC,QACFA,MAAMC,OACCD,UAYjB5B,eAAiB,SACfkB,gBAAkB,EACtB9B,QAAUO,QAAQT,kBAAkBO,KAAK,QACpC,MAAMQ,QAAQf,kBAAkBgB,aAC7BD,KAAK,GAAK,EAAG,KACTkB,SAAWlB,KAAK,GAAGL,cAAc,2BACpB,OAAbuB,UAAqBA,SAAStB,WAC9BqB,iBAAmBD,eAAehB,KAAK,QAK/CiB,gBAAkB,SACXE,aAAaC,OAAO,CACvBC,KAAMF,aAAaG,MAAMC,QACzBC,OAAO,mBAAU,wBAAyB,oBAC1CC,MAAM,mBAAU,uBAAwB,sBAEvCC,MAAKC,QACFA,MAAMC,OACCD,UAUjBzB,eAAkB2B,SAEhBC,UAAYD,GAAGE,OAAOhB,QAAQ,4CAElCC,eAAec,YAQbpC,QAAWsC,aACTC,KAAOD,OAAOrC,cAAc,kDAC5BuC,MAAQF,OAAOrC,cAAc,kDAC7BwC,IAAMH,OAAOrC,cAAc,kDAC3ByC,KAAOJ,OAAOrC,cAAc,kDAC5B0C,OAASL,OAAOrC,cAAc,yDAC3B,IAAIP,KACP6C,KAAKK,QAAQL,KAAKM,eAAeC,MACjCN,MAAMI,QAAQJ,MAAMK,eAAeC,MAAQ,EAC3CL,IAAIG,QAAQH,IAAII,eAAeC,MAC/BJ,KAAKE,QAAQF,KAAKG,eAAeC,MACjCH,OAAOC,QAAQD,OAAOE,eAAeC,QASvCxB,eAAkByB,SAChBC,YAAa,EACbC,QAAUjD,QAAQ+C,OAClBE,QAAUxD,SAAWwD,QAAUzD,UAAW,CAC1CuD,GAAGG,WAAWC,UAAUC,IA5JR,kBA6JX,MAAMC,SAASN,GAAGnD,iBAAiB,eAAe0D,SACnDD,MAAMF,UAAUC,IA9JJ,cAgKhBJ,YAAa,MACV,CACHD,GAAGG,WAAWC,UAAUI,OAlKR,kBAmKX,MAAMF,SAASN,GAAGnD,iBAAiB,eAAe0D,SACnDD,MAAMF,UAAUI,OApKJ,qBAuKbP"} \ No newline at end of file diff --git a/amd/src/datehelper.js b/amd/src/datehelper.js index 090c5b4..e09e38f 100644 --- a/amd/src/datehelper.js +++ b/amd/src/datehelper.js @@ -24,18 +24,25 @@ import * as ModalFactory from 'core/modal_factory'; import {get_string as getString} from 'core/str'; let datetimeSelectors = {}; -let endDate = {}; -const classEnd = "afterend"; +let startDate = {}; +let endDate = new Date("December 31, 2099 23:59:59"); +const classOutOfRange = "outofrange"; /** * Initialiser function. */ export const init = () => { datetimeSelectors = document.querySelectorAll("div[data-fieldtype='date_time_selector']"); + let courseStart = datetimeSelectors.item(0); let courseEnd = datetimeSelectors.item(1); - endDate = getDate(courseEnd); + startDate = getDate(courseStart); + let optional = courseEnd.querySelector("input[type='checkbox']"); + if (optional.checked) { + endDate = getDate(courseEnd); + } // Event Listeners. + courseStart.addEventListener("change", startDateChanged); courseEnd.addEventListener("change", endDateChanged); // Activity module date time selector events. for (const date of datetimeSelectors.entries()) { @@ -60,38 +67,72 @@ const updateDates = () => { function() { let el = datepicker.calendarimage.getDOMNode(); let datetimeSelector = el.closest("div[data-fieldtype='date_time_selector']"); - if (datetimeSelector === datetimeSelectors.item(1)) { + if (datetimeSelector === datetimeSelectors.item(0)) { + startDateChanged(); + } else if (datetimeSelector === datetimeSelectors.item(1)) { endDateChanged(); } else { - checkEndDate(datetimeSelector); + checkDateRange(datetimeSelector); } }, 100 ); }; +/** + * The start date of the course has been changed. + * Adjust all other dates accordingly if desired. + * @returns {*} + */ +const startDateChanged = () => { + let datesOutOfRange = 0; + startDate = getDate(datetimeSelectors.item(0)); + for (const date of datetimeSelectors.entries()) { + if (date[0] > 1) { + let optional = date[1].querySelector("input[type='checkbox']"); + if (optional === null || optional.checked) { + datesOutOfRange += checkDateRange(date[1]); + } + } + } + + if (datesOutOfRange > 0) { + return ModalFactory.create({ + type: ModalFactory.types.DEFAULT, + title: getString('datesoutofrange_title', 'report_editdates'), + body: getString('datesoutofrange_body', 'report_editdates') + }) + .then(modal => { + modal.show(); + return modal; + }); + } + + return false; +}; + /** * The end date of the course has been changed. * Check if any activity module dates are after this date. * @returns {*} */ const endDateChanged = () => { - let datesPastEnd = 0; + let datesOutOfRange = 0; endDate = getDate(datetimeSelectors.item(1)); for (const date of datetimeSelectors.entries()) { if (date[0] > 1) { let optional = date[1].querySelector("input[type='checkbox']"); if (optional === null || optional.checked) { - datesPastEnd += checkEndDate(date[1]); + datesOutOfRange += checkDateRange(date[1]); } } } - if (datesPastEnd > 0) { + if (datesOutOfRange > 0) { return ModalFactory.create({ type: ModalFactory.types.DEFAULT, - title: getString('datesafterend_title', 'report_editdates'), - body: getString('datesafterend_body', 'report_editdates') + title: getString('datesoutofrange_title', 'report_editdates'), + body: getString('datesoutofrange_body', 'report_editdates') }) .then(modal => { modal.show(); @@ -109,7 +150,7 @@ const modDateChanged = (ev) => { // Check against course end date. let container = ev.target.closest("div[data-fieldtype='date_time_selector']"); - checkEndDate(container); + checkDateRange(container); }; /** @@ -137,19 +178,20 @@ const getDate = (dateEl) => { * @param {HTMLElement} el * @returns {boolean} */ -const checkEndDate = (el) => { - let afterEndDate = false; - if (getDate(el) > endDate) { - el.parentNode.classList.add(classEnd); +const checkDateRange = (el) => { + let outOfRange = false; + let modDate = getDate(el); + if (modDate > endDate || modDate < startDate) { + el.parentNode.classList.add(classOutOfRange); for (const input of el.querySelectorAll(".form-group").values()) { - input.classList.add(classEnd); + input.classList.add(classOutOfRange); } - afterEndDate = true; + outOfRange = true; } else { - el.parentNode.classList.remove(classEnd); + el.parentNode.classList.remove(classOutOfRange); for (const input of el.querySelectorAll(".form-group").values()) { - input.classList.remove(classEnd); + input.classList.remove(classOutOfRange); } } - return afterEndDate; + return outOfRange; }; diff --git a/form.php b/form.php index 05cdd13..d5b37fe 100644 --- a/form.php +++ b/form.php @@ -190,12 +190,15 @@ public function definition() { if ($mod && ($cmdatesettings = $mod->get_settings($cm))) { // Added activity name on the form. foreach ($cmdatesettings as $cmdatetype => $cmdatesetting) { - $aferendclass = $cmdatesetting->currentvalue > $course->enddate ? 'afterend' : ''; + $dateoutofrange = ($cmdatesetting->currentvalue > $course->enddate && $course->enddate != 0) || + $cmdatesetting->currentvalue < $course->startdate && + $cmdatesetting->currentvalue != 0; + $dateoutofrangeclass = $dateoutofrange ? 'outofrange' : ''; $elname = 'date_mod_'.$cm->id.'_'.$cmdatetype; $mform->addElement($cmdatesetting->type, $elname, $cmdatesetting->label, array( 'optional' => $cmdatesetting->isoptional, - 'step' => $cmdatesetting->getstep), ['class' => $aferendclass]); + 'step' => $cmdatesetting->getstep), ['class' => $dateoutofrangeclass]); $mform->setDefault($elname, $cmdatesetting->currentvalue); $timeline[$cm->id] = array_merge($timeline[$cm->id], array($cmdatesetting->label => $cmdatesetting->currentvalue)); diff --git a/lang/en/report_editdates.php b/lang/en/report_editdates.php index fefa735..98a8498 100644 --- a/lang/en/report_editdates.php +++ b/lang/en/report_editdates.php @@ -56,5 +56,6 @@ $string['editrestrictedaccess'] = 'Edit restricted access (opens a new window)'; $string['event:reportviewed'] = 'Edit dates report viewed'; $string['privacy:metadata'] = 'The Dates plugin does not store any personal data.'; -$string['datesafterend_title'] = 'Uh oh'; -$string['datesafterend_body'] = 'There are one or more dates that are after the end of the course. You amy want to check the dates on this page.'; +$string['datesoutofrange_title'] = 'Uh oh'; +$string['datesoutofrange_body'] = 'There are one or more dates that are either after the end of the course or +before the start of the course. You may want to check the dates on this page.'; diff --git a/styles.css b/styles.css index 4cc3e35..ee642e1 100644 --- a/styles.css +++ b/styles.css @@ -76,11 +76,11 @@ padding: 2px; } -.path-report-editdates .afterend select { +.path-report-editdates .outofrange select { color: red; } -.path-report-editdates div.row.afterend .col-form-label::after { +.path-report-editdates div.row.outofrange .col-form-label::after { content: "\f071"; font-family: "fontawesome"; color: orange; From a5d017be46d5ed1239073923866cb2ded766da8c Mon Sep 17 00:00:00 2001 From: Jeremy FitzPatrick Date: Mon, 21 Nov 2022 17:01:36 +1300 Subject: [PATCH 3/3] Adjust all module dates when course start date is changed. --- amd/build/datehelper.min.js | 2 +- amd/build/datehelper.min.js.map | 2 +- amd/src/datehelper.js | 153 +++++++++++++++++++++----------- form.php | 3 + lang/en/report_editdates.php | 4 + 5 files changed, 112 insertions(+), 52 deletions(-) diff --git a/amd/build/datehelper.min.js b/amd/build/datehelper.min.js index 01dec4e..d3e844b 100644 --- a/amd/build/datehelper.min.js +++ b/amd/build/datehelper.min.js @@ -1,3 +1,3 @@ -define("report_editdates/datehelper",["exports","core/modal_factory","core/str"],(function(_exports,ModalFactory,_str){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,ModalFactory=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalFactory);let datetimeSelectors={},startDate={},endDate=new Date("December 31, 2099 23:59:59");_exports.init=()=>{datetimeSelectors=document.querySelectorAll("div[data-fieldtype='date_time_selector']");let courseStart=datetimeSelectors.item(0),courseEnd=datetimeSelectors.item(1);startDate=getDate(courseStart),courseEnd.querySelector("input[type='checkbox']").checked&&(endDate=getDate(courseEnd)),courseStart.addEventListener("change",startDateChanged),courseEnd.addEventListener("change",endDateChanged);for(const date of datetimeSelectors.entries())date[0]>1&&date[1].addEventListener("change",modDateChanged);M.form.dateselector.calendar.on("selectionChange",updateDates,!1)};const updateDates=()=>{let datepicker=M.form.dateselector.currentowner;null!==datepicker&&setTimeout((function(){let datetimeSelector=datepicker.calendarimage.getDOMNode().closest("div[data-fieldtype='date_time_selector']");datetimeSelector===datetimeSelectors.item(0)?startDateChanged():datetimeSelector===datetimeSelectors.item(1)?endDateChanged():checkDateRange(datetimeSelector)}),100)},startDateChanged=()=>{let datesOutOfRange=0;startDate=getDate(datetimeSelectors.item(0));for(const date of datetimeSelectors.entries())if(date[0]>1){let optional=date[1].querySelector("input[type='checkbox']");(null===optional||optional.checked)&&(datesOutOfRange+=checkDateRange(date[1]))}return datesOutOfRange>0&&ModalFactory.create({type:ModalFactory.types.DEFAULT,title:(0,_str.get_string)("datesoutofrange_title","report_editdates"),body:(0,_str.get_string)("datesoutofrange_body","report_editdates")}).then((modal=>(modal.show(),modal)))},endDateChanged=()=>{let datesOutOfRange=0;endDate=getDate(datetimeSelectors.item(1));for(const date of datetimeSelectors.entries())if(date[0]>1){let optional=date[1].querySelector("input[type='checkbox']");(null===optional||optional.checked)&&(datesOutOfRange+=checkDateRange(date[1]))}if(datesOutOfRange>0)return ModalFactory.create({type:ModalFactory.types.DEFAULT,title:(0,_str.get_string)("datesoutofrange_title","report_editdates"),body:(0,_str.get_string)("datesoutofrange_body","report_editdates")}).then((modal=>(modal.show(),modal)))},modDateChanged=ev=>{let container=ev.target.closest("div[data-fieldtype='date_time_selector']");checkDateRange(container)},getDate=dateEl=>{let year=dateEl.querySelector(".fdate_time_selector > div:nth-child(3) select"),month=dateEl.querySelector(".fdate_time_selector > div:nth-child(2) select"),day=dateEl.querySelector(".fdate_time_selector > div:nth-child(1) select"),hour=dateEl.querySelector(".fdate_time_selector > div:nth-child(4) select"),minute=dateEl.querySelector(".fdate_time_selector > div:nth-child(5) select");return new Date(year.options[year.selectedIndex].value,month.options[month.selectedIndex].value-1,day.options[day.selectedIndex].value,hour.options[hour.selectedIndex].value,minute.options[minute.selectedIndex].value)},checkDateRange=el=>{let outOfRange=!1,modDate=getDate(el);if(modDate>endDate||modDate{datetimeSelectors=document.querySelectorAll("div[data-fieldtype='date_time_selector']");let courseStart=datetimeSelectors.item(0),courseEnd=datetimeSelectors.item(1);startDate=getDate(courseStart),courseEnd.querySelector("input[type='checkbox']").checked&&(endDate=getDate(courseEnd)),courseStart.addEventListener("change",startDateChanged),courseEnd.addEventListener("change",endDateChanged);for(const date of datetimeSelectors.entries())date[0]>1&&date[1].addEventListener("change",modDateChanged);M.form.dateselector.calendar.on("selectionChange",updateDates,!1)};const updateDates=()=>{let datepicker=M.form.dateselector.currentowner;null!==datepicker&&setTimeout((function(){let datetimeSelector=datepicker.calendarimage.getDOMNode().closest("div[data-fieldtype='date_time_selector']");datetimeSelector===datetimeSelectors.item(0)?startDateChanged():datetimeSelector===datetimeSelectors.item(1)?endDateChanged():checkDateRange(datetimeSelector)}),100)},startDateChanged=()=>{startDate=adjustDates(),checkAllDatesRange()},endDateChanged=()=>{let endDateSelector=datetimeSelectors.item(1),optional=endDateSelector.querySelector("input[type='checkbox']");endDate=optional.checked?getDate(endDateSelector):new Date(farawayDate),checkAllDatesRange()},modDateChanged=ev=>{let container=ev.target.closest("div[data-fieldtype='date_time_selector']");checkDateRange(container)},getDate=dateEl=>{let year=dateEl.querySelector(".fdate_time_selector > div:nth-child(3) select"),month=dateEl.querySelector(".fdate_time_selector > div:nth-child(2) select"),day=dateEl.querySelector(".fdate_time_selector > div:nth-child(1) select"),hour=dateEl.querySelector(".fdate_time_selector > div:nth-child(4) select"),minute=dateEl.querySelector(".fdate_time_selector > div:nth-child(5) select");return new Date(year.options[year.selectedIndex].value,month.options[month.selectedIndex].value-1,day.options[day.selectedIndex].value,hour.options[hour.selectedIndex].value,minute.options[minute.selectedIndex].value)},setDate=(dateEl,date)=>{let year=dateEl.querySelector(".fdate_time_selector > div:nth-child(3) select"),month=dateEl.querySelector(".fdate_time_selector > div:nth-child(2) select"),day=dateEl.querySelector(".fdate_time_selector > div:nth-child(1) select"),hour=dateEl.querySelector(".fdate_time_selector > div:nth-child(4) select"),minute=dateEl.querySelector(".fdate_time_selector > div:nth-child(5) select");year.querySelector("[value='"+date.getFullYear()+"']").selected="selected",month.querySelector("[value='"+(date.getMonth()+1)+"']").selected="selected",day.querySelector("[value='"+date.getDate()+"']").selected="selected",hour.querySelector("[value='"+date.getHours()+"']").selected="selected",minute.querySelector("[value='"+date.getMinutes()+"']").selected="selected"},checkAllDatesRange=()=>{let datesOutOfRange=0;for(const date of datetimeSelectors.entries())if(date[0]>1){let optional=date[1].querySelector("input[type='checkbox']");(null===optional||optional.checked)&&(datesOutOfRange+=checkDateRange(date[1]))}return datesOutOfRange>0&&ModalFactory.create({type:ModalFactory.types.DEFAULT,title:(0,_str.get_string)("datesoutofrange_title","report_editdates"),body:(0,_str.get_string)("datesoutofrange_body","report_editdates")}).then((modal=>(modal.show(),modal)))},checkDateRange=el=>{let outOfRange=!1,modDate=getDate(el);if(modDate>endDate||modDate{if(!document.getElementById("id_movealldates").checked)return startDate;let newStart=getDate(datetimeSelectors.item(0)),dst1=startDate.getTimezoneOffset()-newStart.getTimezoneOffset(),change=newStart-startDate+6e4*dst1;for(const date of datetimeSelectors.entries())if(date[0]>0){let optional=date[1].querySelector("input[type='checkbox']");if(null===optional||optional.checked){let currDate=getDate(date[1]),newDate=new Date(currDate.getTime()+change),dst2=newDate.getTimezoneOffset()-currDate.getTimezoneOffset();newDate=new Date(newDate.getTime()+6e4*dst2),setDate(date[1],newDate)}}let endDateSelector=datetimeSelectors.item(1),optional=endDateSelector.querySelector("input[type='checkbox']");return endDate=optional.checked?getDate(endDateSelector):new Date(farawayDate),newStart}})); //# sourceMappingURL=datehelper.min.js.map \ No newline at end of file diff --git a/amd/build/datehelper.min.js.map b/amd/build/datehelper.min.js.map index ea566f0..d6dc565 100644 --- a/amd/build/datehelper.min.js.map +++ b/amd/build/datehelper.min.js.map @@ -1 +1 @@ -{"version":3,"file":"datehelper.min.js","sources":["../src/datehelper.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 * AMD module to help editing course dates\n *\n * @module report_editdates/datehelper\n * @copyright 2022 Te Wānanga o Aotearoa\n */\n\nimport * as ModalFactory from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\n\nlet datetimeSelectors = {};\nlet startDate = {};\nlet endDate = new Date(\"December 31, 2099 23:59:59\");\nconst classOutOfRange = \"outofrange\";\n\n/**\n * Initialiser function.\n */\nexport const init = () => {\n datetimeSelectors = document.querySelectorAll(\"div[data-fieldtype='date_time_selector']\");\n let courseStart = datetimeSelectors.item(0);\n let courseEnd = datetimeSelectors.item(1);\n startDate = getDate(courseStart);\n let optional = courseEnd.querySelector(\"input[type='checkbox']\");\n if (optional.checked) {\n endDate = getDate(courseEnd);\n }\n\n // Event Listeners.\n courseStart.addEventListener(\"change\", startDateChanged);\n courseEnd.addEventListener(\"change\", endDateChanged);\n // Activity module date time selector events.\n for (const date of datetimeSelectors.entries()) {\n if (date[0] > 1) {\n date[1].addEventListener(\"change\", modDateChanged);\n }\n }\n // Date picker event.\n M.form.dateselector.calendar.on('selectionChange', updateDates, false);\n};\n\n/**\n * A date has been changed using the date selector calendar.\n */\nconst updateDates = () => {\n let datepicker = M.form.dateselector.currentowner;\n if (null === datepicker) {\n return;\n }\n // Todo: is there a better way to ensure this runs after the selects have been updated?\n setTimeout(\n function() {\n let el = datepicker.calendarimage.getDOMNode();\n let datetimeSelector = el.closest(\"div[data-fieldtype='date_time_selector']\");\n if (datetimeSelector === datetimeSelectors.item(0)) {\n startDateChanged();\n } else if (datetimeSelector === datetimeSelectors.item(1)) {\n endDateChanged();\n } else {\n checkDateRange(datetimeSelector);\n }\n },\n 100\n );\n};\n\n/**\n * The start date of the course has been changed.\n * Adjust all other dates accordingly if desired.\n * @returns {*}\n */\nconst startDateChanged = () => {\n let datesOutOfRange = 0;\n startDate = getDate(datetimeSelectors.item(0));\n for (const date of datetimeSelectors.entries()) {\n if (date[0] > 1) {\n let optional = date[1].querySelector(\"input[type='checkbox']\");\n if (optional === null || optional.checked) {\n datesOutOfRange += checkDateRange(date[1]);\n }\n }\n }\n\n if (datesOutOfRange > 0) {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: getString('datesoutofrange_title', 'report_editdates'),\n body: getString('datesoutofrange_body', 'report_editdates')\n })\n .then(modal => {\n modal.show();\n return modal;\n });\n }\n\n return false;\n};\n\n/**\n * The end date of the course has been changed.\n * Check if any activity module dates are after this date.\n * @returns {*}\n */\nconst endDateChanged = () => {\n let datesOutOfRange = 0;\n endDate = getDate(datetimeSelectors.item(1));\n for (const date of datetimeSelectors.entries()) {\n if (date[0] > 1) {\n let optional = date[1].querySelector(\"input[type='checkbox']\");\n if (optional === null || optional.checked) {\n datesOutOfRange += checkDateRange(date[1]);\n }\n }\n }\n\n if (datesOutOfRange > 0) {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: getString('datesoutofrange_title', 'report_editdates'),\n body: getString('datesoutofrange_body', 'report_editdates')\n })\n .then(modal => {\n modal.show();\n return modal;\n });\n }\n};\n\n/**\n * An activity module date has been changed.\n * Check and warn if it is now after the course end date.\n * @param {Event} ev\n */\nconst modDateChanged = (ev) => {\n // Check against course end date.\n let container = ev.target.closest(\"div[data-fieldtype='date_time_selector']\");\n\n checkDateRange(container);\n};\n\n/**\n * Get a Date object from the select DOM elements.\n * @param {HTMLElement} dateEl\n * @returns {Date}\n */\nconst getDate = (dateEl) => {\n let year = dateEl.querySelector(\".fdate_time_selector > div:nth-child(3) select\");\n let month = dateEl.querySelector(\".fdate_time_selector > div:nth-child(2) select\");\n let day = dateEl.querySelector(\".fdate_time_selector > div:nth-child(1) select\");\n let hour = dateEl.querySelector(\".fdate_time_selector > div:nth-child(4) select\");\n let minute = dateEl.querySelector(\".fdate_time_selector > div:nth-child(5) select\");\n return new Date(\n year.options[year.selectedIndex].value,\n month.options[month.selectedIndex].value - 1,\n day.options[day.selectedIndex].value,\n hour.options[hour.selectedIndex].value,\n minute.options[minute.selectedIndex].value,\n );\n};\n\n/**\n * Compare the date with the course end date. Toggle CSS class of element.\n * @param {HTMLElement} el\n * @returns {boolean}\n */\nconst checkDateRange = (el) => {\n let outOfRange = false;\n let modDate = getDate(el);\n if (modDate > endDate || modDate < startDate) {\n el.parentNode.classList.add(classOutOfRange);\n for (const input of el.querySelectorAll(\".form-group\").values()) {\n input.classList.add(classOutOfRange);\n }\n outOfRange = true;\n } else {\n el.parentNode.classList.remove(classOutOfRange);\n for (const input of el.querySelectorAll(\".form-group\").values()) {\n input.classList.remove(classOutOfRange);\n }\n }\n return outOfRange;\n};\n"],"names":["datetimeSelectors","startDate","endDate","Date","document","querySelectorAll","courseStart","item","courseEnd","getDate","querySelector","checked","addEventListener","startDateChanged","endDateChanged","date","entries","modDateChanged","M","form","dateselector","calendar","on","updateDates","datepicker","currentowner","setTimeout","datetimeSelector","calendarimage","getDOMNode","closest","checkDateRange","datesOutOfRange","optional","ModalFactory","create","type","types","DEFAULT","title","body","then","modal","show","ev","container","target","dateEl","year","month","day","hour","minute","options","selectedIndex","value","el","outOfRange","modDate","parentNode","classList","add","input","values","remove"],"mappings":"moCAyBIA,kBAAoB,GACpBC,UAAY,GACZC,QAAU,IAAIC,KAAK,4CAMH,KAChBH,kBAAoBI,SAASC,iBAAiB,gDAC1CC,YAAcN,kBAAkBO,KAAK,GACrCC,UAAYR,kBAAkBO,KAAK,GACvCN,UAAYQ,QAAQH,aACLE,UAAUE,cAAc,0BAC1BC,UACTT,QAAUO,QAAQD,YAItBF,YAAYM,iBAAiB,SAAUC,kBACvCL,UAAUI,iBAAiB,SAAUE,oBAEhC,MAAMC,QAAQf,kBAAkBgB,UAC7BD,KAAK,GAAK,GACVA,KAAK,GAAGH,iBAAiB,SAAUK,gBAI3CC,EAAEC,KAAKC,aAAaC,SAASC,GAAG,kBAAmBC,aAAa,UAM9DA,YAAc,SACZC,WAAaN,EAAEC,KAAKC,aAAaK,aACjC,OAASD,YAIbE,YACI,eAEQC,iBADKH,WAAWI,cAAcC,aACRC,QAAQ,4CAC9BH,mBAAqB3B,kBAAkBO,KAAK,GAC5CM,mBACOc,mBAAqB3B,kBAAkBO,KAAK,GACnDO,iBAEAiB,eAAeJ,oBAGvB,MASFd,iBAAmB,SACjBmB,gBAAkB,EACtB/B,UAAYQ,QAAQT,kBAAkBO,KAAK,QACtC,MAAMQ,QAAQf,kBAAkBgB,aAC7BD,KAAK,GAAK,EAAG,KACTkB,SAAWlB,KAAK,GAAGL,cAAc,2BACpB,OAAbuB,UAAqBA,SAAStB,WAC9BqB,iBAAmBD,eAAehB,KAAK,YAK/CiB,gBAAkB,GACXE,aAAaC,OAAO,CACvBC,KAAMF,aAAaG,MAAMC,QACzBC,OAAO,mBAAU,wBAAyB,oBAC1CC,MAAM,mBAAU,uBAAwB,sBAEvCC,MAAKC,QACFA,MAAMC,OACCD,UAYjB5B,eAAiB,SACfkB,gBAAkB,EACtB9B,QAAUO,QAAQT,kBAAkBO,KAAK,QACpC,MAAMQ,QAAQf,kBAAkBgB,aAC7BD,KAAK,GAAK,EAAG,KACTkB,SAAWlB,KAAK,GAAGL,cAAc,2BACpB,OAAbuB,UAAqBA,SAAStB,WAC9BqB,iBAAmBD,eAAehB,KAAK,QAK/CiB,gBAAkB,SACXE,aAAaC,OAAO,CACvBC,KAAMF,aAAaG,MAAMC,QACzBC,OAAO,mBAAU,wBAAyB,oBAC1CC,MAAM,mBAAU,uBAAwB,sBAEvCC,MAAKC,QACFA,MAAMC,OACCD,UAUjBzB,eAAkB2B,SAEhBC,UAAYD,GAAGE,OAAOhB,QAAQ,4CAElCC,eAAec,YAQbpC,QAAWsC,aACTC,KAAOD,OAAOrC,cAAc,kDAC5BuC,MAAQF,OAAOrC,cAAc,kDAC7BwC,IAAMH,OAAOrC,cAAc,kDAC3ByC,KAAOJ,OAAOrC,cAAc,kDAC5B0C,OAASL,OAAOrC,cAAc,yDAC3B,IAAIP,KACP6C,KAAKK,QAAQL,KAAKM,eAAeC,MACjCN,MAAMI,QAAQJ,MAAMK,eAAeC,MAAQ,EAC3CL,IAAIG,QAAQH,IAAII,eAAeC,MAC/BJ,KAAKE,QAAQF,KAAKG,eAAeC,MACjCH,OAAOC,QAAQD,OAAOE,eAAeC,QASvCxB,eAAkByB,SAChBC,YAAa,EACbC,QAAUjD,QAAQ+C,OAClBE,QAAUxD,SAAWwD,QAAUzD,UAAW,CAC1CuD,GAAGG,WAAWC,UAAUC,IA5JR,kBA6JX,MAAMC,SAASN,GAAGnD,iBAAiB,eAAe0D,SACnDD,MAAMF,UAAUC,IA9JJ,cAgKhBJ,YAAa,MACV,CACHD,GAAGG,WAAWC,UAAUI,OAlKR,kBAmKX,MAAMF,SAASN,GAAGnD,iBAAiB,eAAe0D,SACnDD,MAAMF,UAAUI,OApKJ,qBAuKbP"} \ No newline at end of file +{"version":3,"file":"datehelper.min.js","sources":["../src/datehelper.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 * AMD module to help editing course dates\n *\n * @module report_editdates/datehelper\n * @copyright 2022 Te Wānanga o Aotearoa\n */\n\nimport * as ModalFactory from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\n\nconst classOutOfRange = \"outofrange\";\nconst farawayDate = \"December 31, 2099 23:59:59\";\nlet datetimeSelectors = {};\nlet startDate = {};\nlet endDate = new Date(farawayDate);\n\n/**\n * Initialiser function.\n */\nexport const init = () => {\n // Give our variables values.\n datetimeSelectors = document.querySelectorAll(\"div[data-fieldtype='date_time_selector']\");\n let courseStart = datetimeSelectors.item(0);\n let courseEnd = datetimeSelectors.item(1);\n startDate = getDate(courseStart);\n let optional = courseEnd.querySelector(\"input[type='checkbox']\");\n if (optional.checked) {\n endDate = getDate(courseEnd);\n }\n\n // Add some event Listeners.\n courseStart.addEventListener(\"change\", startDateChanged);\n courseEnd.addEventListener(\"change\", endDateChanged);\n // Activity module date time selector events.\n for (const date of datetimeSelectors.entries()) {\n if (date[0] > 1) {\n date[1].addEventListener(\"change\", modDateChanged);\n }\n }\n // Date picker event.\n M.form.dateselector.calendar.on('selectionChange', updateDates, false);\n};\n\n/**\n * A date has been changed using the date selector calendar.\n */\nconst updateDates = () => {\n let datepicker = M.form.dateselector.currentowner;\n if (null === datepicker) {\n return;\n }\n // Todo: is there a better way to ensure this runs after the selects have been updated?\n setTimeout(\n function() {\n let el = datepicker.calendarimage.getDOMNode();\n let datetimeSelector = el.closest(\"div[data-fieldtype='date_time_selector']\");\n if (datetimeSelector === datetimeSelectors.item(0)) {\n startDateChanged();\n } else if (datetimeSelector === datetimeSelectors.item(1)) {\n endDateChanged();\n } else {\n checkDateRange(datetimeSelector);\n }\n },\n 100\n );\n};\n\n/**\n * The start date of the course has been changed.\n * Adjust all other dates accordingly if desired.\n */\nconst startDateChanged = () => {\n startDate = adjustDates();\n checkAllDatesRange();\n};\n\n/**\n * The end date of the course has been changed.\n * Check if any activity module dates are after this date.\n */\nconst endDateChanged = () => {\n let endDateSelector = datetimeSelectors.item(1);\n let optional = endDateSelector.querySelector(\"input[type='checkbox']\");\n if (optional.checked) {\n endDate = getDate(endDateSelector);\n } else {\n endDate = new Date(farawayDate);\n }\n checkAllDatesRange();\n};\n\n/**\n * An activity module date has been changed.\n * Check and warn if it is now after the course end date.\n * @param {Event} ev\n */\nconst modDateChanged = (ev) => {\n // Check against course end date.\n let container = ev.target.closest(\"div[data-fieldtype='date_time_selector']\");\n\n checkDateRange(container);\n};\n\n/**\n * Get a Date object from the select DOM elements.\n * @param {HTMLElement} dateEl\n * @returns {Date}\n */\nconst getDate = (dateEl) => {\n let year = dateEl.querySelector(\".fdate_time_selector > div:nth-child(3) select\");\n let month = dateEl.querySelector(\".fdate_time_selector > div:nth-child(2) select\");\n let day = dateEl.querySelector(\".fdate_time_selector > div:nth-child(1) select\");\n let hour = dateEl.querySelector(\".fdate_time_selector > div:nth-child(4) select\");\n let minute = dateEl.querySelector(\".fdate_time_selector > div:nth-child(5) select\");\n return new Date(\n year.options[year.selectedIndex].value,\n month.options[month.selectedIndex].value - 1,\n day.options[day.selectedIndex].value,\n hour.options[hour.selectedIndex].value,\n minute.options[minute.selectedIndex].value,\n );\n};\n\n/**\n * Update the select DOM elements with a given Date object.\n * @param {HTMLElement} dateEl\n * @param {Date} date\n */\nconst setDate = (dateEl, date) => {\n let year = dateEl.querySelector(\".fdate_time_selector > div:nth-child(3) select\");\n let month = dateEl.querySelector(\".fdate_time_selector > div:nth-child(2) select\");\n let day = dateEl.querySelector(\".fdate_time_selector > div:nth-child(1) select\");\n let hour = dateEl.querySelector(\".fdate_time_selector > div:nth-child(4) select\");\n let minute = dateEl.querySelector(\".fdate_time_selector > div:nth-child(5) select\");\n year.querySelector(\"[value='\" + date.getFullYear() + \"']\").selected = \"selected\";\n month.querySelector(\"[value='\" + (date.getMonth() + 1) + \"']\").selected = \"selected\";\n day.querySelector(\"[value='\" + date.getDate() + \"']\").selected = \"selected\";\n hour.querySelector(\"[value='\" + date.getHours() + \"']\").selected = \"selected\";\n minute.querySelector(\"[value='\" + date.getMinutes() + \"']\").selected = \"selected\";\n};\n\n/**\n * Check if the date change has caused any dates to be outside the course start and end.\n * @returns {boolean|*}\n */\nconst checkAllDatesRange = () => {\n let datesOutOfRange = 0;\n // Loop dates and if they are enabled then check the date range.\n for (const date of datetimeSelectors.entries()) {\n if (date[0] > 1) {\n let optional = date[1].querySelector(\"input[type='checkbox']\");\n if (optional === null || optional.checked) {\n datesOutOfRange += checkDateRange(date[1]);\n }\n }\n }\n\n // Warn the person about it.\n if (datesOutOfRange > 0) {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: getString('datesoutofrange_title', 'report_editdates'),\n body: getString('datesoutofrange_body', 'report_editdates')\n })\n .then(modal => {\n modal.show();\n return modal;\n });\n }\n\n return false;\n};\n\n/**\n * Compare the date with the course end date. Toggle CSS class of element.\n * @param {HTMLElement} el\n * @returns {boolean}\n */\nconst checkDateRange = (el) => {\n let outOfRange = false;\n let modDate = getDate(el);\n if (modDate > endDate || modDate < startDate) {\n el.parentNode.classList.add(classOutOfRange);\n for (const input of el.querySelectorAll(\".form-group\").values()) {\n input.classList.add(classOutOfRange);\n }\n outOfRange = true;\n } else {\n el.parentNode.classList.remove(classOutOfRange);\n for (const input of el.querySelectorAll(\".form-group\").values()) {\n input.classList.remove(classOutOfRange);\n }\n }\n return outOfRange;\n};\n\n/**\n * Change the enabled dates by the amount the start date has been shifted.\n * @returns {Date}\n */\nconst adjustDates = () => {\n if (!document.getElementById(\"id_movealldates\").checked) {\n return startDate;\n }\n // Determine the change.\n let newStart = getDate(datetimeSelectors.item(0));\n let dst1 = startDate.getTimezoneOffset() - newStart.getTimezoneOffset(); // Account for daylight savings shift.\n let change = newStart - startDate + (dst1 * 60000);\n\n // Update all enabled dates.\n for (const date of datetimeSelectors.entries()) {\n if (date[0] > 0) {\n let optional = date[1].querySelector(\"input[type='checkbox']\");\n if (optional === null || optional.checked) {\n let currDate = getDate(date[1]);\n let newDate = new Date(currDate.getTime() + change);\n let dst2 = newDate.getTimezoneOffset() - currDate.getTimezoneOffset();\n newDate = new Date(newDate.getTime() + (dst2 * 60000));\n setDate(date[1], newDate);\n }\n }\n }\n\n // Update the stored value for the end date.\n let endDateSelector = datetimeSelectors.item(1);\n let optional = endDateSelector.querySelector(\"input[type='checkbox']\");\n if (optional.checked) {\n endDate = getDate(endDateSelector);\n } else {\n endDate = new Date(farawayDate);\n }\n\n return newStart;\n};\n"],"names":["farawayDate","datetimeSelectors","startDate","endDate","Date","document","querySelectorAll","courseStart","item","courseEnd","getDate","querySelector","checked","addEventListener","startDateChanged","endDateChanged","date","entries","modDateChanged","M","form","dateselector","calendar","on","updateDates","datepicker","currentowner","setTimeout","datetimeSelector","calendarimage","getDOMNode","closest","checkDateRange","adjustDates","checkAllDatesRange","endDateSelector","optional","ev","container","target","dateEl","year","month","day","hour","minute","options","selectedIndex","value","setDate","getFullYear","selected","getMonth","getHours","getMinutes","datesOutOfRange","ModalFactory","create","type","types","DEFAULT","title","body","then","modal","show","el","outOfRange","modDate","parentNode","classList","add","input","values","remove","getElementById","newStart","dst1","getTimezoneOffset","change","currDate","newDate","getTime","dst2"],"mappings":"qoCA0BMA,YAAc,iCAChBC,kBAAoB,GACpBC,UAAY,GACZC,QAAU,IAAIC,KAAKJ,2BAKH,KAEhBC,kBAAoBI,SAASC,iBAAiB,gDAC1CC,YAAcN,kBAAkBO,KAAK,GACrCC,UAAYR,kBAAkBO,KAAK,GACvCN,UAAYQ,QAAQH,aACLE,UAAUE,cAAc,0BAC1BC,UACTT,QAAUO,QAAQD,YAItBF,YAAYM,iBAAiB,SAAUC,kBACvCL,UAAUI,iBAAiB,SAAUE,oBAEhC,MAAMC,QAAQf,kBAAkBgB,UAC7BD,KAAK,GAAK,GACVA,KAAK,GAAGH,iBAAiB,SAAUK,gBAI3CC,EAAEC,KAAKC,aAAaC,SAASC,GAAG,kBAAmBC,aAAa,UAM9DA,YAAc,SACZC,WAAaN,EAAEC,KAAKC,aAAaK,aACjC,OAASD,YAIbE,YACI,eAEQC,iBADKH,WAAWI,cAAcC,aACRC,QAAQ,4CAC9BH,mBAAqB3B,kBAAkBO,KAAK,GAC5CM,mBACOc,mBAAqB3B,kBAAkBO,KAAK,GACnDO,iBAEAiB,eAAeJ,oBAGvB,MAQFd,iBAAmB,KACrBZ,UAAY+B,cACZC,sBAOEnB,eAAiB,SACfoB,gBAAkBlC,kBAAkBO,KAAK,GACzC4B,SAAWD,gBAAgBxB,cAAc,0BAEzCR,QADAiC,SAASxB,QACCF,QAAQyB,iBAER,IAAI/B,KAAKJ,aAEvBkC,sBAQEhB,eAAkBmB,SAEhBC,UAAYD,GAAGE,OAAOR,QAAQ,4CAElCC,eAAeM,YAQb5B,QAAW8B,aACTC,KAAOD,OAAO7B,cAAc,kDAC5B+B,MAAQF,OAAO7B,cAAc,kDAC7BgC,IAAMH,OAAO7B,cAAc,kDAC3BiC,KAAOJ,OAAO7B,cAAc,kDAC5BkC,OAASL,OAAO7B,cAAc,yDAC3B,IAAIP,KACPqC,KAAKK,QAAQL,KAAKM,eAAeC,MACjCN,MAAMI,QAAQJ,MAAMK,eAAeC,MAAQ,EAC3CL,IAAIG,QAAQH,IAAII,eAAeC,MAC/BJ,KAAKE,QAAQF,KAAKG,eAAeC,MACjCH,OAAOC,QAAQD,OAAOE,eAAeC,QASvCC,QAAU,CAACT,OAAQxB,YACjByB,KAAOD,OAAO7B,cAAc,kDAC5B+B,MAAQF,OAAO7B,cAAc,kDAC7BgC,IAAMH,OAAO7B,cAAc,kDAC3BiC,KAAOJ,OAAO7B,cAAc,kDAC5BkC,OAASL,OAAO7B,cAAc,kDAClC8B,KAAK9B,cAAc,WAAaK,KAAKkC,cAAgB,MAAMC,SAAW,WACtET,MAAM/B,cAAc,YAAcK,KAAKoC,WAAa,GAAK,MAAMD,SAAW,WAC1ER,IAAIhC,cAAc,WAAaK,KAAKN,UAAY,MAAMyC,SAAW,WACjEP,KAAKjC,cAAc,WAAaK,KAAKqC,WAAa,MAAMF,SAAW,WACnEN,OAAOlC,cAAc,WAAaK,KAAKsC,aAAe,MAAMH,SAAW,YAOrEjB,mBAAqB,SACnBqB,gBAAkB,MAEjB,MAAMvC,QAAQf,kBAAkBgB,aAC7BD,KAAK,GAAK,EAAG,KACToB,SAAWpB,KAAK,GAAGL,cAAc,2BACpB,OAAbyB,UAAqBA,SAASxB,WAC9B2C,iBAAmBvB,eAAehB,KAAK,YAM/CuC,gBAAkB,GACXC,aAAaC,OAAO,CACvBC,KAAMF,aAAaG,MAAMC,QACzBC,OAAO,mBAAU,wBAAyB,oBAC1CC,MAAM,mBAAU,uBAAwB,sBAEvCC,MAAKC,QACFA,MAAMC,OACCD,UAYjBhC,eAAkBkC,SAChBC,YAAa,EACbC,QAAU1D,QAAQwD,OAClBE,QAAUjE,SAAWiE,QAAUlE,UAAW,CAC1CgE,GAAGG,WAAWC,UAAUC,IA7KR,kBA8KX,MAAMC,SAASN,GAAG5D,iBAAiB,eAAemE,SACnDD,MAAMF,UAAUC,IA/KJ,cAiLhBJ,YAAa,MACV,CACHD,GAAGG,WAAWC,UAAUI,OAnLR,kBAoLX,MAAMF,SAASN,GAAG5D,iBAAiB,eAAemE,SACnDD,MAAMF,UAAUI,OArLJ,qBAwLbP,YAOLlC,YAAc,SACX5B,SAASsE,eAAe,mBAAmB/D,eACrCV,cAGP0E,SAAWlE,QAAQT,kBAAkBO,KAAK,IAC1CqE,KAAO3E,UAAU4E,oBAAsBF,SAASE,oBAChDC,OAASH,SAAW1E,UAAoB,IAAP2E,SAGhC,MAAM7D,QAAQf,kBAAkBgB,aAC7BD,KAAK,GAAK,EAAG,KACToB,SAAWpB,KAAK,GAAGL,cAAc,6BACpB,OAAbyB,UAAqBA,SAASxB,QAAS,KACnCoE,SAAWtE,QAAQM,KAAK,IACxBiE,QAAU,IAAI7E,KAAK4E,SAASE,UAAYH,QACxCI,KAAOF,QAAQH,oBAAsBE,SAASF,oBAClDG,QAAU,IAAI7E,KAAK6E,QAAQC,UAAoB,IAAPC,MACxClC,QAAQjC,KAAK,GAAIiE,cAMzB9C,gBAAkBlC,kBAAkBO,KAAK,GACzC4B,SAAWD,gBAAgBxB,cAAc,iCAEzCR,QADAiC,SAASxB,QACCF,QAAQyB,iBAER,IAAI/B,KAAKJ,aAGhB4E"} \ No newline at end of file diff --git a/amd/src/datehelper.js b/amd/src/datehelper.js index e09e38f..65a90b1 100644 --- a/amd/src/datehelper.js +++ b/amd/src/datehelper.js @@ -23,15 +23,17 @@ import * as ModalFactory from 'core/modal_factory'; import {get_string as getString} from 'core/str'; +const classOutOfRange = "outofrange"; +const farawayDate = "December 31, 2099 23:59:59"; let datetimeSelectors = {}; let startDate = {}; -let endDate = new Date("December 31, 2099 23:59:59"); -const classOutOfRange = "outofrange"; +let endDate = new Date(farawayDate); /** * Initialiser function. */ export const init = () => { + // Give our variables values. datetimeSelectors = document.querySelectorAll("div[data-fieldtype='date_time_selector']"); let courseStart = datetimeSelectors.item(0); let courseEnd = datetimeSelectors.item(1); @@ -41,7 +43,7 @@ export const init = () => { endDate = getDate(courseEnd); } - // Event Listeners. + // Add some event Listeners. courseStart.addEventListener("change", startDateChanged); courseEnd.addEventListener("change", endDateChanged); // Activity module date time selector events. @@ -82,63 +84,25 @@ const updateDates = () => { /** * The start date of the course has been changed. * Adjust all other dates accordingly if desired. - * @returns {*} */ const startDateChanged = () => { - let datesOutOfRange = 0; - startDate = getDate(datetimeSelectors.item(0)); - for (const date of datetimeSelectors.entries()) { - if (date[0] > 1) { - let optional = date[1].querySelector("input[type='checkbox']"); - if (optional === null || optional.checked) { - datesOutOfRange += checkDateRange(date[1]); - } - } - } - - if (datesOutOfRange > 0) { - return ModalFactory.create({ - type: ModalFactory.types.DEFAULT, - title: getString('datesoutofrange_title', 'report_editdates'), - body: getString('datesoutofrange_body', 'report_editdates') - }) - .then(modal => { - modal.show(); - return modal; - }); - } - - return false; + startDate = adjustDates(); + checkAllDatesRange(); }; /** * The end date of the course has been changed. * Check if any activity module dates are after this date. - * @returns {*} */ const endDateChanged = () => { - let datesOutOfRange = 0; - endDate = getDate(datetimeSelectors.item(1)); - for (const date of datetimeSelectors.entries()) { - if (date[0] > 1) { - let optional = date[1].querySelector("input[type='checkbox']"); - if (optional === null || optional.checked) { - datesOutOfRange += checkDateRange(date[1]); - } - } - } - - if (datesOutOfRange > 0) { - return ModalFactory.create({ - type: ModalFactory.types.DEFAULT, - title: getString('datesoutofrange_title', 'report_editdates'), - body: getString('datesoutofrange_body', 'report_editdates') - }) - .then(modal => { - modal.show(); - return modal; - }); + let endDateSelector = datetimeSelectors.item(1); + let optional = endDateSelector.querySelector("input[type='checkbox']"); + if (optional.checked) { + endDate = getDate(endDateSelector); + } else { + endDate = new Date(farawayDate); } + checkAllDatesRange(); }; /** @@ -173,6 +137,56 @@ const getDate = (dateEl) => { ); }; +/** + * Update the select DOM elements with a given Date object. + * @param {HTMLElement} dateEl + * @param {Date} date + */ +const setDate = (dateEl, date) => { + let year = dateEl.querySelector(".fdate_time_selector > div:nth-child(3) select"); + let month = dateEl.querySelector(".fdate_time_selector > div:nth-child(2) select"); + let day = dateEl.querySelector(".fdate_time_selector > div:nth-child(1) select"); + let hour = dateEl.querySelector(".fdate_time_selector > div:nth-child(4) select"); + let minute = dateEl.querySelector(".fdate_time_selector > div:nth-child(5) select"); + year.querySelector("[value='" + date.getFullYear() + "']").selected = "selected"; + month.querySelector("[value='" + (date.getMonth() + 1) + "']").selected = "selected"; + day.querySelector("[value='" + date.getDate() + "']").selected = "selected"; + hour.querySelector("[value='" + date.getHours() + "']").selected = "selected"; + minute.querySelector("[value='" + date.getMinutes() + "']").selected = "selected"; +}; + +/** + * Check if the date change has caused any dates to be outside the course start and end. + * @returns {boolean|*} + */ +const checkAllDatesRange = () => { + let datesOutOfRange = 0; + // Loop dates and if they are enabled then check the date range. + for (const date of datetimeSelectors.entries()) { + if (date[0] > 1) { + let optional = date[1].querySelector("input[type='checkbox']"); + if (optional === null || optional.checked) { + datesOutOfRange += checkDateRange(date[1]); + } + } + } + + // Warn the person about it. + if (datesOutOfRange > 0) { + return ModalFactory.create({ + type: ModalFactory.types.DEFAULT, + title: getString('datesoutofrange_title', 'report_editdates'), + body: getString('datesoutofrange_body', 'report_editdates') + }) + .then(modal => { + modal.show(); + return modal; + }); + } + + return false; +}; + /** * Compare the date with the course end date. Toggle CSS class of element. * @param {HTMLElement} el @@ -195,3 +209,42 @@ const checkDateRange = (el) => { } return outOfRange; }; + +/** + * Change the enabled dates by the amount the start date has been shifted. + * @returns {Date} + */ +const adjustDates = () => { + if (!document.getElementById("id_movealldates").checked) { + return startDate; + } + // Determine the change. + let newStart = getDate(datetimeSelectors.item(0)); + let dst1 = startDate.getTimezoneOffset() - newStart.getTimezoneOffset(); // Account for daylight savings shift. + let change = newStart - startDate + (dst1 * 60000); + + // Update all enabled dates. + for (const date of datetimeSelectors.entries()) { + if (date[0] > 0) { + let optional = date[1].querySelector("input[type='checkbox']"); + if (optional === null || optional.checked) { + let currDate = getDate(date[1]); + let newDate = new Date(currDate.getTime() + change); + let dst2 = newDate.getTimezoneOffset() - currDate.getTimezoneOffset(); + newDate = new Date(newDate.getTime() + (dst2 * 60000)); + setDate(date[1], newDate); + } + } + } + + // Update the stored value for the end date. + let endDateSelector = datetimeSelectors.item(1); + let optional = endDateSelector.querySelector("input[type='checkbox']"); + if (optional.checked) { + endDate = getDate(endDateSelector); + } else { + endDate = new Date(farawayDate); + } + + return newStart; +}; diff --git a/form.php b/form.php index d5b37fe..04d3782 100644 --- a/form.php +++ b/form.php @@ -69,6 +69,9 @@ public function definition() { $mform->addElement('header', 'coursestartdateheader', get_string('coursestartdateheader', 'report_editdates')); $mform->setExpanded('coursestartdateheader', false); + $mform->addElement('checkbox', 'movealldates', get_string('movealldates', 'report_editdates')); + $mform->addHelpButton('movealldates', 'movealldates', 'report_editdates'); + $mform->addElement('date_time_selector', 'coursestartdate', get_string('startdate')); $mform->addHelpButton('coursestartdate', 'startdate'); $mform->setDefault('coursestartdate', $course->startdate); diff --git a/lang/en/report_editdates.php b/lang/en/report_editdates.php index 98a8498..dc37f2a 100644 --- a/lang/en/report_editdates.php +++ b/lang/en/report_editdates.php @@ -59,3 +59,7 @@ $string['datesoutofrange_title'] = 'Uh oh'; $string['datesoutofrange_body'] = 'There are one or more dates that are either after the end of the course or before the start of the course. You may want to check the dates on this page.'; +$string['movealldates'] = 'Move all other dates'; +$string['movealldates_help'] = 'When you change the course start date, all other enabled dates on this page +(including the course end date) will be changed by the same amount. You will still need to save the changes.'; +