From fde36bb49c372597a51640cc47ec01cde89f7e4d Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Thu, 24 Jul 2025 10:32:31 +0800 Subject: [PATCH] MDL-85787 course: Limit course index drag and drop to certain pages only Only allow the drag and drop functionality in the course index on pages that provide alternatives to move course items such as the course homepage and the course section view pages. This is to meet the WCAG 2.2 Level AA Success Criterion 2.5.7 for dragging movements. See: https://www.w3.org/WAI/WCAG22/Understanding/dragging-movements --- .../amd/build/local/courseeditor/dndsectionitem.min.js | 2 +- .../build/local/courseeditor/dndsectionitem.min.js.map | 2 +- course/format/amd/build/local/courseindex/cm.min.js | 2 +- .../format/amd/build/local/courseindex/cm.min.js.map | 2 +- .../format/amd/build/local/courseindex/section.min.js | 2 +- .../amd/build/local/courseindex/section.min.js.map | 2 +- .../amd/build/local/courseindex/sectiontitle.min.js | 2 +- .../build/local/courseindex/sectiontitle.min.js.map | 2 +- .../amd/src/local/courseeditor/dndsectionitem.js | 5 +++-- course/format/amd/src/local/courseindex/cm.js | 5 ++++- course/format/amd/src/local/courseindex/section.js | 3 ++- .../format/amd/src/local/courseindex/sectiontitle.js | 2 +- course/section.php | 10 ++++++++-- course/view.php | 9 ++++++--- 14 files changed, 32 insertions(+), 18 deletions(-) diff --git a/course/format/amd/build/local/courseeditor/dndsectionitem.min.js b/course/format/amd/build/local/courseeditor/dndsectionitem.min.js index faeb137ff7c..0a6174688c3 100644 --- a/course/format/amd/build/local/courseeditor/dndsectionitem.min.js +++ b/course/format/amd/build/local/courseeditor/dndsectionitem.min.js @@ -10,6 +10,6 @@ define("core_courseformat/local/courseeditor/dndsectionitem",["exports","core/re * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class _default extends _reactive.BaseComponent{configDragDrop(sectionid,state,fullregion){this.id=sectionid,void 0===this.section&&(this.section=state.section.get(this.id)),void 0===this.course&&(this.course=state.course),this.section.number>0&&null===this.section.component&&(this.getDraggableData=this._getDraggableData),this.fullregion=fullregion,this.reactive.isEditing&&this.reactive.supportComponents&&(this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.dragdrop&&this.dragdrop.unregister()}setDraggable(value){var _this$dragdrop;this.getDraggableData&&(null===(_this$dragdrop=this.dragdrop)||void 0===_this$dragdrop||_this$dragdrop.setDraggable(value))}dragStart(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!1)}_getDraggableData(){return this.reactive.getExporter().sectionDraggableData(this.reactive.state,this.id)}validateDropData(dropdata){if("cm"===(null==dropdata?void 0:dropdata.type)){var _this$section,_this$section2;if(null!==(_this$section=this.section)&&void 0!==_this$section&&_this$section.component&&!0===(null==dropdata?void 0:dropdata.hasdelegatedsection))return!1;const firstcmid=null===(_this$section2=this.section)||void 0===_this$section2?void 0:_this$section2.cmlist[0];return dropdata.id!==firstcmid}return!1}showDropZone(){this.element.classList.add(this.classes.DROPZONE)}hideDropZone(){this.element.classList.remove(this.classes.DROPZONE)}drop(dropdata,event){if("cm"==dropdata.type){var _this$section3;const mutation=event.altKey?"cmDuplicate":"cmMove";this.reactive.dispatch(mutation,[dropdata.id],this.id,null===(_this$section3=this.section)||void 0===_this$section3?void 0:_this$section3.cmlist[0])}}}return _exports.default=_default,_exports.default})); +class _default extends _reactive.BaseComponent{configDragDrop(sectionid,state,fullregion){let isDndAllowed=!(arguments.length>3&&void 0!==arguments[3])||arguments[3];this.id=sectionid,void 0===this.section&&(this.section=state.section.get(this.id)),void 0===this.course&&(this.course=state.course),this.section.number>0&&null===this.section.component&&(this.getDraggableData=this._getDraggableData),this.fullregion=fullregion,this.reactive.isEditing&&this.reactive.supportComponents&&isDndAllowed&&(this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.dragdrop&&this.dragdrop.unregister()}setDraggable(value){var _this$dragdrop;this.getDraggableData&&(null===(_this$dragdrop=this.dragdrop)||void 0===_this$dragdrop||_this$dragdrop.setDraggable(value))}dragStart(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!1)}_getDraggableData(){return this.reactive.getExporter().sectionDraggableData(this.reactive.state,this.id)}validateDropData(dropdata){if("cm"===(null==dropdata?void 0:dropdata.type)){var _this$section,_this$section2;if(null!==(_this$section=this.section)&&void 0!==_this$section&&_this$section.component&&!0===(null==dropdata?void 0:dropdata.hasdelegatedsection))return!1;const firstcmid=null===(_this$section2=this.section)||void 0===_this$section2?void 0:_this$section2.cmlist[0];return dropdata.id!==firstcmid}return!1}showDropZone(){this.element.classList.add(this.classes.DROPZONE)}hideDropZone(){this.element.classList.remove(this.classes.DROPZONE)}drop(dropdata,event){if("cm"==dropdata.type){var _this$section3;const mutation=event.altKey?"cmDuplicate":"cmMove";this.reactive.dispatch(mutation,[dropdata.id],this.id,null===(_this$section3=this.section)||void 0===_this$section3?void 0:_this$section3.cmlist[0])}}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=dndsectionitem.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/dndsectionitem.min.js.map b/course/format/amd/build/local/courseeditor/dndsectionitem.min.js.map index c1fbdb07d92..2d11328f9ed 100644 --- a/course/format/amd/build/local/courseeditor/dndsectionitem.min.js.map +++ b/course/format/amd/build/local/courseeditor/dndsectionitem.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dndsectionitem.min.js","sources":["../../../src/local/courseeditor/dndsectionitem.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 * Course index section title draggable component.\n *\n * This component is used to control specific course section interactions like drag and drop\n * in both course index and course content.\n *\n * @module core_courseformat/local/courseeditor/dndsectionitem\n * @class core_courseformat/local/courseeditor/dndsectionitem\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\n\nexport default class extends BaseComponent {\n\n /**\n * Initial state ready method.\n *\n * @param {number} sectionid the section id\n * @param {Object} state the initial state\n * @param {Element} fullregion the complete section region to mark as dragged\n */\n configDragDrop(sectionid, state, fullregion) {\n\n this.id = sectionid;\n if (this.section === undefined) {\n this.section = state.section.get(this.id);\n }\n if (this.course === undefined) {\n this.course = state.course;\n }\n\n // Prevent topic zero and delegated sections from being draggable.\n if (this.section.number > 0 && this.section.component === null) {\n this.getDraggableData = this._getDraggableData;\n }\n\n this.fullregion = fullregion;\n\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init the dropzone.\n this.dragdrop = new DragDrop(this);\n // Save dropzone classes.\n this.classes = this.dragdrop.getClasses();\n }\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n /**\n * Enable or disable the draggable property.\n *\n * @param {bool} value the new draggable value\n */\n setDraggable(value) {\n if (this.getDraggableData) {\n this.dragdrop?.setDraggable(value);\n }\n }\n\n // Drag and drop methods.\n\n /**\n * The element drop start hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragStart(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], true);\n }\n\n /**\n * The element end start hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragEnd(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], false);\n }\n\n /**\n * Get the draggable data of this component.\n *\n * @returns {Object} exported course module drop data\n */\n _getDraggableData() {\n const exporter = this.reactive.getExporter();\n return exporter.sectionDraggableData(this.reactive.state, this.id);\n }\n\n /**\n * Validate if the drop data can be dropped over the component.\n *\n * @param {Object} dropdata the exported drop data.\n * @returns {boolean}\n */\n validateDropData(dropdata) {\n // Course module validation.\n if (dropdata?.type === 'cm') {\n // Prevent content loops with subsections.\n if (this.section?.component && dropdata?.hasdelegatedsection === true) {\n return false;\n }\n // The first section element is already there so we can ignore it.\n const firstcmid = this.section?.cmlist[0];\n return dropdata.id !== firstcmid;\n }\n return false;\n }\n\n /**\n * Display the component dropzone.\n */\n showDropZone() {\n this.element.classList.add(this.classes.DROPZONE);\n }\n\n /**\n * Hide the component dropzone.\n */\n hideDropZone() {\n this.element.classList.remove(this.classes.DROPZONE);\n }\n\n /**\n * Drop event handler.\n *\n * @param {Object} dropdata the accepted drop data\n * @param {Event} event the drop event\n */\n drop(dropdata, event) {\n // Call the move mutation.\n if (dropdata.type == 'cm') {\n const mutation = (event.altKey) ? 'cmDuplicate' : 'cmMove';\n this.reactive.dispatch(mutation, [dropdata.id], this.id, this.section?.cmlist[0]);\n }\n }\n}\n"],"names":["BaseComponent","configDragDrop","sectionid","state","fullregion","id","undefined","this","section","get","course","number","component","getDraggableData","_getDraggableData","reactive","isEditing","supportComponents","dragdrop","DragDrop","classes","getClasses","destroy","unregister","setDraggable","value","dragStart","dropdata","dispatch","dragEnd","getExporter","sectionDraggableData","validateDropData","type","hasdelegatedsection","firstcmid","_this$section2","cmlist","showDropZone","element","classList","add","DROPZONE","hideDropZone","remove","drop","event","mutation","altKey","_this$section3"],"mappings":";;;;;;;;;;;;uBA6B6BA,wBASzBC,eAAeC,UAAWC,MAAOC,iBAExBC,GAAKH,eACWI,IAAjBC,KAAKC,eACAA,QAAUL,MAAMK,QAAQC,IAAIF,KAAKF,UAEtBC,IAAhBC,KAAKG,cACAA,OAASP,MAAMO,QAIpBH,KAAKC,QAAQG,OAAS,GAAgC,OAA3BJ,KAAKC,QAAQI,iBACnCC,iBAAmBN,KAAKO,wBAG5BV,WAAaA,WAGdG,KAAKQ,SAASC,WAAaT,KAAKQ,SAASE,yBAEpCC,SAAW,IAAIC,mBAASZ,WAExBa,QAAUb,KAAKW,SAASG,cAOrCC,eAC0BhB,IAAlBC,KAAKW,eACAA,SAASK,aAStBC,aAAaC,0BACLlB,KAAKM,+CACAK,mDAAUM,aAAaC,QAWpCC,UAAUC,eACDZ,SAASa,SAAS,cAAe,CAACD,SAAStB,KAAK,GAQzDwB,QAAQF,eACCZ,SAASa,SAAS,cAAe,CAACD,SAAStB,KAAK,GAQzDS,2BACqBP,KAAKQ,SAASe,cACfC,qBAAqBxB,KAAKQ,SAASZ,MAAOI,KAAKF,IASnE2B,iBAAiBL,aAEU,QAAnBA,MAAAA,gBAAAA,SAAUM,MAAe,gEAEhBzB,gDAASI,YAA+C,KAAlCe,MAAAA,gBAAAA,SAAUO,4BAC9B,QAGLC,iCAAY5B,KAAKC,yCAAL4B,eAAcC,OAAO,UAChCV,SAAStB,KAAO8B,iBAEpB,EAMXG,oBACSC,QAAQC,UAAUC,IAAIlC,KAAKa,QAAQsB,UAM5CC,oBACSJ,QAAQC,UAAUI,OAAOrC,KAAKa,QAAQsB,UAS/CG,KAAKlB,SAAUmB,UAEU,MAAjBnB,SAASM,KAAc,0BACjBc,SAAYD,MAAME,OAAU,cAAgB,cAC7CjC,SAASa,SAASmB,SAAU,CAACpB,SAAStB,IAAKE,KAAKF,0BAAIE,KAAKC,yCAALyC,eAAcZ,OAAO"} \ No newline at end of file +{"version":3,"file":"dndsectionitem.min.js","sources":["../../../src/local/courseeditor/dndsectionitem.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 * Course index section title draggable component.\n *\n * This component is used to control specific course section interactions like drag and drop\n * in both course index and course content.\n *\n * @module core_courseformat/local/courseeditor/dndsectionitem\n * @class core_courseformat/local/courseeditor/dndsectionitem\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\n\nexport default class extends BaseComponent {\n\n /**\n * Initial state ready method.\n *\n * @param {number} sectionid the section id\n * @param {Object} state the initial state\n * @param {Element} fullregion the complete section region to mark as dragged\n * @param {Boolean} isDndAllowed Whether drag and drop is allowed in this section.\n */\n configDragDrop(sectionid, state, fullregion, isDndAllowed = true) {\n\n this.id = sectionid;\n if (this.section === undefined) {\n this.section = state.section.get(this.id);\n }\n if (this.course === undefined) {\n this.course = state.course;\n }\n\n // Prevent topic zero and delegated sections from being draggable.\n if (this.section.number > 0 && this.section.component === null) {\n this.getDraggableData = this._getDraggableData;\n }\n\n this.fullregion = fullregion;\n\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents && isDndAllowed) {\n // Init the dropzone.\n this.dragdrop = new DragDrop(this);\n // Save dropzone classes.\n this.classes = this.dragdrop.getClasses();\n }\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n /**\n * Enable or disable the draggable property.\n *\n * @param {bool} value the new draggable value\n */\n setDraggable(value) {\n if (this.getDraggableData) {\n this.dragdrop?.setDraggable(value);\n }\n }\n\n // Drag and drop methods.\n\n /**\n * The element drop start hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragStart(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], true);\n }\n\n /**\n * The element end start hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragEnd(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], false);\n }\n\n /**\n * Get the draggable data of this component.\n *\n * @returns {Object} exported course module drop data\n */\n _getDraggableData() {\n const exporter = this.reactive.getExporter();\n return exporter.sectionDraggableData(this.reactive.state, this.id);\n }\n\n /**\n * Validate if the drop data can be dropped over the component.\n *\n * @param {Object} dropdata the exported drop data.\n * @returns {boolean}\n */\n validateDropData(dropdata) {\n // Course module validation.\n if (dropdata?.type === 'cm') {\n // Prevent content loops with subsections.\n if (this.section?.component && dropdata?.hasdelegatedsection === true) {\n return false;\n }\n // The first section element is already there so we can ignore it.\n const firstcmid = this.section?.cmlist[0];\n return dropdata.id !== firstcmid;\n }\n return false;\n }\n\n /**\n * Display the component dropzone.\n */\n showDropZone() {\n this.element.classList.add(this.classes.DROPZONE);\n }\n\n /**\n * Hide the component dropzone.\n */\n hideDropZone() {\n this.element.classList.remove(this.classes.DROPZONE);\n }\n\n /**\n * Drop event handler.\n *\n * @param {Object} dropdata the accepted drop data\n * @param {Event} event the drop event\n */\n drop(dropdata, event) {\n // Call the move mutation.\n if (dropdata.type == 'cm') {\n const mutation = (event.altKey) ? 'cmDuplicate' : 'cmMove';\n this.reactive.dispatch(mutation, [dropdata.id], this.id, this.section?.cmlist[0]);\n }\n }\n}\n"],"names":["BaseComponent","configDragDrop","sectionid","state","fullregion","isDndAllowed","id","undefined","this","section","get","course","number","component","getDraggableData","_getDraggableData","reactive","isEditing","supportComponents","dragdrop","DragDrop","classes","getClasses","destroy","unregister","setDraggable","value","dragStart","dropdata","dispatch","dragEnd","getExporter","sectionDraggableData","validateDropData","type","hasdelegatedsection","firstcmid","_this$section2","cmlist","showDropZone","element","classList","add","DROPZONE","hideDropZone","remove","drop","event","mutation","altKey","_this$section3"],"mappings":";;;;;;;;;;;;uBA6B6BA,wBAUzBC,eAAeC,UAAWC,MAAOC,gBAAYC,6EAEpCC,GAAKJ,eACWK,IAAjBC,KAAKC,eACAA,QAAUN,MAAMM,QAAQC,IAAIF,KAAKF,UAEtBC,IAAhBC,KAAKG,cACAA,OAASR,MAAMQ,QAIpBH,KAAKC,QAAQG,OAAS,GAAgC,OAA3BJ,KAAKC,QAAQI,iBACnCC,iBAAmBN,KAAKO,wBAG5BX,WAAaA,WAGdI,KAAKQ,SAASC,WAAaT,KAAKQ,SAASE,mBAAqBb,oBAEzDc,SAAW,IAAIC,mBAASZ,WAExBa,QAAUb,KAAKW,SAASG,cAOrCC,eAC0BhB,IAAlBC,KAAKW,eACAA,SAASK,aAStBC,aAAaC,0BACLlB,KAAKM,+CACAK,mDAAUM,aAAaC,QAWpCC,UAAUC,eACDZ,SAASa,SAAS,cAAe,CAACD,SAAStB,KAAK,GAQzDwB,QAAQF,eACCZ,SAASa,SAAS,cAAe,CAACD,SAAStB,KAAK,GAQzDS,2BACqBP,KAAKQ,SAASe,cACfC,qBAAqBxB,KAAKQ,SAASb,MAAOK,KAAKF,IASnE2B,iBAAiBL,aAEU,QAAnBA,MAAAA,gBAAAA,SAAUM,MAAe,gEAEhBzB,gDAASI,YAA+C,KAAlCe,MAAAA,gBAAAA,SAAUO,4BAC9B,QAGLC,iCAAY5B,KAAKC,yCAAL4B,eAAcC,OAAO,UAChCV,SAAStB,KAAO8B,iBAEpB,EAMXG,oBACSC,QAAQC,UAAUC,IAAIlC,KAAKa,QAAQsB,UAM5CC,oBACSJ,QAAQC,UAAUI,OAAOrC,KAAKa,QAAQsB,UAS/CG,KAAKlB,SAAUmB,UAEU,MAAjBnB,SAASM,KAAc,0BACjBc,SAAYD,MAAME,OAAU,cAAgB,cAC7CjC,SAASa,SAASmB,SAAU,CAACpB,SAAStB,IAAKE,KAAKF,0BAAIE,KAAKC,yCAALyC,eAAcZ,OAAO"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseindex/cm.min.js b/course/format/amd/build/local/courseindex/cm.min.js index 6e5647a1d55..dfbfb9db895 100644 --- a/course/format/amd/build/local/courseindex/cm.min.js +++ b/course/format/amd/build/local/courseindex/cm.min.js @@ -8,6 +8,6 @@ define("core_courseformat/local/courseindex/cm",["exports","core_courseformat/lo * @class core_courseformat/local/courseindex/cm * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_dndcmitem=_interopRequireDefault(_dndcmitem),_templates=_interopRequireDefault(_templates),_prefetch=_interopRequireDefault(_prefetch),_config=_interopRequireDefault(_config),_pending=_interopRequireDefault(_pending);_prefetch.default.prefetchTemplate("core_courseformat/local/courseindex/cmcompletion");class Component extends _dndcmitem.default{create(){this.name="courseindex_cm",this.selectors={CM_NAME:"[data-for='cm_name']",CM_COMPLETION:"[data-for='cm_completion']"},this.classes={CMHIDDEN:"dimmed",LOCKED:"editinprogress",RESTRICTIONS:"restrictions",PAGEITEM:"pageitem",INDENTED:"indented"},this.id=this.element.dataset.id}static init(target,selectors){return new this({element:document.getElementById(target),selectors:selectors})}stateReady(state){this.configDragDrop(this.id);const cm=state.cm.get(this.id),course=state.course;this._refreshCompletion({state:state,element:cm});const anchor=new URL(window.location.href).hash.replace("#","");if((window.location.href==cm.url||window.location.href.includes(course.baseurl)&&anchor==cm.anchor)&&this.element.scrollIntoView({block:"center"}),_config.default.contextid!=_config.default.courseContextId&&_config.default.contextInstanceId==this.id&&(this.reactive.dispatch("setPageItem","cm",this.id,!0),this.element.scrollIntoView({block:"center"})),!cm.uservisible||!cm.url){const element=this.getElement(this.selectors.CM_NAME);this.addEventListener(element,"click",this._activityAnchor),document.getElementById(cm.anchor)||element.setAttribute("href",this._getActivitySectionURL(cm))}}getWatchers(){return[{watch:"cm[".concat(this.id,"]:deleted"),handler:this.remove},{watch:"cm[".concat(this.id,"]:updated"),handler:this._refreshCm},{watch:"cm[".concat(this.id,"].completionstate:updated"),handler:this._refreshCompletion},{watch:"course.pageItem:updated",handler:this._refreshPageItem}]}_refreshCm(_ref){var _element$dragging,_element$locked,_element$hascmrestric;let{element:element}=_ref;this.element.classList.toggle(this.classes.CMHIDDEN,!element.visible),this.getElement(this.selectors.CM_NAME).innerHTML=element.name,this.element.classList.toggle(this.classes.DRAGGING,null!==(_element$dragging=element.dragging)&&void 0!==_element$dragging&&_element$dragging),this.element.classList.toggle(this.classes.LOCKED,null!==(_element$locked=element.locked)&&void 0!==_element$locked&&_element$locked),this.element.classList.toggle(this.classes.RESTRICTIONS,null!==(_element$hascmrestric=element.hascmrestrictions)&&void 0!==_element$hascmrestric&&_element$hascmrestric),this.element.classList.toggle(this.classes.INDENTED,element.indent),this.locked=element.locked}_refreshPageItem(_ref2){let{element:element}=_ref2;if(!element.pageItem)return;const isPageId="cm"==element.pageItem.type&&element.pageItem.id==this.id;this.element.classList.toggle(this.classes.PAGEITEM,isPageId),isPageId&&!this.reactive.isEditing&&this.element.scrollIntoView({block:"nearest"})}async _refreshCompletion(_ref3){let{state:state,element:element}=_ref3;if(this.reactive.isEditing||!element.istrackeduser)return;const completionElement=this.getElement(this.selectors.CM_COMPLETION);if(!completionElement||completionElement.dataset.value==element.completionstate)return;const data=this.reactive.getExporter().cmCompletion(state,element),{html:html,js:js}=await _templates.default.renderForPromise("core_courseformat/local/courseindex/cmcompletion",data);_templates.default.replaceNode(completionElement,html,js)}_activityAnchor(event){const cm=this.reactive.get("cm",this.id);if(document.getElementById(cm.anchor)){this.reactive.dispatch("sectionContentCollapsed",[cm.sectionid],!1);const pendingAnchor=new _pending.default("courseformat/activity:openAnchor");setTimeout((()=>{this.reactive.dispatch("setPageItem","cm",cm.id),pendingAnchor.resolve()}),50)}else event.preventDefault(),window.location=this._getActivitySectionURL(cm)}_getActivitySectionURL(cm){let section=this.reactive.get("section",cm.sectionid);if(section.component&§ion.parentsectionid&&(section=this.reactive.get("section",section.parentsectionid)),!section)return"#";const sectionurl=section.sectionurl.split("#")[0];return"".concat(sectionurl,"#").concat(cm.anchor)}}return _exports.default=Component,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_dndcmitem=_interopRequireDefault(_dndcmitem),_templates=_interopRequireDefault(_templates),_prefetch=_interopRequireDefault(_prefetch),_config=_interopRequireDefault(_config),_pending=_interopRequireDefault(_pending);_prefetch.default.prefetchTemplate("core_courseformat/local/courseindex/cmcompletion");class Component extends _dndcmitem.default{create(){this.name="courseindex_cm",this.selectors={CM_NAME:"[data-for='cm_name']",CM_COMPLETION:"[data-for='cm_completion']",DND_ALLOWED:"[data-courseindexdndallowed='true']"},this.classes={CMHIDDEN:"dimmed",LOCKED:"editinprogress",RESTRICTIONS:"restrictions",PAGEITEM:"pageitem",INDENTED:"indented"},this.id=this.element.dataset.id}static init(target,selectors){return new this({element:document.getElementById(target),selectors:selectors})}stateReady(state){document.querySelector(this.selectors.DND_ALLOWED)&&this.configDragDrop(this.id);const cm=state.cm.get(this.id),course=state.course;this._refreshCompletion({state:state,element:cm});const anchor=new URL(window.location.href).hash.replace("#","");if((window.location.href==cm.url||window.location.href.includes(course.baseurl)&&anchor==cm.anchor)&&this.element.scrollIntoView({block:"center"}),_config.default.contextid!=_config.default.courseContextId&&_config.default.contextInstanceId==this.id&&(this.reactive.dispatch("setPageItem","cm",this.id,!0),this.element.scrollIntoView({block:"center"})),!cm.uservisible||!cm.url){const element=this.getElement(this.selectors.CM_NAME);this.addEventListener(element,"click",this._activityAnchor),document.getElementById(cm.anchor)||element.setAttribute("href",this._getActivitySectionURL(cm))}}getWatchers(){return[{watch:"cm[".concat(this.id,"]:deleted"),handler:this.remove},{watch:"cm[".concat(this.id,"]:updated"),handler:this._refreshCm},{watch:"cm[".concat(this.id,"].completionstate:updated"),handler:this._refreshCompletion},{watch:"course.pageItem:updated",handler:this._refreshPageItem}]}_refreshCm(_ref){var _element$dragging,_element$locked,_element$hascmrestric;let{element:element}=_ref;this.element.classList.toggle(this.classes.CMHIDDEN,!element.visible),this.getElement(this.selectors.CM_NAME).innerHTML=element.name,this.element.classList.toggle(this.classes.DRAGGING,null!==(_element$dragging=element.dragging)&&void 0!==_element$dragging&&_element$dragging),this.element.classList.toggle(this.classes.LOCKED,null!==(_element$locked=element.locked)&&void 0!==_element$locked&&_element$locked),this.element.classList.toggle(this.classes.RESTRICTIONS,null!==(_element$hascmrestric=element.hascmrestrictions)&&void 0!==_element$hascmrestric&&_element$hascmrestric),this.element.classList.toggle(this.classes.INDENTED,element.indent),this.locked=element.locked}_refreshPageItem(_ref2){let{element:element}=_ref2;if(!element.pageItem)return;const isPageId="cm"==element.pageItem.type&&element.pageItem.id==this.id;this.element.classList.toggle(this.classes.PAGEITEM,isPageId),isPageId&&!this.reactive.isEditing&&this.element.scrollIntoView({block:"nearest"})}async _refreshCompletion(_ref3){let{state:state,element:element}=_ref3;if(this.reactive.isEditing||!element.istrackeduser)return;const completionElement=this.getElement(this.selectors.CM_COMPLETION);if(!completionElement||completionElement.dataset.value==element.completionstate)return;const data=this.reactive.getExporter().cmCompletion(state,element),{html:html,js:js}=await _templates.default.renderForPromise("core_courseformat/local/courseindex/cmcompletion",data);_templates.default.replaceNode(completionElement,html,js)}_activityAnchor(event){const cm=this.reactive.get("cm",this.id);if(document.getElementById(cm.anchor)){this.reactive.dispatch("sectionContentCollapsed",[cm.sectionid],!1);const pendingAnchor=new _pending.default("courseformat/activity:openAnchor");setTimeout((()=>{this.reactive.dispatch("setPageItem","cm",cm.id),pendingAnchor.resolve()}),50)}else event.preventDefault(),window.location=this._getActivitySectionURL(cm)}_getActivitySectionURL(cm){let section=this.reactive.get("section",cm.sectionid);if(section.component&§ion.parentsectionid&&(section=this.reactive.get("section",section.parentsectionid)),!section)return"#";const sectionurl=section.sectionurl.split("#")[0];return"".concat(sectionurl,"#").concat(cm.anchor)}}return _exports.default=Component,_exports.default})); //# sourceMappingURL=cm.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseindex/cm.min.js.map b/course/format/amd/build/local/courseindex/cm.min.js.map index b6f25cbe43e..3308573d901 100644 --- a/course/format/amd/build/local/courseindex/cm.min.js.map +++ b/course/format/amd/build/local/courseindex/cm.min.js.map @@ -1 +1 @@ -{"version":3,"file":"cm.min.js","sources":["../../../src/local/courseindex/cm.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 * Course index cm component.\n *\n * This component is used to control specific course modules interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/cm\n * @class core_courseformat/local/courseindex/cm\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DndCmItem from 'core_courseformat/local/courseeditor/dndcmitem';\nimport Templates from 'core/templates';\nimport Prefetch from 'core/prefetch';\nimport Config from 'core/config';\nimport Pending from \"core/pending\";\n\n// Prefetch the completion icons template.\nconst completionTemplate = 'core_courseformat/local/courseindex/cmcompletion';\nPrefetch.prefetchTemplate(completionTemplate);\n\nexport default class Component extends DndCmItem {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'courseindex_cm';\n // Default query selectors.\n this.selectors = {\n CM_NAME: `[data-for='cm_name']`,\n CM_COMPLETION: `[data-for='cm_completion']`,\n };\n // Default classes to toggle on refresh.\n this.classes = {\n CMHIDDEN: 'dimmed',\n LOCKED: 'editinprogress',\n RESTRICTIONS: 'restrictions',\n PAGEITEM: 'pageitem',\n INDENTED: 'indented',\n };\n // We need our id to watch specific events.\n this.id = this.element.dataset.id;\n }\n\n /**\n * Static method to create a component instance form the mustache template.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new this({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the course state.\n */\n stateReady(state) {\n this.configDragDrop(this.id);\n const cm = state.cm.get(this.id);\n const course = state.course;\n // Refresh completion icon.\n this._refreshCompletion({\n state,\n element: cm,\n });\n const url = new URL(window.location.href);\n const anchor = url.hash.replace('#', '');\n // Check if the current url is the cm url.\n if (window.location.href == cm.url\n || (window.location.href.includes(course.baseurl) && anchor == cm.anchor)\n ) {\n this.element.scrollIntoView({block: \"center\"});\n }\n // Check if this we are displaying this activity page.\n if (Config.contextid != Config.courseContextId && Config.contextInstanceId == this.id) {\n this.reactive.dispatch('setPageItem', 'cm', this.id, true);\n this.element.scrollIntoView({block: \"center\"});\n }\n // Add anchor logic if the element is not user visible or the element hasn't URL.\n if (!cm.uservisible || !cm.url) {\n const element = this.getElement(this.selectors.CM_NAME);\n this.addEventListener(\n element,\n 'click',\n this._activityAnchor,\n );\n // If the element is not user visible we also need to update the anchor link including the section page.\n if (!document.getElementById(cm.anchor)) {\n element.setAttribute('href', this._getActivitySectionURL(cm));\n }\n }\n }\n\n /**\n * Component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n {watch: `cm[${this.id}]:deleted`, handler: this.remove},\n {watch: `cm[${this.id}]:updated`, handler: this._refreshCm},\n {watch: `cm[${this.id}].completionstate:updated`, handler: this._refreshCompletion},\n {watch: `course.pageItem:updated`, handler: this._refreshPageItem},\n ];\n }\n\n /**\n * Update a course index cm using the state information.\n *\n * @param {object} param\n * @param {Object} param.element details the update details.\n */\n _refreshCm({element}) {\n // Update classes.\n this.element.classList.toggle(this.classes.CMHIDDEN, !element.visible);\n this.getElement(this.selectors.CM_NAME).innerHTML = element.name;\n this.element.classList.toggle(this.classes.DRAGGING, element.dragging ?? false);\n this.element.classList.toggle(this.classes.LOCKED, element.locked ?? false);\n this.element.classList.toggle(this.classes.RESTRICTIONS, element.hascmrestrictions ?? false);\n this.element.classList.toggle(this.classes.INDENTED, element.indent);\n this.locked = element.locked;\n }\n\n /**\n * Handle a page item update.\n *\n * @param {Object} details the update details\n * @param {Object} details.element the course state data.\n */\n _refreshPageItem({element}) {\n if (!element.pageItem) {\n return;\n }\n const isPageId = (element.pageItem.type == 'cm' && element.pageItem.id == this.id);\n this.element.classList.toggle(this.classes.PAGEITEM, isPageId);\n if (isPageId && !this.reactive.isEditing) {\n this.element.scrollIntoView({block: \"nearest\"});\n }\n }\n\n /**\n * Update the activity completion icon.\n *\n * @param {Object} details the update details\n * @param {Object} details.state the state data\n * @param {Object} details.element the element data\n */\n async _refreshCompletion({state, element}) {\n // No completion icons are displayed in edit mode.\n if (this.reactive.isEditing || !element.istrackeduser) {\n return;\n }\n // Check if the completion value has changed.\n const completionElement = this.getElement(this.selectors.CM_COMPLETION);\n if (!completionElement || completionElement.dataset.value == element.completionstate) {\n return;\n }\n\n // Collect section information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.cmCompletion(state, element);\n\n const {html, js} = await Templates.renderForPromise(completionTemplate, data);\n Templates.replaceNode(completionElement, html, js);\n }\n\n /**\n * The activity anchor event.\n *\n * @param {Event} event\n */\n _activityAnchor(event) {\n const cm = this.reactive.get('cm', this.id);\n // If the user cannot access the element but the element is present in the page\n // the new url should be an anchor link.\n const element = document.getElementById(cm.anchor);\n if (element) {\n // Make sure the section is expanded.\n this.reactive.dispatch('sectionContentCollapsed', [cm.sectionid], false);\n // Marc the element as page item once the event is handled.\n const pendingAnchor = new Pending(`courseformat/activity:openAnchor`);\n setTimeout(() => {\n this.reactive.dispatch('setPageItem', 'cm', cm.id);\n pendingAnchor.resolve();\n }, 50);\n return;\n }\n // If the element is not present in the page we need to go to the specific section.\n event.preventDefault();\n window.location = this._getActivitySectionURL(cm);\n }\n\n /**\n * Get the anchor link in section page for the cm.\n *\n * @param {Object} cm the course module data.\n * @return {String} the anchor link.\n */\n _getActivitySectionURL(cm) {\n let section = this.reactive.get('section', cm.sectionid);\n\n // If the section is delegated get its parent section if it has one.\n if (section.component && section.parentsectionid) {\n section = this.reactive.get('section', section.parentsectionid);\n }\n\n if (!section) {\n return '#';\n }\n\n const sectionurl = section.sectionurl.split(\"#\")[0];\n return `${sectionurl}#${cm.anchor}`;\n }\n}\n"],"names":["prefetchTemplate","Component","DndCmItem","create","name","selectors","CM_NAME","CM_COMPLETION","classes","CMHIDDEN","LOCKED","RESTRICTIONS","PAGEITEM","INDENTED","id","this","element","dataset","target","document","getElementById","stateReady","state","configDragDrop","cm","get","course","_refreshCompletion","anchor","URL","window","location","href","hash","replace","url","includes","baseurl","scrollIntoView","block","Config","contextid","courseContextId","contextInstanceId","reactive","dispatch","uservisible","getElement","addEventListener","_activityAnchor","setAttribute","_getActivitySectionURL","getWatchers","watch","handler","remove","_refreshCm","_refreshPageItem","classList","toggle","visible","innerHTML","DRAGGING","dragging","locked","hascmrestrictions","indent","pageItem","isPageId","type","isEditing","istrackeduser","completionElement","value","completionstate","data","getExporter","cmCompletion","html","js","Templates","renderForPromise","replaceNode","event","sectionid","pendingAnchor","Pending","setTimeout","resolve","preventDefault","section","component","parentsectionid","sectionurl","split"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"cm.min.js","sources":["../../../src/local/courseindex/cm.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 * Course index cm component.\n *\n * This component is used to control specific course modules interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/cm\n * @class core_courseformat/local/courseindex/cm\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DndCmItem from 'core_courseformat/local/courseeditor/dndcmitem';\nimport Templates from 'core/templates';\nimport Prefetch from 'core/prefetch';\nimport Config from 'core/config';\nimport Pending from \"core/pending\";\n\n// Prefetch the completion icons template.\nconst completionTemplate = 'core_courseformat/local/courseindex/cmcompletion';\nPrefetch.prefetchTemplate(completionTemplate);\n\nexport default class Component extends DndCmItem {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'courseindex_cm';\n // Default query selectors.\n this.selectors = {\n CM_NAME: `[data-for='cm_name']`,\n CM_COMPLETION: `[data-for='cm_completion']`,\n DND_ALLOWED: `[data-courseindexdndallowed='true']`,\n };\n // Default classes to toggle on refresh.\n this.classes = {\n CMHIDDEN: 'dimmed',\n LOCKED: 'editinprogress',\n RESTRICTIONS: 'restrictions',\n PAGEITEM: 'pageitem',\n INDENTED: 'indented',\n };\n // We need our id to watch specific events.\n this.id = this.element.dataset.id;\n }\n\n /**\n * Static method to create a component instance form the mustache template.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new this({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the course state.\n */\n stateReady(state) {\n if (document.querySelector(this.selectors.DND_ALLOWED)) {\n this.configDragDrop(this.id);\n }\n const cm = state.cm.get(this.id);\n const course = state.course;\n // Refresh completion icon.\n this._refreshCompletion({\n state,\n element: cm,\n });\n const url = new URL(window.location.href);\n const anchor = url.hash.replace('#', '');\n // Check if the current url is the cm url.\n if (window.location.href == cm.url\n || (window.location.href.includes(course.baseurl) && anchor == cm.anchor)\n ) {\n this.element.scrollIntoView({block: \"center\"});\n }\n // Check if this we are displaying this activity page.\n if (Config.contextid != Config.courseContextId && Config.contextInstanceId == this.id) {\n this.reactive.dispatch('setPageItem', 'cm', this.id, true);\n this.element.scrollIntoView({block: \"center\"});\n }\n // Add anchor logic if the element is not user visible or the element hasn't URL.\n if (!cm.uservisible || !cm.url) {\n const element = this.getElement(this.selectors.CM_NAME);\n this.addEventListener(\n element,\n 'click',\n this._activityAnchor,\n );\n // If the element is not user visible we also need to update the anchor link including the section page.\n if (!document.getElementById(cm.anchor)) {\n element.setAttribute('href', this._getActivitySectionURL(cm));\n }\n }\n }\n\n /**\n * Component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n {watch: `cm[${this.id}]:deleted`, handler: this.remove},\n {watch: `cm[${this.id}]:updated`, handler: this._refreshCm},\n {watch: `cm[${this.id}].completionstate:updated`, handler: this._refreshCompletion},\n {watch: `course.pageItem:updated`, handler: this._refreshPageItem},\n ];\n }\n\n /**\n * Update a course index cm using the state information.\n *\n * @param {object} param\n * @param {Object} param.element details the update details.\n */\n _refreshCm({element}) {\n // Update classes.\n this.element.classList.toggle(this.classes.CMHIDDEN, !element.visible);\n this.getElement(this.selectors.CM_NAME).innerHTML = element.name;\n this.element.classList.toggle(this.classes.DRAGGING, element.dragging ?? false);\n this.element.classList.toggle(this.classes.LOCKED, element.locked ?? false);\n this.element.classList.toggle(this.classes.RESTRICTIONS, element.hascmrestrictions ?? false);\n this.element.classList.toggle(this.classes.INDENTED, element.indent);\n this.locked = element.locked;\n }\n\n /**\n * Handle a page item update.\n *\n * @param {Object} details the update details\n * @param {Object} details.element the course state data.\n */\n _refreshPageItem({element}) {\n if (!element.pageItem) {\n return;\n }\n const isPageId = (element.pageItem.type == 'cm' && element.pageItem.id == this.id);\n this.element.classList.toggle(this.classes.PAGEITEM, isPageId);\n if (isPageId && !this.reactive.isEditing) {\n this.element.scrollIntoView({block: \"nearest\"});\n }\n }\n\n /**\n * Update the activity completion icon.\n *\n * @param {Object} details the update details\n * @param {Object} details.state the state data\n * @param {Object} details.element the element data\n */\n async _refreshCompletion({state, element}) {\n // No completion icons are displayed in edit mode.\n if (this.reactive.isEditing || !element.istrackeduser) {\n return;\n }\n // Check if the completion value has changed.\n const completionElement = this.getElement(this.selectors.CM_COMPLETION);\n if (!completionElement || completionElement.dataset.value == element.completionstate) {\n return;\n }\n\n // Collect section information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.cmCompletion(state, element);\n\n const {html, js} = await Templates.renderForPromise(completionTemplate, data);\n Templates.replaceNode(completionElement, html, js);\n }\n\n /**\n * The activity anchor event.\n *\n * @param {Event} event\n */\n _activityAnchor(event) {\n const cm = this.reactive.get('cm', this.id);\n // If the user cannot access the element but the element is present in the page\n // the new url should be an anchor link.\n const element = document.getElementById(cm.anchor);\n if (element) {\n // Make sure the section is expanded.\n this.reactive.dispatch('sectionContentCollapsed', [cm.sectionid], false);\n // Marc the element as page item once the event is handled.\n const pendingAnchor = new Pending(`courseformat/activity:openAnchor`);\n setTimeout(() => {\n this.reactive.dispatch('setPageItem', 'cm', cm.id);\n pendingAnchor.resolve();\n }, 50);\n return;\n }\n // If the element is not present in the page we need to go to the specific section.\n event.preventDefault();\n window.location = this._getActivitySectionURL(cm);\n }\n\n /**\n * Get the anchor link in section page for the cm.\n *\n * @param {Object} cm the course module data.\n * @return {String} the anchor link.\n */\n _getActivitySectionURL(cm) {\n let section = this.reactive.get('section', cm.sectionid);\n\n // If the section is delegated get its parent section if it has one.\n if (section.component && section.parentsectionid) {\n section = this.reactive.get('section', section.parentsectionid);\n }\n\n if (!section) {\n return '#';\n }\n\n const sectionurl = section.sectionurl.split(\"#\")[0];\n return `${sectionurl}#${cm.anchor}`;\n }\n}\n"],"names":["prefetchTemplate","Component","DndCmItem","create","name","selectors","CM_NAME","CM_COMPLETION","DND_ALLOWED","classes","CMHIDDEN","LOCKED","RESTRICTIONS","PAGEITEM","INDENTED","id","this","element","dataset","target","document","getElementById","stateReady","state","querySelector","configDragDrop","cm","get","course","_refreshCompletion","anchor","URL","window","location","href","hash","replace","url","includes","baseurl","scrollIntoView","block","Config","contextid","courseContextId","contextInstanceId","reactive","dispatch","uservisible","getElement","addEventListener","_activityAnchor","setAttribute","_getActivitySectionURL","getWatchers","watch","handler","remove","_refreshCm","_refreshPageItem","classList","toggle","visible","innerHTML","DRAGGING","dragging","locked","hascmrestrictions","indent","pageItem","isPageId","type","isEditing","istrackeduser","completionElement","value","completionstate","data","getExporter","cmCompletion","html","js","Templates","renderForPromise","replaceNode","event","sectionid","pendingAnchor","Pending","setTimeout","resolve","preventDefault","section","component","parentsectionid","sectionurl","split"],"mappings":"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"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseindex/section.min.js b/course/format/amd/build/local/courseindex/section.min.js index a9536599f4e..1469edc20da 100644 --- a/course/format/amd/build/local/courseindex/section.min.js +++ b/course/format/amd/build/local/courseindex/section.min.js @@ -8,6 +8,6 @@ define("core_courseformat/local/courseindex/section",["exports","core_courseform * @class core_courseformat/local/courseindex/section * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_sectiontitle=_interopRequireDefault(_sectiontitle),_dndsection=_interopRequireDefault(_dndsection);class Component extends _dndsection.default{create(){this.name="courseindex_section",this.selectors={SECTION:"[data-for='section']",SECTION_ITEM:"[data-for='section_item']",SECTION_TITLE:"[data-for='section_title']",CM_LAST:'[data-for="cm"]:last-child'},this.classes={SECTIONHIDDEN:"dimmed",SECTIONCURRENT:"current",LOCKED:"editinprogress",RESTRICTIONS:"restrictions",PAGEITEM:"pageitem",OVERLAYBORDERS:"overlay-preview-borders"},this.id=this.element.dataset.id,this.isPageItem=!1}static init(target,selectors){return new this({element:document.getElementById(target),selectors:selectors})}stateReady(state){this.configState(state);const sectionItem=this.getElement(this.selectors.SECTION_ITEM);if(this.reactive.isEditing&&this.reactive.supportComponents){const titleitem=new _sectiontitle.default({...this,element:sectionItem,fullregion:this.element});this.configDragDrop(titleitem)}const section=state.section.get(this.id);window.location.href==section.sectionurl.replace(/&/g,"&")&&(this.reactive.dispatch("setPageItem","section",this.id),sectionItem.scrollIntoView())}getWatchers(){return[{watch:"section[".concat(this.id,"]:deleted"),handler:this.remove},{watch:"section[".concat(this.id,"]:updated"),handler:this._refreshSection},{watch:"course.pageItem:updated",handler:this._refreshPageItem}]}getLastCm(){return this.getElement(this.selectors.CM_LAST)}_refreshSection(_ref){var _element$hasrestricti,_element$dragging,_element$locked;let{element:element}=_ref;const sectionItem=this.getElement(this.selectors.SECTION_ITEM);sectionItem.classList.toggle(this.classes.SECTIONHIDDEN,!element.visible),sectionItem.classList.toggle(this.classes.RESTRICTIONS,null!==(_element$hasrestricti=element.hasrestrictions)&&void 0!==_element$hasrestricti&&_element$hasrestricti),this.element.classList.toggle(this.classes.SECTIONCURRENT,element.current),this.element.classList.toggle(this.classes.DRAGGING,null!==(_element$dragging=element.dragging)&&void 0!==_element$dragging&&_element$dragging),this.element.classList.toggle(this.classes.LOCKED,null!==(_element$locked=element.locked)&&void 0!==_element$locked&&_element$locked),this.locked=element.locked,this.getElement(this.selectors.SECTION_TITLE).innerHTML=element.title}_refreshPageItem(_ref2){var _element$pageItem,_this$pageItem;let{element:element,state:state}=_ref2;if(!element.pageItem)return;const containsPageItem=this._isPageItemInThisSection(element.pageItem);if(!containsPageItem||this._isParentSectionIndexCollapsed(state))return this.pageItem=!1,void this.getElement(this.selectors.SECTION_ITEM).classList.remove(this.classes.PAGEITEM);!state.section.get(this.id).indexcollapsed||null!==(_element$pageItem=element.pageItem)&&void 0!==_element$pageItem&&_element$pageItem.isStatic?this.pageItem="section"==element.pageItem.type&&element.pageItem.id==this.id:this.pageItem=containsPageItem;this.getElement(this.selectors.SECTION_ITEM).classList.toggle(this.classes.PAGEITEM,null!==(_this$pageItem=this.pageItem)&&void 0!==_this$pageItem&&_this$pageItem),this.pageItem&&!this.reactive.isEditing&&this.element.scrollIntoView({block:"nearest"})}_isPageItemInThisSection(pageItem){if(pageItem.sectionId==this.id)return!0;return!!this.element.querySelector("".concat(this.selectors.SECTION,'[data-id="').concat(pageItem.sectionId,'"]'))}_isParentSectionIndexCollapsed(state){const parentElement=this.element.parentElement.closest(this.selectors.SECTION);if(!parentElement||!parentElement.dataset.id)return null;return!!state.section.get(parentElement.dataset.id).indexcollapsed}async addOverlay(){this.element.classList.add(this.classes.OVERLAYBORDERS)}removeOverlay(){this.element.classList.remove(this.classes.OVERLAYBORDERS)}}return _exports.default=Component,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_sectiontitle=_interopRequireDefault(_sectiontitle),_dndsection=_interopRequireDefault(_dndsection);class Component extends _dndsection.default{create(){this.name="courseindex_section",this.selectors={SECTION:"[data-for='section']",SECTION_ITEM:"[data-for='section_item']",SECTION_TITLE:"[data-for='section_title']",CM_LAST:'[data-for="cm"]:last-child',DND_ALLOWED:"[data-courseindexdndallowed='true']"},this.classes={SECTIONHIDDEN:"dimmed",SECTIONCURRENT:"current",LOCKED:"editinprogress",RESTRICTIONS:"restrictions",PAGEITEM:"pageitem",OVERLAYBORDERS:"overlay-preview-borders"},this.id=this.element.dataset.id,this.isPageItem=!1}static init(target,selectors){return new this({element:document.getElementById(target),selectors:selectors})}stateReady(state){this.configState(state);const sectionItem=this.getElement(this.selectors.SECTION_ITEM);if(this.reactive.isEditing&&this.reactive.supportComponents&&document.querySelector(this.selectors.DND_ALLOWED)){const titleitem=new _sectiontitle.default({...this,element:sectionItem,fullregion:this.element});this.configDragDrop(titleitem)}const section=state.section.get(this.id);window.location.href==section.sectionurl.replace(/&/g,"&")&&(this.reactive.dispatch("setPageItem","section",this.id),sectionItem.scrollIntoView())}getWatchers(){return[{watch:"section[".concat(this.id,"]:deleted"),handler:this.remove},{watch:"section[".concat(this.id,"]:updated"),handler:this._refreshSection},{watch:"course.pageItem:updated",handler:this._refreshPageItem}]}getLastCm(){return this.getElement(this.selectors.CM_LAST)}_refreshSection(_ref){var _element$hasrestricti,_element$dragging,_element$locked;let{element:element}=_ref;const sectionItem=this.getElement(this.selectors.SECTION_ITEM);sectionItem.classList.toggle(this.classes.SECTIONHIDDEN,!element.visible),sectionItem.classList.toggle(this.classes.RESTRICTIONS,null!==(_element$hasrestricti=element.hasrestrictions)&&void 0!==_element$hasrestricti&&_element$hasrestricti),this.element.classList.toggle(this.classes.SECTIONCURRENT,element.current),this.element.classList.toggle(this.classes.DRAGGING,null!==(_element$dragging=element.dragging)&&void 0!==_element$dragging&&_element$dragging),this.element.classList.toggle(this.classes.LOCKED,null!==(_element$locked=element.locked)&&void 0!==_element$locked&&_element$locked),this.locked=element.locked,this.getElement(this.selectors.SECTION_TITLE).innerHTML=element.title}_refreshPageItem(_ref2){var _element$pageItem,_this$pageItem;let{element:element,state:state}=_ref2;if(!element.pageItem)return;const containsPageItem=this._isPageItemInThisSection(element.pageItem);if(!containsPageItem||this._isParentSectionIndexCollapsed(state))return this.pageItem=!1,void this.getElement(this.selectors.SECTION_ITEM).classList.remove(this.classes.PAGEITEM);!state.section.get(this.id).indexcollapsed||null!==(_element$pageItem=element.pageItem)&&void 0!==_element$pageItem&&_element$pageItem.isStatic?this.pageItem="section"==element.pageItem.type&&element.pageItem.id==this.id:this.pageItem=containsPageItem;this.getElement(this.selectors.SECTION_ITEM).classList.toggle(this.classes.PAGEITEM,null!==(_this$pageItem=this.pageItem)&&void 0!==_this$pageItem&&_this$pageItem),this.pageItem&&!this.reactive.isEditing&&this.element.scrollIntoView({block:"nearest"})}_isPageItemInThisSection(pageItem){if(pageItem.sectionId==this.id)return!0;return!!this.element.querySelector("".concat(this.selectors.SECTION,'[data-id="').concat(pageItem.sectionId,'"]'))}_isParentSectionIndexCollapsed(state){const parentElement=this.element.parentElement.closest(this.selectors.SECTION);if(!parentElement||!parentElement.dataset.id)return null;return!!state.section.get(parentElement.dataset.id).indexcollapsed}async addOverlay(){this.element.classList.add(this.classes.OVERLAYBORDERS)}removeOverlay(){this.element.classList.remove(this.classes.OVERLAYBORDERS)}}return _exports.default=Component,_exports.default})); //# sourceMappingURL=section.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseindex/section.min.js.map b/course/format/amd/build/local/courseindex/section.min.js.map index a6152fddf81..33de9180347 100644 --- a/course/format/amd/build/local/courseindex/section.min.js.map +++ b/course/format/amd/build/local/courseindex/section.min.js.map @@ -1 +1 @@ -{"version":3,"file":"section.min.js","sources":["../../../src/local/courseindex/section.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 * Course index section component.\n *\n * This component is used to control specific course section interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/section\n * @class core_courseformat/local/courseindex/section\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport SectionTitle from 'core_courseformat/local/courseindex/sectiontitle';\nimport DndSection from 'core_courseformat/local/courseeditor/dndsection';\n\nexport default class Component extends DndSection {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'courseindex_section';\n // Default query selectors.\n this.selectors = {\n SECTION: `[data-for='section']`,\n SECTION_ITEM: `[data-for='section_item']`,\n SECTION_TITLE: `[data-for='section_title']`,\n CM_LAST: `[data-for=\"cm\"]:last-child`,\n };\n // Default classes to toggle on refresh.\n this.classes = {\n SECTIONHIDDEN: 'dimmed',\n SECTIONCURRENT: 'current',\n LOCKED: 'editinprogress',\n RESTRICTIONS: 'restrictions',\n PAGEITEM: 'pageitem',\n OVERLAYBORDERS: 'overlay-preview-borders',\n };\n\n // We need our id to watch specific events.\n this.id = this.element.dataset.id;\n this.isPageItem = false;\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new this({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the initial state\n */\n stateReady(state) {\n this.configState(state);\n const sectionItem = this.getElement(this.selectors.SECTION_ITEM);\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init the inner dragable element passing the full section as affected region.\n const titleitem = new SectionTitle({\n ...this,\n element: sectionItem,\n fullregion: this.element,\n });\n this.configDragDrop(titleitem);\n }\n // Check if the current url is the section url.\n const section = state.section.get(this.id);\n if (window.location.href == section.sectionurl.replace(/&/g, \"&\")) {\n this.reactive.dispatch('setPageItem', 'section', this.id);\n sectionItem.scrollIntoView();\n }\n }\n\n /**\n * Component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n {watch: `section[${this.id}]:deleted`, handler: this.remove},\n {watch: `section[${this.id}]:updated`, handler: this._refreshSection},\n {watch: `course.pageItem:updated`, handler: this._refreshPageItem},\n ];\n }\n\n /**\n * Get the last CM element of that section.\n *\n * @returns {element|null}\n */\n getLastCm() {\n return this.getElement(this.selectors.CM_LAST);\n }\n\n /**\n * Update a course index section using the state information.\n *\n * @param {Object} param details the update details.\n * @param {Object} param.element the section element\n */\n _refreshSection({element}) {\n // Update classes.\n const sectionItem = this.getElement(this.selectors.SECTION_ITEM);\n sectionItem.classList.toggle(this.classes.SECTIONHIDDEN, !element.visible);\n sectionItem.classList.toggle(this.classes.RESTRICTIONS, element.hasrestrictions ?? false);\n this.element.classList.toggle(this.classes.SECTIONCURRENT, element.current);\n this.element.classList.toggle(this.classes.DRAGGING, element.dragging ?? false);\n this.element.classList.toggle(this.classes.LOCKED, element.locked ?? false);\n this.locked = element.locked;\n // Update title.\n this.getElement(this.selectors.SECTION_TITLE).innerHTML = element.title;\n }\n\n /**\n * Handle a page item update.\n *\n * @param {Object} details the update details\n * @param {Object} details.state the state data.\n * @param {Object} details.element the course state data.\n */\n _refreshPageItem({element, state}) {\n if (!element.pageItem) {\n return;\n }\n\n const containsPageItem = this._isPageItemInThisSection(element.pageItem);\n\n if (!containsPageItem || this._isParentSectionIndexCollapsed(state)) {\n this.pageItem = false;\n this.getElement(this.selectors.SECTION_ITEM).classList.remove(this.classes.PAGEITEM);\n return;\n }\n\n const section = state.section.get(this.id);\n if (section.indexcollapsed && !element.pageItem?.isStatic) {\n this.pageItem = containsPageItem;\n } else {\n this.pageItem = (element.pageItem.type == 'section' && element.pageItem.id == this.id);\n }\n const sectionItem = this.getElement(this.selectors.SECTION_ITEM);\n sectionItem.classList.toggle(this.classes.PAGEITEM, this.pageItem ?? false);\n if (this.pageItem && !this.reactive.isEditing) {\n this.element.scrollIntoView({block: \"nearest\"});\n }\n }\n\n /**\n * Check if the page item is inside this section.\n *\n * @private\n * @param {Object} pageItem\n * @param {Object} pageItem.sectionId the current page item section id.\n * @returns {boolean}\n */\n _isPageItemInThisSection(pageItem) {\n if (pageItem.sectionId == this.id) {\n return true;\n }\n // Check for any possible subsections.\n const subsection = this.element.querySelector(`${this.selectors.SECTION}[data-id=\"${pageItem.sectionId}\"]`);\n if (subsection) {\n return true;\n }\n return false;\n }\n\n /**\n * Check if the parent section index is collapsed.\n *\n * @private\n * @param {Object} state the current state\n * @returns {boolean|null} null if no parent section is found.\n */\n _isParentSectionIndexCollapsed(state) {\n const parentElement = this.element.parentElement.closest(this.selectors.SECTION);\n if (!parentElement || !parentElement.dataset.id) {\n return null;\n }\n const parentSection = state.section.get(parentElement.dataset.id);\n return !!parentSection.indexcollapsed;\n }\n\n /**\n * Overridden version of the component addOverlay async method.\n *\n * The course index is not compatible with overlay elements.\n */\n async addOverlay() {\n this.element.classList.add(this.classes.OVERLAYBORDERS);\n }\n\n /**\n * Overridden version of the component removeOverlay.\n *\n * The course index is not compatible with overlay elements.\n */\n removeOverlay() {\n this.element.classList.remove(this.classes.OVERLAYBORDERS);\n }\n}\n"],"names":["Component","DndSection","create","name","selectors","SECTION","SECTION_ITEM","SECTION_TITLE","CM_LAST","classes","SECTIONHIDDEN","SECTIONCURRENT","LOCKED","RESTRICTIONS","PAGEITEM","OVERLAYBORDERS","id","this","element","dataset","isPageItem","target","document","getElementById","stateReady","state","configState","sectionItem","getElement","reactive","isEditing","supportComponents","titleitem","SectionTitle","fullregion","configDragDrop","section","get","window","location","href","sectionurl","replace","dispatch","scrollIntoView","getWatchers","watch","handler","remove","_refreshSection","_refreshPageItem","getLastCm","classList","toggle","visible","hasrestrictions","current","DRAGGING","dragging","locked","innerHTML","title","pageItem","containsPageItem","_isPageItemInThisSection","_isParentSectionIndexCollapsed","indexcollapsed","_element$pageItem","isStatic","type","block","sectionId","querySelector","parentElement","closest","add","removeOverlay"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"section.min.js","sources":["../../../src/local/courseindex/section.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 * Course index section component.\n *\n * This component is used to control specific course section interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/section\n * @class core_courseformat/local/courseindex/section\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport SectionTitle from 'core_courseformat/local/courseindex/sectiontitle';\nimport DndSection from 'core_courseformat/local/courseeditor/dndsection';\n\nexport default class Component extends DndSection {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'courseindex_section';\n // Default query selectors.\n this.selectors = {\n SECTION: `[data-for='section']`,\n SECTION_ITEM: `[data-for='section_item']`,\n SECTION_TITLE: `[data-for='section_title']`,\n CM_LAST: `[data-for=\"cm\"]:last-child`,\n DND_ALLOWED: `[data-courseindexdndallowed='true']`,\n };\n // Default classes to toggle on refresh.\n this.classes = {\n SECTIONHIDDEN: 'dimmed',\n SECTIONCURRENT: 'current',\n LOCKED: 'editinprogress',\n RESTRICTIONS: 'restrictions',\n PAGEITEM: 'pageitem',\n OVERLAYBORDERS: 'overlay-preview-borders',\n };\n\n // We need our id to watch specific events.\n this.id = this.element.dataset.id;\n this.isPageItem = false;\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new this({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the initial state\n */\n stateReady(state) {\n this.configState(state);\n const sectionItem = this.getElement(this.selectors.SECTION_ITEM);\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents && document.querySelector(this.selectors.DND_ALLOWED)) {\n // Init the inner dragable element passing the full section as affected region.\n const titleitem = new SectionTitle({\n ...this,\n element: sectionItem,\n fullregion: this.element,\n });\n this.configDragDrop(titleitem);\n }\n // Check if the current url is the section url.\n const section = state.section.get(this.id);\n if (window.location.href == section.sectionurl.replace(/&/g, \"&\")) {\n this.reactive.dispatch('setPageItem', 'section', this.id);\n sectionItem.scrollIntoView();\n }\n }\n\n /**\n * Component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n {watch: `section[${this.id}]:deleted`, handler: this.remove},\n {watch: `section[${this.id}]:updated`, handler: this._refreshSection},\n {watch: `course.pageItem:updated`, handler: this._refreshPageItem},\n ];\n }\n\n /**\n * Get the last CM element of that section.\n *\n * @returns {element|null}\n */\n getLastCm() {\n return this.getElement(this.selectors.CM_LAST);\n }\n\n /**\n * Update a course index section using the state information.\n *\n * @param {Object} param details the update details.\n * @param {Object} param.element the section element\n */\n _refreshSection({element}) {\n // Update classes.\n const sectionItem = this.getElement(this.selectors.SECTION_ITEM);\n sectionItem.classList.toggle(this.classes.SECTIONHIDDEN, !element.visible);\n sectionItem.classList.toggle(this.classes.RESTRICTIONS, element.hasrestrictions ?? false);\n this.element.classList.toggle(this.classes.SECTIONCURRENT, element.current);\n this.element.classList.toggle(this.classes.DRAGGING, element.dragging ?? false);\n this.element.classList.toggle(this.classes.LOCKED, element.locked ?? false);\n this.locked = element.locked;\n // Update title.\n this.getElement(this.selectors.SECTION_TITLE).innerHTML = element.title;\n }\n\n /**\n * Handle a page item update.\n *\n * @param {Object} details the update details\n * @param {Object} details.state the state data.\n * @param {Object} details.element the course state data.\n */\n _refreshPageItem({element, state}) {\n if (!element.pageItem) {\n return;\n }\n\n const containsPageItem = this._isPageItemInThisSection(element.pageItem);\n\n if (!containsPageItem || this._isParentSectionIndexCollapsed(state)) {\n this.pageItem = false;\n this.getElement(this.selectors.SECTION_ITEM).classList.remove(this.classes.PAGEITEM);\n return;\n }\n\n const section = state.section.get(this.id);\n if (section.indexcollapsed && !element.pageItem?.isStatic) {\n this.pageItem = containsPageItem;\n } else {\n this.pageItem = (element.pageItem.type == 'section' && element.pageItem.id == this.id);\n }\n const sectionItem = this.getElement(this.selectors.SECTION_ITEM);\n sectionItem.classList.toggle(this.classes.PAGEITEM, this.pageItem ?? false);\n if (this.pageItem && !this.reactive.isEditing) {\n this.element.scrollIntoView({block: \"nearest\"});\n }\n }\n\n /**\n * Check if the page item is inside this section.\n *\n * @private\n * @param {Object} pageItem\n * @param {Object} pageItem.sectionId the current page item section id.\n * @returns {boolean}\n */\n _isPageItemInThisSection(pageItem) {\n if (pageItem.sectionId == this.id) {\n return true;\n }\n // Check for any possible subsections.\n const subsection = this.element.querySelector(`${this.selectors.SECTION}[data-id=\"${pageItem.sectionId}\"]`);\n if (subsection) {\n return true;\n }\n return false;\n }\n\n /**\n * Check if the parent section index is collapsed.\n *\n * @private\n * @param {Object} state the current state\n * @returns {boolean|null} null if no parent section is found.\n */\n _isParentSectionIndexCollapsed(state) {\n const parentElement = this.element.parentElement.closest(this.selectors.SECTION);\n if (!parentElement || !parentElement.dataset.id) {\n return null;\n }\n const parentSection = state.section.get(parentElement.dataset.id);\n return !!parentSection.indexcollapsed;\n }\n\n /**\n * Overridden version of the component addOverlay async method.\n *\n * The course index is not compatible with overlay elements.\n */\n async addOverlay() {\n this.element.classList.add(this.classes.OVERLAYBORDERS);\n }\n\n /**\n * Overridden version of the component removeOverlay.\n *\n * The course index is not compatible with overlay elements.\n */\n removeOverlay() {\n this.element.classList.remove(this.classes.OVERLAYBORDERS);\n }\n}\n"],"names":["Component","DndSection","create","name","selectors","SECTION","SECTION_ITEM","SECTION_TITLE","CM_LAST","DND_ALLOWED","classes","SECTIONHIDDEN","SECTIONCURRENT","LOCKED","RESTRICTIONS","PAGEITEM","OVERLAYBORDERS","id","this","element","dataset","isPageItem","target","document","getElementById","stateReady","state","configState","sectionItem","getElement","reactive","isEditing","supportComponents","querySelector","titleitem","SectionTitle","fullregion","configDragDrop","section","get","window","location","href","sectionurl","replace","dispatch","scrollIntoView","getWatchers","watch","handler","remove","_refreshSection","_refreshPageItem","getLastCm","classList","toggle","visible","hasrestrictions","current","DRAGGING","dragging","locked","innerHTML","title","pageItem","containsPageItem","_isPageItemInThisSection","_isParentSectionIndexCollapsed","indexcollapsed","_element$pageItem","isStatic","type","block","sectionId","parentElement","closest","add","removeOverlay"],"mappings":"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"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseindex/sectiontitle.min.js b/course/format/amd/build/local/courseindex/sectiontitle.min.js index 6cbdca4d8ab..b0066d6efd1 100644 --- a/course/format/amd/build/local/courseindex/sectiontitle.min.js +++ b/course/format/amd/build/local/courseindex/sectiontitle.min.js @@ -8,6 +8,6 @@ define("core_courseformat/local/courseindex/sectiontitle",["exports","core_cours * @class core_courseformat/local/courseindex/sectiontitle * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_dndsectionitem=(obj=_dndsectionitem)&&obj.__esModule?obj:{default:obj};class Component extends _dndsectionitem.default{create(descriptor){this.name="courseindex_sectiontitle",this.id=descriptor.id,this.section=descriptor.section,this.course=descriptor.course,this.fullregion=descriptor.fullregion,this.section.number>0&&null===this.section.component&&(this.getDraggableData=this._getDraggableData)}static init(target,selectors){return new this({element:document.getElementById(target),selectors:selectors})}stateReady(state){this.configDragDrop(this.id,state,this.fullregion)}}return _exports.default=Component,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_dndsectionitem=(obj=_dndsectionitem)&&obj.__esModule?obj:{default:obj};class Component extends _dndsectionitem.default{create(descriptor){this.name="courseindex_sectiontitle",this.id=descriptor.id,this.section=descriptor.section,this.course=descriptor.course,this.fullregion=descriptor.fullregion,this.section.number>0&&null===this.section.component&&(this.getDraggableData=this._getDraggableData)}static init(target,selectors){return new this({element:document.getElementById(target),selectors:selectors})}stateReady(state){this.configDragDrop(this.id,state,this.fullregion,!!document.querySelector('[data-courseindexdndallowed="true"]'))}}return _exports.default=Component,_exports.default})); //# sourceMappingURL=sectiontitle.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseindex/sectiontitle.min.js.map b/course/format/amd/build/local/courseindex/sectiontitle.min.js.map index 1363f9efedc..4359e31478f 100644 --- a/course/format/amd/build/local/courseindex/sectiontitle.min.js.map +++ b/course/format/amd/build/local/courseindex/sectiontitle.min.js.map @@ -1 +1 @@ -{"version":3,"file":"sectiontitle.min.js","sources":["../../../src/local/courseindex/sectiontitle.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 * Course index section title component.\n *\n * This component is used to control specific course section interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/sectiontitle\n * @class core_courseformat/local/courseindex/sectiontitle\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DndSectionItem from 'core_courseformat/local/courseeditor/dndsectionitem';\n\nexport default class Component extends DndSectionItem {\n\n /**\n * Constructor hook.\n *\n * @param {Object} descriptor\n */\n create(descriptor) {\n // Optional component name for debugging.\n this.name = 'courseindex_sectiontitle';\n\n this.id = descriptor.id;\n this.section = descriptor.section;\n this.course = descriptor.course;\n this.fullregion = descriptor.fullregion;\n\n // Prevent topic zero and delegated sections from being draggable.\n if (this.section.number > 0 && this.section.component === null) {\n this.getDraggableData = this._getDraggableData;\n }\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new this({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the initial state\n */\n stateReady(state) {\n this.configDragDrop(this.id, state, this.fullregion);\n }\n}\n"],"names":["Component","DndSectionItem","create","descriptor","name","id","section","course","fullregion","this","number","component","getDraggableData","_getDraggableData","target","selectors","element","document","getElementById","stateReady","state","configDragDrop"],"mappings":";;;;;;;;;;mKA4BqBA,kBAAkBC,wBAOnCC,OAAOC,iBAEEC,KAAO,gCAEPC,GAAKF,WAAWE,QAChBC,QAAUH,WAAWG,aACrBC,OAASJ,WAAWI,YACpBC,WAAaL,WAAWK,WAGzBC,KAAKH,QAAQI,OAAS,GAAgC,OAA3BD,KAAKH,QAAQK,iBACnCC,iBAAmBH,KAAKI,+BAWzBC,OAAQC,kBACT,IAAIN,KAAK,CACZO,QAASC,SAASC,eAAeJ,QACjCC,UAAAA,YASRI,WAAWC,YACFC,eAAeZ,KAAKJ,GAAIe,MAAOX,KAAKD"} \ No newline at end of file +{"version":3,"file":"sectiontitle.min.js","sources":["../../../src/local/courseindex/sectiontitle.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 * Course index section title component.\n *\n * This component is used to control specific course section interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/sectiontitle\n * @class core_courseformat/local/courseindex/sectiontitle\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DndSectionItem from 'core_courseformat/local/courseeditor/dndsectionitem';\n\nexport default class Component extends DndSectionItem {\n\n /**\n * Constructor hook.\n *\n * @param {Object} descriptor\n */\n create(descriptor) {\n // Optional component name for debugging.\n this.name = 'courseindex_sectiontitle';\n\n this.id = descriptor.id;\n this.section = descriptor.section;\n this.course = descriptor.course;\n this.fullregion = descriptor.fullregion;\n\n // Prevent topic zero and delegated sections from being draggable.\n if (this.section.number > 0 && this.section.component === null) {\n this.getDraggableData = this._getDraggableData;\n }\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new this({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the initial state\n */\n stateReady(state) {\n this.configDragDrop(this.id, state, this.fullregion, !!document.querySelector(`[data-courseindexdndallowed=\"true\"]`));\n }\n}\n"],"names":["Component","DndSectionItem","create","descriptor","name","id","section","course","fullregion","this","number","component","getDraggableData","_getDraggableData","target","selectors","element","document","getElementById","stateReady","state","configDragDrop","querySelector"],"mappings":";;;;;;;;;;mKA4BqBA,kBAAkBC,wBAOnCC,OAAOC,iBAEEC,KAAO,gCAEPC,GAAKF,WAAWE,QAChBC,QAAUH,WAAWG,aACrBC,OAASJ,WAAWI,YACpBC,WAAaL,WAAWK,WAGzBC,KAAKH,QAAQI,OAAS,GAAgC,OAA3BD,KAAKH,QAAQK,iBACnCC,iBAAmBH,KAAKI,+BAWzBC,OAAQC,kBACT,IAAIN,KAAK,CACZO,QAASC,SAASC,eAAeJ,QACjCC,UAAAA,YASRI,WAAWC,YACFC,eAAeZ,KAAKJ,GAAIe,MAAOX,KAAKD,aAAcS,SAASK"} \ No newline at end of file diff --git a/course/format/amd/src/local/courseeditor/dndsectionitem.js b/course/format/amd/src/local/courseeditor/dndsectionitem.js index b71b728868b..86404ce4744 100644 --- a/course/format/amd/src/local/courseeditor/dndsectionitem.js +++ b/course/format/amd/src/local/courseeditor/dndsectionitem.js @@ -35,8 +35,9 @@ export default class extends BaseComponent { * @param {number} sectionid the section id * @param {Object} state the initial state * @param {Element} fullregion the complete section region to mark as dragged + * @param {Boolean} isDndAllowed Whether drag and drop is allowed in this section. */ - configDragDrop(sectionid, state, fullregion) { + configDragDrop(sectionid, state, fullregion, isDndAllowed = true) { this.id = sectionid; if (this.section === undefined) { @@ -54,7 +55,7 @@ export default class extends BaseComponent { this.fullregion = fullregion; // Drag and drop is only available for components compatible course formats. - if (this.reactive.isEditing && this.reactive.supportComponents) { + if (this.reactive.isEditing && this.reactive.supportComponents && isDndAllowed) { // Init the dropzone. this.dragdrop = new DragDrop(this); // Save dropzone classes. diff --git a/course/format/amd/src/local/courseindex/cm.js b/course/format/amd/src/local/courseindex/cm.js index 6dd943dcecd..ad6557aded4 100644 --- a/course/format/amd/src/local/courseindex/cm.js +++ b/course/format/amd/src/local/courseindex/cm.js @@ -46,6 +46,7 @@ export default class Component extends DndCmItem { this.selectors = { CM_NAME: `[data-for='cm_name']`, CM_COMPLETION: `[data-for='cm_completion']`, + DND_ALLOWED: `[data-courseindexdndallowed='true']`, }; // Default classes to toggle on refresh. this.classes = { @@ -79,7 +80,9 @@ export default class Component extends DndCmItem { * @param {Object} state the course state. */ stateReady(state) { - this.configDragDrop(this.id); + if (document.querySelector(this.selectors.DND_ALLOWED)) { + this.configDragDrop(this.id); + } const cm = state.cm.get(this.id); const course = state.course; // Refresh completion icon. diff --git a/course/format/amd/src/local/courseindex/section.js b/course/format/amd/src/local/courseindex/section.js index 996b8008b24..ff1738422ce 100644 --- a/course/format/amd/src/local/courseindex/section.js +++ b/course/format/amd/src/local/courseindex/section.js @@ -41,6 +41,7 @@ export default class Component extends DndSection { SECTION_ITEM: `[data-for='section_item']`, SECTION_TITLE: `[data-for='section_title']`, CM_LAST: `[data-for="cm"]:last-child`, + DND_ALLOWED: `[data-courseindexdndallowed='true']`, }; // Default classes to toggle on refresh. this.classes = { @@ -80,7 +81,7 @@ export default class Component extends DndSection { this.configState(state); const sectionItem = this.getElement(this.selectors.SECTION_ITEM); // Drag and drop is only available for components compatible course formats. - if (this.reactive.isEditing && this.reactive.supportComponents) { + if (this.reactive.isEditing && this.reactive.supportComponents && document.querySelector(this.selectors.DND_ALLOWED)) { // Init the inner dragable element passing the full section as affected region. const titleitem = new SectionTitle({ ...this, diff --git a/course/format/amd/src/local/courseindex/sectiontitle.js b/course/format/amd/src/local/courseindex/sectiontitle.js index 4d419ed244f..b0ff4472325 100644 --- a/course/format/amd/src/local/courseindex/sectiontitle.js +++ b/course/format/amd/src/local/courseindex/sectiontitle.js @@ -68,6 +68,6 @@ export default class Component extends DndSectionItem { * @param {Object} state the initial state */ stateReady(state) { - this.configDragDrop(this.id, state, this.fullregion); + this.configDragDrop(this.id, state, this.fullregion, !!document.querySelector(`[data-courseindexdndallowed="true"]`)); } } diff --git a/course/section.php b/course/section.php index 0fe81a5b6e2..dd3441aa07f 100644 --- a/course/section.php +++ b/course/section.php @@ -198,15 +198,21 @@ if (core_communication\api::is_available() && has_capability('moodle/course:upda $communication->show_communication_room_status_notification(); } -// Display a warning if asynchronous backups are pending for this course. +$containerattributes = []; if ($PAGE->user_is_editing()) { require_once($CFG->dirroot . '/backup/util/helper/async_helper.class.php'); + // Display a warning if asynchronous backups are pending for this course. if (async_helper::is_async_pending($course->id, 'course', 'backup')) { echo $OUTPUT->notification(get_string('pendingasyncedit', 'backup'), 'warning'); } + + // Allow drag and drop in the course index. + $containerattributes = [ + 'data-courseindexdndallowed' => 'true', + ]; } -echo $renderer->container_start('course-content'); +echo $renderer->container_start('course-content', attributes: $containerattributes); // Include course AJAX. include_course_ajax($course, $modinfo->get_used_module_names()); diff --git a/course/view.php b/course/view.php index 900607f0112..8ac0fafa2f3 100644 --- a/course/view.php +++ b/course/view.php @@ -311,18 +311,21 @@ if (has_capability('moodle/course:update', $context)) { core_communication\helper::get_course_communication_status_notification($course); } -if ($USER->editing == 1) { - +$containerattributes = ['class' => 'course-content']; +if ($PAGE->user_is_editing()) { // MDL-65321 The backup libraries are quite heavy, only require the bare minimum. require_once($CFG->dirroot . '/backup/util/helper/async_helper.class.php'); if (async_helper::is_async_pending($id, 'course', 'backup')) { echo $OUTPUT->notification(get_string('pendingasyncedit', 'backup'), 'warning'); } + + // Allow drag and drop in the course index. + $containerattributes['data-courseindexdndallowed'] = 'true'; } // Course wrapper start. -echo html_writer::start_tag('div', ['class' => 'course-content']); +echo html_writer::start_tag('div', $containerattributes); // Make sure that section 0 exists (this function will create one if it is missing). course_create_sections_if_missing($course, 0);