diff --git a/public/course/format/amd/build/local/courseindex/section.min.js b/public/course/format/amd/build/local/courseindex/section.min.js index 5bb626db405..df56a5096a7 100644 --- a/public/course/format/amd/build/local/courseindex/section.min.js +++ b/public/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),_log=_interopRequireDefault(_log);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){let element=document.querySelector(target);return element||(_log.default.debug("Init component with id is deprecated, use a query selector instead."),element=document.getElementById(target)),new this({element:element,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})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_sectiontitle=_interopRequireDefault(_sectiontitle),_dndsection=_interopRequireDefault(_dndsection),_log=_interopRequireDefault(_log);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){let element=document.querySelector(target);return element||(_log.default.debug("Init component with id is deprecated, use a query selector instead."),element=document.getElementById(target)),new this({element:element,selectors:selectors})}stateReady(state){var _section$sectionurl;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==(null===(_section$sectionurl=section.sectionurl)||void 0===_section$sectionurl?void 0:_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/public/course/format/amd/build/local/courseindex/section.min.js.map b/public/course/format/amd/build/local/courseindex/section.min.js.map index 30643cbc8c6..2ac1284f61a 100644 --- a/public/course/format/amd/build/local/courseindex/section.min.js.map +++ b/public/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';\nimport log from \"core/log\";\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 let element = document.querySelector(target);\n // TODO Remove this if condition as part of MDL-83851.\n if (!element) {\n log.debug('Init component with id is deprecated, use a query selector instead.');\n element = document.getElementById(target);\n }\n return new this({\n element,\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","querySelector","debug","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","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';\nimport log from \"core/log\";\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 let element = document.querySelector(target);\n // TODO Remove this if condition as part of MDL-83851.\n if (!element) {\n log.debug('Init component with id is deprecated, use a query selector instead.');\n element = document.getElementById(target);\n }\n return new this({\n element,\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 // Sections urls may be null.\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","querySelector","debug","getElementById","stateReady","state","configState","sectionItem","getElement","reactive","isEditing","supportComponents","titleitem","SectionTitle","fullregion","configDragDrop","section","get","window","location","href","sectionurl","_section$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/public/course/format/amd/src/local/courseindex/section.js b/public/course/format/amd/src/local/courseindex/section.js index f52fc88fc35..c432b99ecd1 100644 --- a/public/course/format/amd/src/local/courseindex/section.js +++ b/public/course/format/amd/src/local/courseindex/section.js @@ -99,7 +99,8 @@ export default class Component extends DndSection { } // Check if the current url is the section url. const section = state.section.get(this.id); - if (window.location.href == section.sectionurl.replace(/&/g, "&")) { + // Sections urls may be null. + if (window.location.href == section.sectionurl?.replace(/&/g, "&")) { this.reactive.dispatch('setPageItem', 'section', this.id); sectionItem.scrollIntoView(); } diff --git a/public/course/format/classes/output/local/content/sectionnavigation.php b/public/course/format/classes/output/local/content/sectionnavigation.php index 2b5639d2c9b..02f7059009c 100644 --- a/public/course/format/classes/output/local/content/sectionnavigation.php +++ b/public/course/format/classes/output/local/content/sectionnavigation.php @@ -101,7 +101,8 @@ class sectionnavigation implements named_templatable, renderable { } $data->previousname = get_section_name($course, $sections[$back]); $data->previousurl = course_get_url($course, $back, ['navigation' => true]); - $data->hasprevious = true; + // If there is no url for the section the link should not be displayed. + $data->hasprevious = !empty($data->previousurl); } $back--; } @@ -115,7 +116,8 @@ class sectionnavigation implements named_templatable, renderable { } $data->nextname = get_section_name($course, $sections[$forward]); $data->nexturl = course_get_url($course, $forward, ['navigation' => true]); - $data->hasnext = true; + // If there is no url for the section the link should not be displayed. + $data->hasnext = !empty($data->nexturl); } $forward++; } diff --git a/public/course/format/classes/output/local/content/sectionselector.php b/public/course/format/classes/output/local/content/sectionselector.php index 74a7fc36f80..7113d452159 100644 --- a/public/course/format/classes/output/local/content/sectionselector.php +++ b/public/course/format/classes/output/local/content/sectionselector.php @@ -120,7 +120,9 @@ class sectionselector implements named_templatable, renderable { nothing: ['' => get_string('jumpto')], ); // Disable the current section. - $select->set_option_disabled($disabledlink); + if (!is_null($disabledlink)) { + $select->set_option_disabled($disabledlink); + } $select->class = 'jumpmenu'; $select->formid = 'sectionmenu'; @@ -143,17 +145,19 @@ class sectionselector implements named_templatable, renderable { bool $indent = false ) { $url = $this->get_section_url($course, $section); - $indentation = $indent ? self::INDENTER : ''; - $this->sectionmenu[$url] = $indentation . $format->get_section_name($section); + if (!is_null($url)) { + $indentation = $indent ? self::INDENTER : ''; + $this->sectionmenu[$url] = $indentation . $format->get_section_name($section); + } } /** * Get the section url. * @param stdClass $course * @param section_info $section - * @return string + * @return string|null */ - private function get_section_url(stdClass $course, section_info $section): string { - return course_get_url($course, (object) $section, ['navigation' => true])->out(false); + private function get_section_url(stdClass $course, section_info $section): ?string { + return course_get_url($course, (object) $section, ['navigation' => true])?->out(false); } } diff --git a/public/course/format/classes/output/local/state/section.php b/public/course/format/classes/output/local/state/section.php index 0ca2e63bfb5..96355dc401f 100644 --- a/public/course/format/classes/output/local/state/section.php +++ b/public/course/format/classes/output/local/state/section.php @@ -83,7 +83,7 @@ class section implements renderable { 'rawtitle' => $section->name, 'cmlist' => [], 'visible' => !empty($section->visible), - 'sectionurl' => course_get_url($course, $section->section, ['navigation' => true])->out(false), + 'sectionurl' => course_get_url($course, $section->section, ['navigation' => true])?->out(false), 'current' => $format->is_section_current($section), 'indexcollapsed' => $indexcollapsed, 'contentcollapsed' => $contentcollapsed, diff --git a/public/course/format/singleactivity/lib.php b/public/course/format/singleactivity/lib.php index 8d831b749f7..a142102736b 100644 --- a/public/course/format/singleactivity/lib.php +++ b/public/course/format/singleactivity/lib.php @@ -41,7 +41,7 @@ class format_singleactivity extends core_courseformat\base implements core_cours * @param array $options options for view URL. At the moment core uses: * 'navigation' (bool) ignored by this format * 'sr' (int) ignored by this format - * @return null|moodle_url + * @return moodle_url */ public function get_view_url($section, $options = []) { return new moodle_url('/course/view.php', ['id' => $this->courseid]); diff --git a/public/course/format/social/lib.php b/public/course/format/social/lib.php index a620deacc58..55a0235d65d 100644 --- a/public/course/format/social/lib.php +++ b/public/course/format/social/lib.php @@ -43,7 +43,7 @@ class format_social extends core_courseformat\base { * @param array $options options for view URL. At the moment core uses: * 'navigation' (bool) ignored by this format * 'sr' (int) ignored by this format - * @return null|moodle_url + * @return moodle_url */ public function get_view_url($section, $options = array()) { return new moodle_url('/course/view.php', ['id' => $this->courseid]); diff --git a/public/course/format/templates/local/courseindex/section.mustache b/public/course/format/templates/local/courseindex/section.mustache index 41163cea732..e163fd3758b 100644 --- a/public/course/format/templates/local/courseindex/section.mustache +++ b/public/course/format/templates/local/courseindex/section.mustache @@ -91,14 +91,27 @@ {{#str}} collapse, core {{/str}} - - {{{title}}} - + {{! We display a link when there is a sectionurl, otherwise a span. The data-for attribute is required by JS. }} + {{#sectionurl}} + + {{{title}}} + + {{/sectionurl}} + {{^sectionurl}} + + {{{title}}} + + {{/sectionurl}} {{highlighted}} diff --git a/public/course/format/topics/lib.php b/public/course/format/topics/lib.php index 9a21437440e..f2279d46d7b 100644 --- a/public/course/format/topics/lib.php +++ b/public/course/format/topics/lib.php @@ -107,7 +107,7 @@ class format_topics extends core_courseformat\base { * @param array $options options for view URL. At the moment core uses: * 'navigation' (bool) if true and section not empty, the function returns section page; otherwise, it returns course page. * 'sr' (int) used by course formats to specify to which section to return - * @return null|moodle_url + * @return moodle_url */ public function get_view_url($section, $options = []) { $course = $this->get_course(); diff --git a/public/course/format/weeks/lib.php b/public/course/format/weeks/lib.php index 867b1cdb35d..3359399d4ee 100644 --- a/public/course/format/weeks/lib.php +++ b/public/course/format/weeks/lib.php @@ -124,7 +124,7 @@ class format_weeks extends core_courseformat\base { * @param array $options options for view URL. At the moment core uses: * 'navigation' (bool) if true and section not empty, the function returns section page; otherwise, it returns course page. * 'sr' (int) used by course formats to specify to which section to return - * @return null|moodle_url + * @return moodle_url */ public function get_view_url($section, $options = array()) { $course = $this->get_course(); diff --git a/public/course/lib.php b/public/course/lib.php index e2b60b6295b..ee5c8886a3d 100644 --- a/public/course/lib.php +++ b/public/course/lib.php @@ -2578,7 +2578,7 @@ function get_sorted_course_formats($enabledonly = false) { * @param array $options options for view URL. At the moment core uses: * 'navigation' (bool) if true and section has no separate page, the function returns null * 'sr' (int) used by multipage formats to specify to which section to return - * @return moodle_url The url of course + * @return moodle_url|null The url of course */ function course_get_url($courseorid, $section = null, $options = array()) { return course_get_format($courseorid)->get_view_url($section, $options);