diff --git a/amd/build/courseformat/content.min.js b/amd/build/courseformat/content.min.js index bdc1b57..7c869a9 100644 --- a/amd/build/courseformat/content.min.js +++ b/amd/build/courseformat/content.min.js @@ -7,6 +7,6 @@ define("format_multitopic/courseformat/content",["exports","core_courseformat/lo * @copyright 2022 James Calder and Otago Polytechnic * @copyright based on work by 2020 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,_content=_interopRequireDefault(_content),_inplace_editable=_interopRequireDefault(_inplace_editable),_section=_interopRequireDefault(_section),_cmitem=_interopRequireDefault(_cmitem),_templates=_interopRequireDefault(_templates),_actions=_interopRequireDefault(_actions),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents);class Component extends _content.default{create(descriptor){super.create(descriptor),this.version=descriptor.version}static init(target,selectors,sectionReturn,version){return new this({element:document.getElementById(target),reactive:(0,_courseeditor.getCurrentCourseEditor)(),selectors:selectors,sectionReturn:sectionReturn,version:version})}stateReady(state){this._indexContents(),this.addEventListener(this.element,"click",this._sectionTogglers);const toogleAll=this.getElement(this.selectors.TOGGLEALL);if(toogleAll){const collapseElementIds=[...this.getElements(this.selectors.COLLAPSE)].map((element=>element.id));toogleAll.setAttribute("aria-controls",collapseElementIds.join(" ")),this.addEventListener(toogleAll,"click",this._allSectionToggler),this.addEventListener(toogleAll,"keydown",(e=>{" "===e.key&&this._allSectionToggler(e)})),this._refreshAllSectionsToggler(state)}this.reactive.supportComponents&&(this.reactive.isEditing&&new _actions.default(this),this.element.classList.add(this.classes.STATEDREADY)),this.addEventListener(this.element,CourseEvents.manualCompletionToggled,this._completionHandler),this.addEventListener(this.version>=2023081800?document:document.querySelector(this.selectors.PAGE),"scroll",this._scrollHandler),this.fmtCollapseOnHashChange(),window.addEventListener("hashchange",this.fmtCollapseOnHashChange.bind(this))}fmtCollapseOnHashChange(event){let anchor=window.location.hash;if(!anchor.match(/^#sectionid-\d+(?:-title)?$/))return;let oldStyle=!1;anchor.match(/^#sectionid-\d+$/)&&(anchor+="-title",oldStyle=!0,history.replaceState(history.state,"",anchor));const selSectionHeaderDom=document.querySelector(".course-content ul.sections li.section.section-topic .sectionname"+anchor);if(!selSectionHeaderDom)return;const selSectionDom=selSectionHeaderDom.closest("li.section.section-topic"),sectionId=selSectionDom.getAttribute("data-id"),section=this.reactive.get("section",sectionId);selSectionDom.matches(".section-topic-collapsible")&&(selSectionDom.querySelector(".course-section-header .icons-collapse-expand.collapsed")||section.contentcollapsed)&&this.reactive.dispatch("sectionContentCollapsed",[sectionId],!1),oldStyle&&selSectionDom.scrollIntoView()}_allSectionToggler(event){event.preventDefault();const isAllCollapsed=event.target.closest(this.selectors.TOGGLEALL).classList.contains(this.classes.COLLAPSED);let sectionlist=[];const sectionlistDom=this.element.querySelectorAll(".course-section.section-topic-collapsible[data-fmtonpage='1']");for(let sectionCount=0;sectionCount{sectionCollapsible[section.id]&&(allcollapsed=allcollapsed&§ion.contentcollapsed,allexpanded=allexpanded&&!section.contentcollapsed)})),target.style.display=allexpanded&&allcollapsed?"none":"block",allcollapsed&&(target.classList.add(this.classes.COLLAPSED),target.setAttribute("aria-expanded",!1)),allexpanded&&(target.classList.remove(this.classes.COLLAPSED),target.setAttribute("aria-expanded",!0))}_refreshSectionNumber(_ref){let{element:element}=_ref;const target=this.getElement(this.selectors.SECTION,element.id);if(!target)return;target.id="section-".concat(element.number),target.dataset.sectionid=element.number,target.dataset.number=element.number;const inplace=_inplace_editable.default.getInplaceEditable(target.querySelector(this.selectors.SECTION_ITEM));if(inplace){const currentvalue=inplace.getValue(),currentitemid=inplace.getItemId();(""===inplace.getValue()||element.timed)&&(currentitemid!=element.id||currentvalue==element.rawtitle&&""!=element.rawtitle&&!element.timed||inplace.setValue(element.rawtitle))}}_refreshCourseSectionlist(param){super._refreshCourseSectionlist(param);const sectionsDom=this.element.querySelectorAll(this.selectors.SECTION);for(let sdi=0;sdinew _section.default(item))),this._scanIndex(this.selectors.CM,this.cms,(item=>new _cmitem.default(item)))}}return _exports.default=Component,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_content=_interopRequireDefault(_content),_inplace_editable=_interopRequireDefault(_inplace_editable),_section=_interopRequireDefault(_section),_cmitem=_interopRequireDefault(_cmitem),_templates=_interopRequireDefault(_templates),_actions=_interopRequireDefault(_actions),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents);class Component extends _content.default{create(descriptor){super.create(descriptor),this.version=descriptor.version,this.originalsinglesectionid=this.element.querySelector("ul.sections").dataset.originalsinglesectionid}static init(target,selectors,sectionReturn,version){return new this({element:document.getElementById(target),reactive:(0,_courseeditor.getCurrentCourseEditor)(),selectors:selectors,sectionReturn:sectionReturn,version:version})}stateReady(state){this._indexContents(),this.addEventListener(this.element,"click",this._sectionTogglers);const toogleAll=this.getElement(this.selectors.TOGGLEALL);if(toogleAll){const collapseElementIds=[...this.getElements(this.selectors.COLLAPSE)].map((element=>element.id));toogleAll.setAttribute("aria-controls",collapseElementIds.join(" ")),this.addEventListener(toogleAll,"click",this._allSectionToggler),this.addEventListener(toogleAll,"keydown",(e=>{" "===e.key&&this._allSectionToggler(e)})),this._refreshAllSectionsToggler(state)}this.reactive.supportComponents&&(this.reactive.isEditing&&new _actions.default(this),this.element.classList.add(this.classes.STATEDREADY)),this.addEventListener(this.element,CourseEvents.manualCompletionToggled,this._completionHandler),this.addEventListener(this.version>=2023081800?document:document.querySelector(this.selectors.PAGE),"scroll",this._scrollHandler),this.fmtCollapseOnHashChange(),window.addEventListener("hashchange",this.fmtCollapseOnHashChange.bind(this))}fmtCollapseOnHashChange(event){let anchor=window.location.hash;if(!anchor.match(/^#sectionid-\d+(?:-title)?$/))return;let oldStyle=!1;anchor.match(/^#sectionid-\d+$/)&&(anchor+="-title",oldStyle=!0,history.replaceState(history.state,"",anchor));const selSectionHeaderDom=document.querySelector(".course-content ul.sections li.section.section-topic .sectionname"+anchor);if(!selSectionHeaderDom)return;const selSectionDom=selSectionHeaderDom.closest("li.section.section-topic"),sectionId=selSectionDom.getAttribute("data-id"),section=this.reactive.get("section",sectionId);selSectionDom.matches(".section-topic-collapsible")&&(selSectionDom.querySelector(".course-section-header .icons-collapse-expand.collapsed")||section.contentcollapsed)&&this.reactive.dispatch("sectionContentCollapsed",[sectionId],!1),oldStyle&&selSectionDom.scrollIntoView()}_allSectionToggler(event){event.preventDefault();const isAllCollapsed=event.target.closest(this.selectors.TOGGLEALL).classList.contains(this.classes.COLLAPSED);let sectionlist=[];const sectionlistDom=this.element.querySelectorAll(".course-section.section-topic-collapsible[data-fmtonpage='1']");for(let sectionCount=0;sectionCount{sectionCollapsible[section.id]&&(allcollapsed=allcollapsed&§ion.contentcollapsed,allexpanded=allexpanded&&!section.contentcollapsed)})),target.style.display=allexpanded&&allcollapsed?"none":"block",allcollapsed&&(target.classList.add(this.classes.COLLAPSED),target.setAttribute("aria-expanded",!1)),allexpanded&&(target.classList.remove(this.classes.COLLAPSED),target.setAttribute("aria-expanded",!0))}_refreshSectionNumber(_ref){let{element:element}=_ref;const target=this.getElement(this.selectors.SECTION,element.id);if(!target)return;target.id="section-".concat(element.number),target.dataset.sectionid=element.number,target.dataset.number=element.number;const inplace=_inplace_editable.default.getInplaceEditable(target.querySelector(this.selectors.SECTION_ITEM));if(inplace){const currentvalue=inplace.getValue(),currentitemid=inplace.getItemId();(""===inplace.getValue()||element.timed)&&(currentitemid!=element.id||currentvalue==element.rawtitle&&""!=element.rawtitle&&!element.timed||inplace.setValue(element.rawtitle))}}_refreshCourseSectionlist(param){super._refreshCourseSectionlist(param);const originalSingleSection=this.reactive.get("section",this.originalsinglesectionid),singleSectionId=originalSingleSection?originalSingleSection.levelsan<2?originalSingleSection.id:originalSingleSection.pageid:null,sectionsDom=this.element.querySelectorAll(this.selectors.SECTION);for(let sdi=0;sdi a");addSectionDom.dataset.intoId!=singleSectionId&&(addSectionDom.dataset.intoId=singleSectionId,addSectionDom.href=addSectionDom.href.replace(/\binsertparentid=\d+\b/,"insertparentid="+singleSectionId))}_indexContents(){this._scanIndex(this.selectors.SECTION,this.sections,(item=>new _section.default(item))),this._scanIndex(this.selectors.CM,this.cms,(item=>new _cmitem.default(item)))}}return _exports.default=Component,_exports.default})); //# sourceMappingURL=content.min.js.map \ No newline at end of file diff --git a/amd/build/courseformat/content.min.js.map b/amd/build/courseformat/content.min.js.map index 0977776..e22d45e 100644 --- a/amd/build/courseformat/content.min.js.map +++ b/amd/build/courseformat/content.min.js.map @@ -1 +1 @@ -{"version":3,"file":"content.min.js","sources":["../../src/courseformat/content.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 main component.\n *\n * @module format_multitopic/courseformat/content\n * @class format_multitopic/courseformat/content\n * @copyright 2022 James Calder and Otago Polytechnic\n * @copyright based on work by 2020 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BaseComponent from 'core_courseformat/local/content';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport inplaceeditable from 'core/inplace_editable';\nimport Section from 'format_multitopic/courseformat/content/section';\nimport CmItem from 'format_multitopic/courseformat/content/section/cmitem';\nimport Templates from 'core/templates';\nimport DispatchActions from 'format_multitopic/courseformat/content/actions';\nimport * as CourseEvents from 'core_course/events';\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n *\n * @param {Object} descriptor the component descriptor\n */\n create(descriptor) {\n super.create(descriptor);\n this.version = descriptor.version;\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 * @param {number} sectionReturn the content section return\n * @param {number} version Moodle version number\n * @return {Component}\n */\n static init(target, selectors, sectionReturn, version) {\n return new this({ // CHANGED.\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n selectors,\n sectionReturn,\n version,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the state data\n */\n stateReady(state) {\n this._indexContents();\n // Activate section togglers.\n this.addEventListener(this.element, 'click', this._sectionTogglers);\n\n // Collapse/Expand all sections button.\n const toogleAll = this.getElement(this.selectors.TOGGLEALL);\n if (toogleAll) {\n\n // Ensure collapse menu button adds aria-controls attribute referring to each collapsible element.\n const collapseElements = this.getElements(this.selectors.COLLAPSE);\n const collapseElementIds = [...collapseElements].map(element => element.id);\n toogleAll.setAttribute('aria-controls', collapseElementIds.join(' '));\n\n this.addEventListener(toogleAll, 'click', this._allSectionToggler);\n this.addEventListener(toogleAll, 'keydown', e => {\n // Collapse/expand all sections when Space key is pressed on the toggle button.\n if (e.key === ' ') {\n this._allSectionToggler(e);\n }\n });\n this._refreshAllSectionsToggler(state);\n }\n\n if (this.reactive.supportComponents) {\n // Actions are only available in edit mode.\n if (this.reactive.isEditing) {\n new DispatchActions(this); // CHANGED.\n }\n\n // Mark content as state ready.\n this.element.classList.add(this.classes.STATEDREADY);\n }\n\n // Capture completion events.\n this.addEventListener(\n this.element,\n CourseEvents.manualCompletionToggled,\n this._completionHandler\n );\n\n // Capture page scroll to update page item.\n this.addEventListener(\n (this.version >= 2023081800) ? document : document.querySelector(this.selectors.PAGE),\n \"scroll\",\n this._scrollHandler\n );\n\n // Set the initial state of collapsible sections.\n this.fmtCollapseOnHashChange();\n\n // Capture clicks on course section links.\n window.addEventListener(\"hashchange\", this.fmtCollapseOnHashChange.bind(this));\n\n }\n\n /**\n * Expand, and scroll to, the section specified in the URL bar.\n *\n * @param {HashChangeEvent?} event The triggering event, if any\n */\n /* eslint-disable no-unused-vars */\n fmtCollapseOnHashChange(event) {\n /* eslint-enable no-unused-vars */\n\n // Find the specified section.\n let anchor = window.location.hash;\n if (!anchor.match(/^#sectionid-\\d+(?:-title)?$/)) {\n return;\n }\n let oldStyle = false;\n if (anchor.match(/^#sectionid-\\d+$/)) {\n anchor = anchor + \"-title\";\n oldStyle = true;\n history.replaceState(history.state, \"\", anchor);\n }\n const selSectionHeaderDom =\n document.querySelector(\".course-content ul.sections li.section.section-topic .sectionname\" + anchor);\n\n // Exit if there is no recognised section.\n if (!selSectionHeaderDom) {\n return;\n }\n\n const selSectionDom = selSectionHeaderDom.closest(\"li.section.section-topic\");\n const sectionId = selSectionDom.getAttribute('data-id');\n const section = this.reactive.get('section', sectionId);\n\n // Expand, if appropriate.\n if (selSectionDom.matches(\".section-topic-collapsible\")\n && (selSectionDom.querySelector(\".course-section-header .icons-collapse-expand.collapsed\")\n || section.contentcollapsed)) {\n this.reactive.dispatch(\n 'sectionContentCollapsed',\n [sectionId],\n false\n );\n }\n\n // Scroll to the specified section.\n if (oldStyle) {\n selSectionDom.scrollIntoView();\n }\n\n }\n\n /**\n * Handle the collapse/expand all sections button.\n *\n * Toggler click is delegated to the main course content element because new sections can\n * appear at any moment and this way we prevent accidental double bindings.\n *\n * @param {Event} event the triggered event\n */\n _allSectionToggler(event) {\n event.preventDefault();\n\n const target = event.target.closest(this.selectors.TOGGLEALL);\n const isAllCollapsed = target.classList.contains(this.classes.COLLAPSED);\n\n // CHANGED.\n let sectionlist = [];\n const sectionlistDom = this.element.querySelectorAll(\".course-section.section-topic-collapsible[data-fmtonpage='1']\");\n for (let sectionCount = 0; sectionCount < sectionlistDom.length; sectionCount++) {\n sectionlist.push(sectionlistDom[sectionCount].dataset.id);\n }\n // END CHANGED.\n\n this.reactive.dispatch(\n 'sectionContentCollapsed',\n sectionlist, // CHANGED.\n !isAllCollapsed\n );\n }\n\n /**\n * Refresh the collapse/expand all sections element.\n *\n * @param {Object} state The state data\n */\n _refreshAllSectionsToggler(state) {\n const target = this.getElement(this.selectors.TOGGLEALL);\n if (!target) {\n return;\n }\n // Check if we have all sections collapsed/expanded.\n let allcollapsed = true;\n let allexpanded = true;\n // ADDED.\n let sectionCollapsible = {};\n const sectionlistDom = this.element.querySelectorAll(\".course-section.section-topic-collapsible[data-fmtonpage='1']\");\n for (let sectionCount = 0; sectionCount < sectionlistDom.length; sectionCount++) {\n sectionCollapsible[sectionlistDom[sectionCount].dataset.id] = true;\n }\n // END ADDED.\n state.section.forEach(\n section => {\n if (sectionCollapsible[section.id]) { // ADDED.\n allcollapsed = allcollapsed && section.contentcollapsed;\n allexpanded = allexpanded && !section.contentcollapsed;\n }\n }\n );\n target.style.display = (allexpanded && allcollapsed) ? \"none\" : \"block\"; // ADDED.\n if (allcollapsed) {\n target.classList.add(this.classes.COLLAPSED);\n target.setAttribute('aria-expanded', false);\n }\n if (allexpanded) {\n target.classList.remove(this.classes.COLLAPSED);\n target.setAttribute('aria-expanded', true);\n }\n }\n\n /**\n * Update a course section when the section number changes.\n *\n * The courseActions module used for most course section tools still depends on css classes and\n * section numbers (not id). To prevent inconsistencies when a section is moved, we need to refresh\n * the\n *\n * Course formats can override the section title rendering so the frontend depends heavily on backend\n * rendering. Luckily in edit mode we can trigger a title update using the inplace_editable module.\n *\n * @param {Object} param\n * @param {Object} param.element details the update details.\n */\n _refreshSectionNumber({element}) {\n // Find the element.\n const target = this.getElement(this.selectors.SECTION, element.id);\n if (!target) {\n // Job done. Nothing to refresh.\n return;\n }\n // Update section numbers in all data, css and YUI attributes.\n target.id = `section-${element.number}`;\n // YUI uses section number as section id in data-sectionid, in principle if a format use components\n // don't need this sectionid attribute anymore, but we keep the compatibility in case some plugin\n // use it for legacy purposes.\n target.dataset.sectionid = element.number;\n // The data-number is the attribute used by components to store the section number.\n target.dataset.number = element.number;\n\n // Update title and title inplace editable, if any.\n const inplace = inplaceeditable.getInplaceEditable(target.querySelector(this.selectors.SECTION_ITEM));\n if (inplace) {\n // The course content HTML can be modified at any moment, so the function need to do some checkings\n // to make sure the inplace editable still represents the same itemid.\n const currentvalue = inplace.getValue();\n const currentitemid = inplace.getItemId();\n // Unnamed sections must be recalculated.\n if (inplace.getValue() === '' || element.timed) { // CHANGED.\n // The value to send can be an empty value if it is a default name.\n if (currentitemid == element.id\n && (currentvalue != element.rawtitle || element.rawtitle == '' || element.timed)) { // CHANGED.\n inplace.setValue(element.rawtitle);\n }\n }\n }\n }\n\n /**\n * Refresh the section list.\n *\n * @param {Object} param\n * @param {Object} param.element details the update details (Moodle <4.4).\n * @param {Object} param.state the full state object (Moodle >=4.4).\n */\n _refreshCourseSectionlist(param) {\n super._refreshCourseSectionlist(param);\n const sectionsDom = this.element.querySelectorAll(this.selectors.SECTION);\n for (let sdi = 0; sdi < sectionsDom.length; sdi++) {\n const sectionDom = sectionsDom[sdi];\n const section = this.reactive.get(\"section\", sectionDom.dataset.id);\n if (!section) {\n continue;\n }\n let refreshCms = false;\n const pageSectionDom = this.element.querySelector(\".course-section[data-id='\" + section.pageid + \"']\");\n if (pageSectionDom) {\n const pageSectionDisplay = pageSectionDom.dataset.fmtonpage;\n if (sectionDom.dataset.fmtonpage != pageSectionDisplay) {\n sectionDom.dataset.fmtonpage = pageSectionDisplay;\n sectionDom.style.display = (pageSectionDisplay == \"1\") ? \"block\" : \"none\";\n if (pageSectionDisplay == \"1\") {\n refreshCms = true;\n }\n }\n }\n if (section.visible == sectionDom.classList.contains(\"hidden\")) {\n const badgeDom = sectionDom.querySelector(\"span.badge[data-type='hiddenfromstudents']\");\n if (section.visible) {\n sectionDom.classList.remove(\"hidden\");\n badgeDom.classList.add(\"d-none\");\n } else {\n sectionDom.classList.add(\"hidden\");\n badgeDom.classList.remove(\"d-none\");\n }\n if (sectionDom.dataset.fmtonpage == \"1\") {\n refreshCms = true;\n }\n }\n if (refreshCms) {\n // Note: Visibility state doesn't get updated for CMs already rendered.\n this._refreshSectionCmlist({element: section});\n }\n const menuDom = sectionDom.querySelector(\".course-section-header .section_action_menu\");\n Templates.render(\"core_courseformat/local/content/section/controlmenu\", section.controlmenu).done(function(html) {\n Templates.replaceNode(menuDom, html, \"\");\n });\n }\n this._refreshAllSectionsToggler(this.reactive.stateManager.state);\n }\n\n /**\n * Regenerate content indexes.\n *\n * This method is used when a legacy action refresh some content element.\n */\n _indexContents() {\n // Find unindexed sections.\n this._scanIndex(\n this.selectors.SECTION,\n this.sections,\n (item) => {\n return new Section(item); // CHANGED.\n }\n );\n\n // Find unindexed cms.\n this._scanIndex(\n this.selectors.CM,\n this.cms,\n (item) => {\n return new CmItem(item); // CHANGED.\n }\n );\n }\n\n}"],"names":["Component","BaseComponent","create","descriptor","version","target","selectors","sectionReturn","this","element","document","getElementById","reactive","stateReady","state","_indexContents","addEventListener","_sectionTogglers","toogleAll","getElement","TOGGLEALL","collapseElementIds","getElements","COLLAPSE","map","id","setAttribute","join","_allSectionToggler","e","key","_refreshAllSectionsToggler","supportComponents","isEditing","DispatchActions","classList","add","classes","STATEDREADY","CourseEvents","manualCompletionToggled","_completionHandler","querySelector","PAGE","_scrollHandler","fmtCollapseOnHashChange","window","bind","event","anchor","location","hash","match","oldStyle","history","replaceState","selSectionHeaderDom","selSectionDom","closest","sectionId","getAttribute","section","get","matches","contentcollapsed","dispatch","scrollIntoView","preventDefault","isAllCollapsed","contains","COLLAPSED","sectionlist","sectionlistDom","querySelectorAll","sectionCount","length","push","dataset","allcollapsed","allexpanded","sectionCollapsible","forEach","style","display","remove","_refreshSectionNumber","SECTION","number","sectionid","inplace","inplaceeditable","getInplaceEditable","SECTION_ITEM","currentvalue","getValue","currentitemid","getItemId","timed","rawtitle","setValue","_refreshCourseSectionlist","param","sectionsDom","sdi","sectionDom","refreshCms","pageSectionDom","pageid","pageSectionDisplay","fmtonpage","visible","badgeDom","_refreshSectionCmlist","menuDom","render","controlmenu","done","html","replaceNode","stateManager","_scanIndex","sections","item","Section","CM","cms","CmItem"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"content.min.js","sources":["../../src/courseformat/content.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 main component.\n *\n * @module format_multitopic/courseformat/content\n * @class format_multitopic/courseformat/content\n * @copyright 2022 James Calder and Otago Polytechnic\n * @copyright based on work by 2020 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BaseComponent from 'core_courseformat/local/content';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport inplaceeditable from 'core/inplace_editable';\nimport Section from 'format_multitopic/courseformat/content/section';\nimport CmItem from 'format_multitopic/courseformat/content/section/cmitem';\nimport Templates from 'core/templates';\nimport DispatchActions from 'format_multitopic/courseformat/content/actions';\nimport * as CourseEvents from 'core_course/events';\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n *\n * @param {Object} descriptor the component descriptor\n */\n create(descriptor) {\n super.create(descriptor);\n this.version = descriptor.version;\n this.originalsinglesectionid = this.element.querySelector(\"ul.sections\").dataset.originalsinglesectionid;\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 * @param {number} sectionReturn the content section return\n * @param {number} version Moodle version number\n * @return {Component}\n */\n static init(target, selectors, sectionReturn, version) {\n return new this({ // CHANGED.\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n selectors,\n sectionReturn,\n version,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the state data\n */\n stateReady(state) {\n this._indexContents();\n // Activate section togglers.\n this.addEventListener(this.element, 'click', this._sectionTogglers);\n\n // Collapse/Expand all sections button.\n const toogleAll = this.getElement(this.selectors.TOGGLEALL);\n if (toogleAll) {\n\n // Ensure collapse menu button adds aria-controls attribute referring to each collapsible element.\n const collapseElements = this.getElements(this.selectors.COLLAPSE);\n const collapseElementIds = [...collapseElements].map(element => element.id);\n toogleAll.setAttribute('aria-controls', collapseElementIds.join(' '));\n\n this.addEventListener(toogleAll, 'click', this._allSectionToggler);\n this.addEventListener(toogleAll, 'keydown', e => {\n // Collapse/expand all sections when Space key is pressed on the toggle button.\n if (e.key === ' ') {\n this._allSectionToggler(e);\n }\n });\n this._refreshAllSectionsToggler(state);\n }\n\n if (this.reactive.supportComponents) {\n // Actions are only available in edit mode.\n if (this.reactive.isEditing) {\n new DispatchActions(this); // CHANGED.\n }\n\n // Mark content as state ready.\n this.element.classList.add(this.classes.STATEDREADY);\n }\n\n // Capture completion events.\n this.addEventListener(\n this.element,\n CourseEvents.manualCompletionToggled,\n this._completionHandler\n );\n\n // Capture page scroll to update page item.\n this.addEventListener(\n (this.version >= 2023081800) ? document : document.querySelector(this.selectors.PAGE),\n \"scroll\",\n this._scrollHandler\n );\n\n // Set the initial state of collapsible sections.\n this.fmtCollapseOnHashChange();\n\n // Capture clicks on course section links.\n window.addEventListener(\"hashchange\", this.fmtCollapseOnHashChange.bind(this));\n\n }\n\n /**\n * Expand, and scroll to, the section specified in the URL bar.\n *\n * @param {HashChangeEvent?} event The triggering event, if any\n */\n /* eslint-disable no-unused-vars */\n fmtCollapseOnHashChange(event) {\n /* eslint-enable no-unused-vars */\n\n // Find the specified section.\n let anchor = window.location.hash;\n if (!anchor.match(/^#sectionid-\\d+(?:-title)?$/)) {\n return;\n }\n let oldStyle = false;\n if (anchor.match(/^#sectionid-\\d+$/)) {\n anchor = anchor + \"-title\";\n oldStyle = true;\n history.replaceState(history.state, \"\", anchor);\n }\n const selSectionHeaderDom =\n document.querySelector(\".course-content ul.sections li.section.section-topic .sectionname\" + anchor);\n\n // Exit if there is no recognised section.\n if (!selSectionHeaderDom) {\n return;\n }\n\n const selSectionDom = selSectionHeaderDom.closest(\"li.section.section-topic\");\n const sectionId = selSectionDom.getAttribute('data-id');\n const section = this.reactive.get('section', sectionId);\n\n // Expand, if appropriate.\n if (selSectionDom.matches(\".section-topic-collapsible\")\n && (selSectionDom.querySelector(\".course-section-header .icons-collapse-expand.collapsed\")\n || section.contentcollapsed)) {\n this.reactive.dispatch(\n 'sectionContentCollapsed',\n [sectionId],\n false\n );\n }\n\n // Scroll to the specified section.\n if (oldStyle) {\n selSectionDom.scrollIntoView();\n }\n\n }\n\n /**\n * Handle the collapse/expand all sections button.\n *\n * Toggler click is delegated to the main course content element because new sections can\n * appear at any moment and this way we prevent accidental double bindings.\n *\n * @param {Event} event the triggered event\n */\n _allSectionToggler(event) {\n event.preventDefault();\n\n const target = event.target.closest(this.selectors.TOGGLEALL);\n const isAllCollapsed = target.classList.contains(this.classes.COLLAPSED);\n\n // CHANGED.\n let sectionlist = [];\n const sectionlistDom = this.element.querySelectorAll(\".course-section.section-topic-collapsible[data-fmtonpage='1']\");\n for (let sectionCount = 0; sectionCount < sectionlistDom.length; sectionCount++) {\n sectionlist.push(sectionlistDom[sectionCount].dataset.id);\n }\n // END CHANGED.\n\n this.reactive.dispatch(\n 'sectionContentCollapsed',\n sectionlist, // CHANGED.\n !isAllCollapsed\n );\n }\n\n /**\n * Refresh the collapse/expand all sections element.\n *\n * @param {Object} state The state data\n */\n _refreshAllSectionsToggler(state) {\n const target = this.getElement(this.selectors.TOGGLEALL);\n if (!target) {\n return;\n }\n // Check if we have all sections collapsed/expanded.\n let allcollapsed = true;\n let allexpanded = true;\n // ADDED.\n let sectionCollapsible = {};\n const sectionlistDom = this.element.querySelectorAll(\".course-section.section-topic-collapsible[data-fmtonpage='1']\");\n for (let sectionCount = 0; sectionCount < sectionlistDom.length; sectionCount++) {\n sectionCollapsible[sectionlistDom[sectionCount].dataset.id] = true;\n }\n // END ADDED.\n state.section.forEach(\n section => {\n if (sectionCollapsible[section.id]) { // ADDED.\n allcollapsed = allcollapsed && section.contentcollapsed;\n allexpanded = allexpanded && !section.contentcollapsed;\n }\n }\n );\n target.style.display = (allexpanded && allcollapsed) ? \"none\" : \"block\"; // ADDED.\n if (allcollapsed) {\n target.classList.add(this.classes.COLLAPSED);\n target.setAttribute('aria-expanded', false);\n }\n if (allexpanded) {\n target.classList.remove(this.classes.COLLAPSED);\n target.setAttribute('aria-expanded', true);\n }\n }\n\n /**\n * Update a course section when the section number changes.\n *\n * The courseActions module used for most course section tools still depends on css classes and\n * section numbers (not id). To prevent inconsistencies when a section is moved, we need to refresh\n * the\n *\n * Course formats can override the section title rendering so the frontend depends heavily on backend\n * rendering. Luckily in edit mode we can trigger a title update using the inplace_editable module.\n *\n * @param {Object} param\n * @param {Object} param.element details the update details.\n */\n _refreshSectionNumber({element}) {\n // Find the element.\n const target = this.getElement(this.selectors.SECTION, element.id);\n if (!target) {\n // Job done. Nothing to refresh.\n return;\n }\n // Update section numbers in all data, css and YUI attributes.\n target.id = `section-${element.number}`;\n // YUI uses section number as section id in data-sectionid, in principle if a format use components\n // don't need this sectionid attribute anymore, but we keep the compatibility in case some plugin\n // use it for legacy purposes.\n target.dataset.sectionid = element.number;\n // The data-number is the attribute used by components to store the section number.\n target.dataset.number = element.number;\n\n // Update title and title inplace editable, if any.\n const inplace = inplaceeditable.getInplaceEditable(target.querySelector(this.selectors.SECTION_ITEM));\n if (inplace) {\n // The course content HTML can be modified at any moment, so the function need to do some checkings\n // to make sure the inplace editable still represents the same itemid.\n const currentvalue = inplace.getValue();\n const currentitemid = inplace.getItemId();\n // Unnamed sections must be recalculated.\n if (inplace.getValue() === '' || element.timed) { // CHANGED.\n // The value to send can be an empty value if it is a default name.\n if (currentitemid == element.id\n && (currentvalue != element.rawtitle || element.rawtitle == '' || element.timed)) { // CHANGED.\n inplace.setValue(element.rawtitle);\n }\n }\n }\n }\n\n /**\n * Refresh the section list.\n *\n * @param {Object} param\n * @param {Object} param.element details the update details (Moodle <4.4).\n * @param {Object} param.state the full state object (Moodle >=4.4).\n */\n _refreshCourseSectionlist(param) {\n super._refreshCourseSectionlist(param);\n const originalSingleSection = this.reactive.get(\"section\", this.originalsinglesectionid);\n const singleSectionId = originalSingleSection ?\n (originalSingleSection.levelsan < 2 ? originalSingleSection.id : originalSingleSection.pageid)\n : null;\n const sectionsDom = this.element.querySelectorAll(this.selectors.SECTION);\n for (let sdi = 0; sdi < sectionsDom.length; sdi++) {\n const sectionDom = sectionsDom[sdi];\n const section = this.reactive.get(\"section\", sectionDom.dataset.id);\n if (!section) {\n continue;\n }\n let refreshCms = false;\n const pageSectionDisplay = (section.pageid == singleSectionId);\n if (sectionDom.dataset.fmtonpage != pageSectionDisplay) {\n sectionDom.dataset.fmtonpage = pageSectionDisplay;\n sectionDom.style.display = (pageSectionDisplay == \"1\") ? \"block\" : \"none\";\n if (pageSectionDisplay == \"1\") {\n refreshCms = true;\n }\n }\n if (section.visible == sectionDom.classList.contains(\"hidden\")) {\n const badgeDom = sectionDom.querySelector(\"span.badge[data-type='hiddenfromstudents']\");\n if (section.visible) {\n sectionDom.classList.remove(\"hidden\");\n if (badgeDom) {\n badgeDom.classList.add(\"d-none\");\n }\n } else {\n sectionDom.classList.add(\"hidden\");\n if (badgeDom) {\n badgeDom.classList.remove(\"d-none\");\n }\n }\n if (sectionDom.dataset.fmtonpage == \"1\") {\n refreshCms = true;\n }\n }\n if (refreshCms) {\n // Note: Visibility state doesn't get updated for CMs already rendered.\n this._refreshSectionCmlist({element: section});\n }\n const menuDom = sectionDom.querySelector(\".course-section-header .section_action_menu\");\n Templates.render(\"core_courseformat/local/content/section/controlmenu\", section.controlmenu).done(function(html) {\n Templates.replaceNode(menuDom, html, \"\");\n });\n }\n this._refreshAllSectionsToggler(this.reactive.stateManager.state);\n\n // Update Add section button if necessary.\n const addSectionDom = /*this.element*/document.querySelector(\"div#course-addsection > a\");\n if (addSectionDom.dataset.intoId != singleSectionId) {\n addSectionDom.dataset.intoId = singleSectionId;\n addSectionDom.href = addSectionDom.href.replace(/\\binsertparentid=\\d+\\b/, \"insertparentid=\" + singleSectionId);\n }\n\n }\n\n /**\n * Regenerate content indexes.\n *\n * This method is used when a legacy action refresh some content element.\n */\n _indexContents() {\n // Find unindexed sections.\n this._scanIndex(\n this.selectors.SECTION,\n this.sections,\n (item) => {\n return new Section(item); // CHANGED.\n }\n );\n\n // Find unindexed cms.\n this._scanIndex(\n this.selectors.CM,\n this.cms,\n (item) => {\n return new CmItem(item); // CHANGED.\n }\n );\n }\n\n}"],"names":["Component","BaseComponent","create","descriptor","version","originalsinglesectionid","this","element","querySelector","dataset","target","selectors","sectionReturn","document","getElementById","reactive","stateReady","state","_indexContents","addEventListener","_sectionTogglers","toogleAll","getElement","TOGGLEALL","collapseElementIds","getElements","COLLAPSE","map","id","setAttribute","join","_allSectionToggler","e","key","_refreshAllSectionsToggler","supportComponents","isEditing","DispatchActions","classList","add","classes","STATEDREADY","CourseEvents","manualCompletionToggled","_completionHandler","PAGE","_scrollHandler","fmtCollapseOnHashChange","window","bind","event","anchor","location","hash","match","oldStyle","history","replaceState","selSectionHeaderDom","selSectionDom","closest","sectionId","getAttribute","section","get","matches","contentcollapsed","dispatch","scrollIntoView","preventDefault","isAllCollapsed","contains","COLLAPSED","sectionlist","sectionlistDom","querySelectorAll","sectionCount","length","push","allcollapsed","allexpanded","sectionCollapsible","forEach","style","display","remove","_refreshSectionNumber","SECTION","number","sectionid","inplace","inplaceeditable","getInplaceEditable","SECTION_ITEM","currentvalue","getValue","currentitemid","getItemId","timed","rawtitle","setValue","_refreshCourseSectionlist","param","originalSingleSection","singleSectionId","levelsan","pageid","sectionsDom","sdi","sectionDom","refreshCms","pageSectionDisplay","fmtonpage","visible","badgeDom","_refreshSectionCmlist","menuDom","render","controlmenu","done","html","replaceNode","stateManager","addSectionDom","intoId","href","replace","_scanIndex","sections","item","Section","CM","cms","CmItem"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/courseformat/content/section.min.js b/amd/build/courseformat/content/section.min.js index dacf8e6..6497ea5 100644 --- a/amd/build/courseformat/content/section.min.js +++ b/amd/build/courseformat/content/section.min.js @@ -7,6 +7,6 @@ define("format_multitopic/courseformat/content/section",["exports","format_multi * @copyright 2022 James Calder and Otago Polytechnic * @copyright based on work by 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,_header=_interopRequireDefault(_header),_section=_interopRequireDefault(_section);class _default extends _section.default{stateReady(state){if(this.configState(state),this.reactive.isEditing&&this.reactive.supportComponents){const sectionItem=this.getElement(this.selectors.SECTION_ITEM);if(sectionItem){const headerComponent=new _header.default({...this,element:sectionItem,fullregion:this.element});this.configDragDrop(headerComponent)}}const pageSectionDom=document.querySelector(".course-section[data-id='"+this.section.pageid+"']");if(pageSectionDom){const pageSectionDisplay=pageSectionDom.dataset.fmtonpage;this.element.dataset.fmtonpage!=pageSectionDisplay&&(this.element.dataset.fmtonpage=pageSectionDisplay,this.element.style.display="1"==pageSectionDisplay?"block":"none")}}validateDropData(dropdata){if("section"===(null==dropdata?void 0:dropdata.type)){const origin=this.reactive.get("section",dropdata.id);return origin.id!=this.section.id&&origin.levelsan>=2&&(this.section.levelsan>=2||this.section.section>origin.section||this.course.draganddropsectionmoveafter)}return super.validateDropData(dropdata)}}return _exports.default=_default,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_header=_interopRequireDefault(_header),_section=_interopRequireDefault(_section);class _default extends _section.default{stateReady(state){if(this.configState(state),this.reactive.isEditing&&this.reactive.supportComponents){const sectionItem=this.getElement(this.selectors.SECTION_ITEM);if(sectionItem){const headerComponent=new _header.default({...this,element:sectionItem,fullregion:this.element});this.configDragDrop(headerComponent)}}const originalSingleSectionId=document.querySelector("ul.sections").dataset.originalsinglesectionid,originalSingleSection=this.reactive.get("section",originalSingleSectionId),singleSectionId=originalSingleSection?originalSingleSection.levelsan<2?originalSingleSection.id:originalSingleSection.pageid:null,pageSectionDisplay=this.section.pageid==singleSectionId;this.element.dataset.fmtonpage!=pageSectionDisplay&&(this.element.dataset.fmtonpage=pageSectionDisplay,this.element.style.display="1"==pageSectionDisplay?"block":"none")}validateDropData(dropdata){if("section"===(null==dropdata?void 0:dropdata.type)){const origin=this.reactive.get("section",dropdata.id);return origin.id!=this.section.id&&origin.levelsan>=2&&(this.section.levelsan>=2||this.section.section>origin.section||this.course.draganddropsectionmoveafter)}return super.validateDropData(dropdata)}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=section.min.js.map \ No newline at end of file diff --git a/amd/build/courseformat/content/section.min.js.map b/amd/build/courseformat/content/section.min.js.map index 67b7367..a24109b 100644 --- a/amd/build/courseformat/content/section.min.js.map +++ b/amd/build/courseformat/content/section.min.js.map @@ -1 +1 @@ -{"version":3,"file":"section.min.js","sources":["../../../src/courseformat/content/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 section format component.\n *\n * @module format_multitopic/courseformat/content/section\n * @class format_multitopic/courseformat/content/section\n * @copyright 2022 James Calder and Otago Polytechnic\n * @copyright based on work by 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Header from 'format_multitopic/courseformat/content/section/header';\nimport SectionBase from 'core_courseformat/local/content/section';\n\nexport default class extends SectionBase {\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the initial state\n */\n stateReady(state) {\n this.configState(state);\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Section zero and other formats sections may not have a title to drag.\n const sectionItem = this.getElement(this.selectors.SECTION_ITEM);\n if (sectionItem) {\n // Init the inner dragable element.\n const headerComponent = new Header({ // CHANGED.\n ...this,\n element: sectionItem,\n fullregion: this.element,\n });\n this.configDragDrop(headerComponent);\n }\n }\n\n const pageSectionDom = document.querySelector(\".course-section[data-id='\" + this.section.pageid + \"']\");\n if (pageSectionDom) {\n const pageSectionDisplay = pageSectionDom.dataset.fmtonpage;\n if (this.element.dataset.fmtonpage != pageSectionDisplay) {\n this.element.dataset.fmtonpage = pageSectionDisplay;\n this.element.style.display = (pageSectionDisplay == \"1\") ? \"block\" : \"none\";\n }\n }\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 if (dropdata?.type === 'section') {\n const origin = this.reactive.get(\"section\", dropdata.id);\n return origin.id != this.section.id && origin.levelsan >= 2\n && (this.section.levelsan >= 2 || this.section.section > origin.section\n || this.course.draganddropsectionmoveafter);\n }\n return super.validateDropData(dropdata);\n }\n\n}"],"names":["SectionBase","stateReady","state","configState","this","reactive","isEditing","supportComponents","sectionItem","getElement","selectors","SECTION_ITEM","headerComponent","Header","element","fullregion","configDragDrop","pageSectionDom","document","querySelector","section","pageid","pageSectionDisplay","dataset","fmtonpage","style","display","validateDropData","dropdata","type","origin","get","id","levelsan","course","draganddropsectionmoveafter","super"],"mappings":";;;;;;;;;8LA4B6BA,iBAOzBC,WAAWC,eACFC,YAAYD,OAEbE,KAAKC,SAASC,WAAaF,KAAKC,SAASE,kBAAmB,OAEtDC,YAAcJ,KAAKK,WAAWL,KAAKM,UAAUC,iBAC/CH,YAAa,OAEPI,gBAAkB,IAAIC,gBAAO,IAC5BT,KACHU,QAASN,YACTO,WAAYX,KAAKU,eAEhBE,eAAeJ,wBAItBK,eAAiBC,SAASC,cAAc,4BAA8Bf,KAAKgB,QAAQC,OAAS,SAC9FJ,eAAgB,OACVK,mBAAqBL,eAAeM,QAAQC,UAC9CpB,KAAKU,QAAQS,QAAQC,WAAaF,0BAC7BR,QAAQS,QAAQC,UAAYF,wBAC5BR,QAAQW,MAAMC,QAAiC,KAAtBJ,mBAA6B,QAAU,SAWjFK,iBAAiBC,aACU,aAAnBA,MAAAA,gBAAAA,SAAUC,MAAoB,OACxBC,OAAS1B,KAAKC,SAAS0B,IAAI,UAAWH,SAASI,WAC9CF,OAAOE,IAAM5B,KAAKgB,QAAQY,IAAMF,OAAOG,UAAY,IAC9C7B,KAAKgB,QAAQa,UAAY,GAAK7B,KAAKgB,QAAQA,QAAUU,OAAOV,SACzDhB,KAAK8B,OAAOC,oCAExBC,MAAMT,iBAAiBC"} \ No newline at end of file +{"version":3,"file":"section.min.js","sources":["../../../src/courseformat/content/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 section format component.\n *\n * @module format_multitopic/courseformat/content/section\n * @class format_multitopic/courseformat/content/section\n * @copyright 2022 James Calder and Otago Polytechnic\n * @copyright based on work by 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Header from 'format_multitopic/courseformat/content/section/header';\nimport SectionBase from 'core_courseformat/local/content/section';\n\nexport default class extends SectionBase {\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the initial state\n */\n stateReady(state) {\n this.configState(state);\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Section zero and other formats sections may not have a title to drag.\n const sectionItem = this.getElement(this.selectors.SECTION_ITEM);\n if (sectionItem) {\n // Init the inner dragable element.\n const headerComponent = new Header({ // CHANGED.\n ...this,\n element: sectionItem,\n fullregion: this.element,\n });\n this.configDragDrop(headerComponent);\n }\n }\n\n const originalSingleSectionId = document.querySelector(\"ul.sections\").dataset.originalsinglesectionid;\n const originalSingleSection = this.reactive.get(\"section\", originalSingleSectionId);\n const singleSectionId = originalSingleSection ?\n (originalSingleSection.levelsan < 2 ? originalSingleSection.id : originalSingleSection.pageid)\n : null;\n const pageSectionDisplay = (this.section.pageid == singleSectionId);\n if (this.element.dataset.fmtonpage != pageSectionDisplay) {\n this.element.dataset.fmtonpage = pageSectionDisplay;\n this.element.style.display = (pageSectionDisplay == \"1\") ? \"block\" : \"none\";\n }\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 if (dropdata?.type === 'section') {\n const origin = this.reactive.get(\"section\", dropdata.id);\n return origin.id != this.section.id && origin.levelsan >= 2\n && (this.section.levelsan >= 2 || this.section.section > origin.section\n || this.course.draganddropsectionmoveafter);\n }\n return super.validateDropData(dropdata);\n }\n\n}"],"names":["SectionBase","stateReady","state","configState","this","reactive","isEditing","supportComponents","sectionItem","getElement","selectors","SECTION_ITEM","headerComponent","Header","element","fullregion","configDragDrop","originalSingleSectionId","document","querySelector","dataset","originalsinglesectionid","originalSingleSection","get","singleSectionId","levelsan","id","pageid","pageSectionDisplay","section","fmtonpage","style","display","validateDropData","dropdata","type","origin","course","draganddropsectionmoveafter","super"],"mappings":";;;;;;;;;8LA4B6BA,iBAOzBC,WAAWC,eACFC,YAAYD,OAEbE,KAAKC,SAASC,WAAaF,KAAKC,SAASE,kBAAmB,OAEtDC,YAAcJ,KAAKK,WAAWL,KAAKM,UAAUC,iBAC/CH,YAAa,OAEPI,gBAAkB,IAAIC,gBAAO,IAC5BT,KACHU,QAASN,YACTO,WAAYX,KAAKU,eAEhBE,eAAeJ,wBAItBK,wBAA0BC,SAASC,cAAc,eAAeC,QAAQC,wBACxEC,sBAAwBlB,KAAKC,SAASkB,IAAI,UAAWN,yBACrDO,gBAAkBF,sBACCA,sBAAsBG,SAAW,EAAIH,sBAAsBI,GAAKJ,sBAAsBK,OACrF,KACpBC,mBAAsBxB,KAAKyB,QAAQF,QAAUH,gBAC/CpB,KAAKU,QAAQM,QAAQU,WAAaF,0BAC7Bd,QAAQM,QAAQU,UAAYF,wBAC5Bd,QAAQiB,MAAMC,QAAiC,KAAtBJ,mBAA6B,QAAU,QAU7EK,iBAAiBC,aACU,aAAnBA,MAAAA,gBAAAA,SAAUC,MAAoB,OACxBC,OAAShC,KAAKC,SAASkB,IAAI,UAAWW,SAASR,WAC9CU,OAAOV,IAAMtB,KAAKyB,QAAQH,IAAMU,OAAOX,UAAY,IAC9CrB,KAAKyB,QAAQJ,UAAY,GAAKrB,KAAKyB,QAAQA,QAAUO,OAAOP,SACzDzB,KAAKiC,OAAOC,oCAExBC,MAAMN,iBAAiBC"} \ No newline at end of file diff --git a/amd/build/courseformat/contenttabs/tabtree.min.js b/amd/build/courseformat/contenttabs/tabtree.min.js index 2a8a5a2..e37c4c6 100644 --- a/amd/build/courseformat/contenttabs/tabtree.min.js +++ b/amd/build/courseformat/contenttabs/tabtree.min.js @@ -8,6 +8,6 @@ define("format_multitopic/courseformat/contenttabs/tabtree",["exports","core/rea * @copyright 2023 James Calder and Otago Polytechnic * @copyright based on work by 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,_tab=_interopRequireDefault(_tab),_templates=_interopRequireDefault(_templates);class Component extends _reactive.BaseComponent{create(){this.name="contenttabs",this.selectors={TAB:"ul:first-of-type li",CHILDTAB:"ul:nth-child(2) li",SECTION_ITEM:"a.nav-link"},this.classes={ACTIVETAB:"active"},this.tabs={},this.childtabs={},this.activetab=[null,null]}static init(target){return new this({element:document.getElementById(target),reactive:(0,_courseeditor.getCurrentCourseEditor)()})}stateReady(){this._indexContents()}getWatchers(){return[{watch:"course.sectionlist:updated",handler:this._refreshCourseSectionTabs}]}async _refreshCourseSectionTabs(_ref){var _element$firstsection,_element$secondsectio;let{element:element}=_ref,tabsSecondRowDom=this.element.querySelector("ul:nth-of-type(2)");const tabsSecondRowShow=element.firstsectionlist.length>1||element.secondsectionlist.length>1;if(tabsSecondRowShow&&!tabsSecondRowDom){this.element.querySelector("ul:first-of-type").insertAdjacentElement("afterend",tabsSecondRowDom=document.createElement("ul")),tabsSecondRowDom.className="nav nav-tabs mb-3";const activeTab0Dom=this.element.querySelector("ul:first-of-type li:first-of-type");let data={sectionid:activeTab0Dom.dataset.id,level:1,active:!0,inactive:!1,title:activeTab0Dom.querySelector("a").getAttribute("title"),text:'
'+activeTab0Dom.querySelector("a").getAttribute("title")+"
"},item=document.createElement("li");tabsSecondRowDom.insertAdjacentElement("afterbegin",item);let html=await _templates.default.render("format_multitopic/courseformat/contenttabs/tab",data);item=_templates.default.replaceNode(item,html,"")[0];const addTab0Dom=this.element.querySelector("ul:first-of-type li:last-of-type");data={level:1,active:!1,inactive:!1,link:[{link:addTab0Dom.querySelector("a").getAttribute("href").replace(/\binsertlevel=0\b/,"insertlevel=1")}],title:addTab0Dom.getAttribute("title"),text:''},item=document.createElement("li"),tabsSecondRowDom.insertAdjacentElement("beforeend",item),html=await _templates.default.render("format_multitopic/courseformat/contenttabs/tab",data),item=_templates.default.replaceNode(item,html,"")[0]}else tabsSecondRowDom&&!tabsSecondRowShow&&tabsSecondRowDom.remove();const activeTab1=this.reactive.get("section",this.activetab[1]);let newActiveTab0id=activeTab1.levelsan>=1?activeTab1.parentid:activeTab1.id;if(newActiveTab0id!=this.activetab[0]){let section=this.reactive.get("section",this.activetab[0]),anchor=this.element.querySelector('ul:first-of-type div[data-itemid="'+this.activetab[0]+'"]').parentElement;anchor.classList.remove("active"),anchor.href=section.sectionurl.replace("&","&"),this.activetab[0]=newActiveTab0id,anchor=this.element.querySelector('ul:first-of-type div[data-itemid="'+this.activetab[0]+'"]').parentElement,anchor.classList.add("active"),anchor.removeAttribute("href");const addAnchor=this.element.querySelector("ul:nth-of-type(2) li:last-of-type a"),addLink=addAnchor.href.replace(/\binsertparentid=\d+\b/,"insertparentid="+this.activetab[0]);addAnchor.setAttribute("href",addLink)}const toptabslist=null!==(_element$firstsection=element.firstsectionlist)&&void 0!==_element$firstsection?_element$firstsection:[],childtabslist=null!==(_element$secondsectio=element.secondsectionlist)&&void 0!==_element$secondsectio?_element$secondsectio:[];let toptabs=this.element.querySelector("ul:first-of-type");await this._fixOrder(toptabs,toptabslist,this.selectors.TAB,0,childtabslist[this.activetab[0]].length>1);let childtabs=this.element.querySelector("ul:nth-of-type(2)");childtabs&&await this._fixOrder(childtabs,childtabslist[this.activetab[0]],this.selectors.CHILDTAB,1,!1),this._indexContents()}_indexContents(){this._scanIndex(this.selectors.TAB,this.tabs,(item=>new _tab.default(item)),0),this._scanIndex(this.selectors.CHILDTAB,this.childtabs,(item=>new _tab.default(item)),1)}_scanIndex(selector,index,creationhandler,level){this.getElements("".concat(selector,":not([data-indexed])")).forEach((item=>{var _item$dataset;if(null==item||null===(_item$dataset=item.dataset)||void 0===_item$dataset||!_item$dataset.id)return;void 0!==index[item.dataset.id]&&index[item.dataset.id].unregister(),index[item.dataset.id]=creationhandler({...this,element:item}),-1!==item.querySelector("a").classList.value.indexOf(this.classes.ACTIVETAB)&&(level<=0&&(this.activetab[0]=item.dataset.id),this.activetab[1]=item.dataset.id),item.dataset.indexed=!0}))}async _fixOrder(container,neworder,selector,level,hassubtree){if(!neworder.length)return container.classList.add("hidden"),void(container.innerHTML="");container.classList.remove("hidden");for(const[index,itemid]of Object.entries(neworder)){const section=this.reactive.get("section",itemid),visible=(section.visible&§ion.available||0==section.section)&&(neworder.length>1||hassubtree),current=null!=section.currentnestedlevel&§ion.currentnestedlevel>=level;let item=this.getElement(selector,itemid);if(null===item){let data={sectionid:itemid,level:level,active:0,inactive:0,link:[{link:section.sectionurl}],title:section.name,text:'
'+section.title+"
"};item=document.createElement("li"),container.insertBefore(item,container.lastElementChild);let html=await _templates.default.render("format_multitopic/courseformat/contenttabs/tab",data);item=_templates.default.replaceNode(item,html,"")[0]}const content=item.querySelector("div.tab_content");content&&content.classList.contains("dimmed")==visible&&(visible?content.classList.remove("dimmed"):content.classList.add("dimmed")),content&&content.classList.contains("marker")!=current&&(current?content.classList.add("marker"):content.classList.remove("marker"));const currentitem=container.children[index];if(void 0===currentitem)return void container.append(item);currentitem!==item&&container.insertBefore(item,currentitem)}for(;container.children.length>neworder.length+1;)container.removeChild(container.lastElementChild.previousSibling)}}return _exports.default=Component,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_tab=_interopRequireDefault(_tab),_templates=_interopRequireDefault(_templates);class Component extends _reactive.BaseComponent{create(){this.name="contenttabs",this.selectors={TAB:"ul:first-of-type li",CHILDTAB:"ul:nth-child(2) li",SECTION_ITEM:"a.nav-link"},this.classes={ACTIVETAB:"active"},this.tabs={},this.childtabs={},this.activetab=[null,null],this.originalsinglesectionid=document.querySelector("ul.sections").dataset.originalsinglesectionid}static init(target){return new this({element:document.getElementById(target),reactive:(0,_courseeditor.getCurrentCourseEditor)()})}stateReady(){this._indexContents()}getWatchers(){return[{watch:"course.sectionlist:updated",handler:this._refreshCourseSectionTabs}]}async _refreshCourseSectionTabs(_ref){var _element$firstsection,_element$secondsectio;let{element:element}=_ref;const originalSingleSection=this.reactive.get("section",this.originalsinglesectionid);let singleSectionId,singleSection;originalSingleSection?(singleSectionId=originalSingleSection.levelsan<2?originalSingleSection.id:originalSingleSection.pageid,singleSection=singleSectionId==originalSingleSection.id?originalSingleSection:this.reactive.get("section",singleSectionId)):(singleSectionId=null,singleSection=null);let newActiveTab0id=singleSection?singleSection.levelsan>=1?singleSection.parentid:singleSection.id:null,tabsSecondRowDom=this.element.querySelector("ul:nth-of-type(2)");const tabsSecondRowShow=singleSection&&(element.firstsectionlist.length>1||element.secondsectionlist.length>1);if(tabsSecondRowShow&&!tabsSecondRowDom){this.element.querySelector("ul:first-of-type").insertAdjacentElement("afterend",tabsSecondRowDom=document.createElement("ul")),tabsSecondRowDom.className="nav nav-tabs mb-3";const activeTab0Dom=this.element.querySelector("ul:first-of-type li:first-of-type");let data={sectionid:activeTab0Dom.dataset.id,level:1,active:!0,inactive:!1,title:activeTab0Dom.querySelector("a").getAttribute("title"),text:'
'+activeTab0Dom.querySelector("a").getAttribute("title")+"
"},item=document.createElement("li");tabsSecondRowDom.insertAdjacentElement("afterbegin",item);let html=await _templates.default.render("format_multitopic/courseformat/contenttabs/tab",data);item=_templates.default.replaceNode(item,html,"")[0];const addTab0Dom=this.element.querySelector("ul:first-of-type li:last-of-type");data={level:1,active:!1,inactive:!1,link:[{link:addTab0Dom.querySelector("a").getAttribute("href").replace(/\binsertlevel=0\b/,"insertlevel=1")}],title:addTab0Dom.getAttribute("title"),text:''},item=document.createElement("li"),tabsSecondRowDom.insertAdjacentElement("beforeend",item),html=await _templates.default.render("format_multitopic/courseformat/contenttabs/tab",data),item=_templates.default.replaceNode(item,html,"")[0]}else tabsSecondRowDom&&!tabsSecondRowShow&&tabsSecondRowDom.remove();const toptabslist=null!==(_element$firstsection=element.firstsectionlist)&&void 0!==_element$firstsection?_element$firstsection:[],childtabslist=null!==(_element$secondsectio=element.secondsectionlist)&&void 0!==_element$secondsectio?_element$secondsectio:[];let toptabs=this.element.querySelector("ul:first-of-type");if(await this._fixOrder(toptabs,toptabslist,this.selectors.TAB,0,childtabslist[toptabslist[0]].length>1),tabsSecondRowShow){let childtabs=this.element.querySelector("ul:nth-of-type(2)");childtabs&&await this._fixOrder(childtabs,childtabslist[newActiveTab0id],this.selectors.CHILDTAB,1,!1)}if(newActiveTab0id!=this.activetab[0]){var _this$element$querySe,_this$element$querySe2;let anchor=null===(_this$element$querySe=this.element.querySelector('ul:first-of-type div[data-itemid="'+this.activetab[0]+'"]'))||void 0===_this$element$querySe?void 0:_this$element$querySe.parentElement;if(anchor){let section=this.reactive.get("section",this.activetab[0]);anchor.classList.remove("active"),anchor.href=section.sectionurl.replace("&","&")}if(this.activetab[0]=newActiveTab0id,anchor=null===(_this$element$querySe2=this.element.querySelector('ul:first-of-type div[data-itemid="'+this.activetab[0]+'"]'))||void 0===_this$element$querySe2?void 0:_this$element$querySe2.parentElement,anchor&&(anchor.classList.add("active"),anchor.removeAttribute("href")),tabsSecondRowShow){const addAnchor=this.element.querySelector("ul:nth-of-type(2) li:last-of-type a"),addLink=addAnchor.href.replace(/\binsertparentid=\d+\b/,"insertparentid="+this.activetab[0]);addAnchor.setAttribute("href",addLink)}}if(tabsSecondRowShow&&singleSection.id!=this.activetab[1]){var _this$element$querySe3,_this$element$querySe4;let anchor=null===(_this$element$querySe3=this.element.querySelector('ul:nth-of-type(2) div[data-itemid="'+this.activetab[1]+'"]'))||void 0===_this$element$querySe3?void 0:_this$element$querySe3.parentElement;if(anchor){let section=this.reactive.get("section",this.activetab[1]);anchor.classList.remove("active"),anchor.href=section.sectionurl.replace("&","&")}this.activetab[1]=singleSection.id,anchor=null===(_this$element$querySe4=this.element.querySelector('ul:nth-of-type(2) div[data-itemid="'+this.activetab[1]+'"]'))||void 0===_this$element$querySe4?void 0:_this$element$querySe4.parentElement,anchor&&(anchor.classList.add("active"),anchor.removeAttribute("href"))}this._indexContents()}_indexContents(){this._scanIndex(this.selectors.TAB,this.tabs,(item=>new _tab.default(item)),0),this._scanIndex(this.selectors.CHILDTAB,this.childtabs,(item=>new _tab.default(item)),1)}_scanIndex(selector,index,creationhandler,level){this.getElements("".concat(selector,":not([data-indexed])")).forEach((item=>{var _item$dataset;if(null==item||null===(_item$dataset=item.dataset)||void 0===_item$dataset||!_item$dataset.id)return;void 0!==index[item.dataset.id]&&index[item.dataset.id].unregister(),index[item.dataset.id]=creationhandler({...this,element:item}),-1!==item.querySelector("a").classList.value.indexOf(this.classes.ACTIVETAB)&&(level<=0&&(this.activetab[0]=item.dataset.id),this.activetab[1]=item.dataset.id),item.dataset.indexed=!0}))}async _fixOrder(container,neworder,selector,level,hassubtree){if(!neworder.length)return container.classList.add("hidden"),void(container.innerHTML="");container.classList.remove("hidden");for(const[index,itemid]of Object.entries(neworder)){const section=this.reactive.get("section",itemid),visible=(section.visible&§ion.available||0==section.section)&&(neworder.length>1||hassubtree),current=null!=section.currentnestedlevel&§ion.currentnestedlevel>=level;let item=this.getElement(selector,itemid);if(null===item){let data={sectionid:itemid,level:level,active:0,inactive:0,link:[{link:section.sectionurl}],title:section.name,text:'
'+section.title+"
"};item=document.createElement("li"),container.insertBefore(item,container.lastElementChild);let html=await _templates.default.render("format_multitopic/courseformat/contenttabs/tab",data);item=_templates.default.replaceNode(item,html,"")[0]}const content=item.querySelector("div.tab_content");content&&content.classList.contains("dimmed")==visible&&(visible?content.classList.remove("dimmed"):content.classList.add("dimmed")),content&&content.classList.contains("marker")!=current&&(current?content.classList.add("marker"):content.classList.remove("marker"));const currentitem=container.children[index];if(void 0===currentitem)return void container.append(item);currentitem!==item&&container.insertBefore(item,currentitem)}for(;container.children.length>neworder.length+1;)container.removeChild(container.lastElementChild.previousSibling)}}return _exports.default=Component,_exports.default})); //# sourceMappingURL=tabtree.min.js.map \ No newline at end of file diff --git a/amd/build/courseformat/contenttabs/tabtree.min.js.map b/amd/build/courseformat/contenttabs/tabtree.min.js.map index 7ca90c6..e91f1e2 100644 --- a/amd/build/courseformat/contenttabs/tabtree.min.js.map +++ b/amd/build/courseformat/contenttabs/tabtree.min.js.map @@ -1 +1 @@ -{"version":3,"file":"tabtree.min.js","sources":["../../../src/courseformat/contenttabs/tabtree.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\nimport {BaseComponent} from 'core/reactive';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport Tab from 'format_multitopic/courseformat/contenttabs/tab';\nimport Templates from 'core/templates';\n\n\n/**\n * Course section tabs updater.\n *\n * @module format_multitopic/courseformat/contenttabs/tabtree\n * @class format_multitopic/courseformat/contenttabs/tabtree\n * @copyright 2022 Jeremy FitzPatrick and Te Wānanga o Aotearoa\n * @copyright 2023 James Calder and Otago Polytechnic\n * @copyright based on work by 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'contenttabs';\n // Default query selectors.\n this.selectors = {\n TAB: `ul:first-of-type li`,\n CHILDTAB: `ul:nth-child(2) li`,\n SECTION_ITEM: `a.nav-link`,\n };\n // Default classes\n this.classes = {\n ACTIVETAB: 'active'\n };\n // Objects to keep tabs on the tabs\n this.tabs = {};\n this.childtabs = {};\n this.activetab = [null, null];\n }\n\n static init(target) {\n return new this({\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n });\n }\n\n /**\n * Initial state ready method.\n *\n */\n stateReady() {\n this._indexContents();\n }\n\n getWatchers() {\n return [\n // Sections sorting.\n {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionTabs},\n ];\n }\n\n /**\n * Refresh the section tabs.\n *\n * @param {object} param\n * @param {Object} param.element\n */\n async _refreshCourseSectionTabs({element}) {\n\n // Add/remove the second-level tabs, if necessary.\n let tabsSecondRowDom = this.element.querySelector('ul:nth-of-type(2)');\n const tabsSecondRowShow = (element.firstsectionlist.length > 1) || (element.secondsectionlist.length > 1);\n if (tabsSecondRowShow && !tabsSecondRowDom) {\n // Create tab row.\n this.element.querySelector('ul:first-of-type').insertAdjacentElement(\n 'afterend', tabsSecondRowDom = document.createElement('ul')\n );\n tabsSecondRowDom.className = 'nav nav-tabs mb-3';\n // Create index tab.\n const activeTab0Dom = this.element.querySelector('ul:first-of-type li:first-of-type');\n let data = {\n \"sectionid\": activeTab0Dom.dataset.id,\n \"level\": 1,\n \"active\": true,\n \"inactive\": false,\n \"title\": activeTab0Dom.querySelector('a').getAttribute('title'),\n \"text\": '
'\n + activeTab0Dom.querySelector('a').getAttribute('title') + '
',\n };\n let item = document.createElement(\"li\");\n tabsSecondRowDom.insertAdjacentElement('afterbegin', item);\n let html = await Templates.render(\"format_multitopic/courseformat/contenttabs/tab\", data);\n item = Templates.replaceNode(item, html, \"\")[0];\n // Create add tab.\n const addTab0Dom = this.element.querySelector('ul:first-of-type li:last-of-type');\n data = {\n \"level\": 1,\n \"active\": false,\n \"inactive\": false,\n \"link\": [{\n \"link\": addTab0Dom.querySelector('a').getAttribute('href').replace(/\\binsertlevel=0\\b/, 'insertlevel=1'),\n }],\n \"title\": addTab0Dom.getAttribute('title'),\n \"text\": '',\n };\n item = document.createElement(\"li\");\n tabsSecondRowDom.insertAdjacentElement('beforeend', item);\n html = await Templates.render(\"format_multitopic/courseformat/contenttabs/tab\", data);\n item = Templates.replaceNode(item, html, \"\")[0];\n } else if (tabsSecondRowDom && !tabsSecondRowShow) {\n tabsSecondRowDom.remove();\n }\n\n // Change the active top-level tab, if necessary.\n const activeTab1 = this.reactive.get('section', this.activetab[1]);\n let newActiveTab0id = (activeTab1.levelsan >= 1) ? activeTab1.parentid : activeTab1.id;\n if (newActiveTab0id != this.activetab[0]) {\n let section = this.reactive.get(\"section\", this.activetab[0]);\n let anchor = this.element.querySelector('ul:first-of-type div[data-itemid=\"' + this.activetab[0] + '\"]').parentElement;\n anchor.classList.remove(\"active\");\n anchor.href = section.sectionurl.replace(\"&\", \"&\");\n this.activetab[0] = newActiveTab0id;\n anchor = this.element.querySelector('ul:first-of-type div[data-itemid=\"' + this.activetab[0] + '\"]').parentElement;\n anchor.classList.add(\"active\");\n anchor.removeAttribute(\"href\");\n const addAnchor = this.element.querySelector('ul:nth-of-type(2) li:last-of-type a');\n const addLink = addAnchor.href.replace(/\\binsertparentid=\\d+\\b/, \"insertparentid=\" + this.activetab[0]);\n addAnchor.setAttribute(\"href\", addLink);\n }\n\n // Do things that make the first row tabs match firstsectionlist.\n const toptabslist = element.firstsectionlist ?? [];\n const childtabslist = element.secondsectionlist ?? [];\n let toptabs = this.element.querySelector('ul:first-of-type');\n await this._fixOrder(toptabs, toptabslist, this.selectors.TAB, 0, childtabslist[this.activetab[0]].length > 1);\n\n // And the second row tabs match secondsectionlist.\n let childtabs = this.element.querySelector('ul:nth-of-type(2)');\n if (childtabs) {\n await this._fixOrder(childtabs, childtabslist[this.activetab[0]], this.selectors.CHILDTAB, 1, false);\n }\n\n this._indexContents();\n }\n\n /**\n * Regenerate content indexes.\n *\n * This method is used when a legacy action refresh some content element.\n */\n _indexContents() {\n // Find unindexed tabs.\n this._scanIndex(\n this.selectors.TAB,\n this.tabs,\n (item) => {\n return new Tab(item);\n },\n 0\n );\n\n // Find unindexed child tabs.\n this._scanIndex(\n this.selectors.CHILDTAB,\n this.childtabs,\n (item) => {\n return new Tab(item);\n },\n 1\n );\n }\n\n /**\n * Reindex a tab.\n *\n * This method is used internally by _indexContents.\n *\n * @param {string} selector the DOM selector to scan\n * @param {*} index the index attribute to update\n * @param {*} creationhandler method to create a new indexed element\n * @param {int} level tab level\n */\n _scanIndex(selector, index, creationhandler, level) {\n const items = this.getElements(`${selector}:not([data-indexed])`);\n items.forEach((item) => {\n if (!item?.dataset?.id) {\n return;\n }\n // Delete previous item component.\n if (index[item.dataset.id] !== undefined) {\n index[item.dataset.id].unregister();\n }\n // Create the new component.\n index[item.dataset.id] = creationhandler({\n ...this,\n element: item,\n });\n // Update selected tab\n let classes = item.querySelector(\"a\").classList.value;\n if (classes.indexOf(this.classes.ACTIVETAB) !== -1) {\n if (level <= 0) {\n this.activetab[0] = item.dataset.id;\n }\n this.activetab[1] = item.dataset.id;\n }\n // Mark as indexed.\n item.dataset.indexed = true;\n });\n }\n\n /**\n * Fix/reorder the section or cms order.\n *\n * @param {Element} container the HTML element to reorder.\n * @param {Array} neworder an array with the ids order\n * @param {string} selector the element selector\n * @param {int} level the tab level\n * @param {boolean} hassubtree\n */\n async _fixOrder(container, neworder, selector, level, hassubtree) {\n\n // Empty lists should not be visible.\n if (!neworder.length) {\n container.classList.add('hidden');\n container.innerHTML = '';\n return;\n }\n\n // Grant the list is visible (in case it was empty).\n container.classList.remove('hidden');\n\n // Move the elements in order at the beginning of the list.\n for (const [index, itemid] of Object.entries(neworder)) {\n const section = this.reactive.get(\"section\", itemid);\n const visible = (section.visible && section.available || section.section == 0)\n && (neworder.length > 1 || hassubtree);\n const current = (section.currentnestedlevel != undefined && section.currentnestedlevel >= level);\n let item = this.getElement(selector, itemid);\n if (item === null) {\n // If we don't have an item, create it.\n let data = {\n \"sectionid\": itemid,\n \"level\": level,\n \"active\": 0,\n \"inactive\": 0,\n \"link\": [{\n \"link\": section.sectionurl\n }],\n \"title\": section.name,\n \"text\": '
' + section.title + '
'\n };\n item = document.createElement(\"li\");\n container.insertBefore(item, container.lastElementChild);\n let html = await Templates.render(\"format_multitopic/courseformat/contenttabs/tab\", data);\n item = Templates.replaceNode(item, html, \"\")[0];\n }\n\n // Update visibility & current marker\n const content = item.querySelector(\"div.tab_content\");\n if (content && content.classList.contains(\"dimmed\") == visible) {\n if (visible) {\n content.classList.remove(\"dimmed\");\n } else {\n content.classList.add(\"dimmed\");\n }\n }\n if (content && content.classList.contains(\"marker\") != current) {\n if (current) {\n content.classList.add(\"marker\");\n } else {\n content.classList.remove(\"marker\");\n }\n }\n\n // Get the current element at that position.\n const currentitem = container.children[index];\n if (currentitem === undefined) {\n container.append(item);\n return;\n }\n if (currentitem !== item) {\n container.insertBefore(item, currentitem);\n }\n }\n // Remove the remaining elements.\n // But we don't want the \"Add\" blown away.\n while (container.children.length > neworder.length + 1) {\n container.removeChild(container.lastElementChild.previousSibling);\n }\n\n }\n\n}"],"names":["Component","BaseComponent","create","name","selectors","TAB","CHILDTAB","SECTION_ITEM","classes","ACTIVETAB","tabs","childtabs","activetab","target","this","element","document","getElementById","reactive","stateReady","_indexContents","getWatchers","watch","handler","_refreshCourseSectionTabs","tabsSecondRowDom","querySelector","tabsSecondRowShow","firstsectionlist","length","secondsectionlist","insertAdjacentElement","createElement","className","activeTab0Dom","data","dataset","id","getAttribute","item","html","Templates","render","replaceNode","addTab0Dom","replace","remove","activeTab1","get","newActiveTab0id","levelsan","parentid","section","anchor","parentElement","classList","href","sectionurl","add","removeAttribute","addAnchor","addLink","setAttribute","toptabslist","childtabslist","toptabs","_fixOrder","_scanIndex","Tab","selector","index","creationhandler","level","getElements","forEach","_item$dataset","undefined","unregister","value","indexOf","indexed","container","neworder","hassubtree","innerHTML","itemid","Object","entries","visible","available","current","currentnestedlevel","getElement","title","insertBefore","lastElementChild","content","contains","currentitem","children","append","removeChild","previousSibling"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"tabtree.min.js","sources":["../../../src/courseformat/contenttabs/tabtree.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\nimport {BaseComponent} from 'core/reactive';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport Tab from 'format_multitopic/courseformat/contenttabs/tab';\nimport Templates from 'core/templates';\n\n\n/**\n * Course section tabs updater.\n *\n * @module format_multitopic/courseformat/contenttabs/tabtree\n * @class format_multitopic/courseformat/contenttabs/tabtree\n * @copyright 2022 Jeremy FitzPatrick and Te Wānanga o Aotearoa\n * @copyright 2023 James Calder and Otago Polytechnic\n * @copyright based on work by 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'contenttabs';\n // Default query selectors.\n this.selectors = {\n TAB: `ul:first-of-type li`,\n CHILDTAB: `ul:nth-child(2) li`,\n SECTION_ITEM: `a.nav-link`,\n };\n // Default classes\n this.classes = {\n ACTIVETAB: 'active'\n };\n // Objects to keep tabs on the tabs\n this.tabs = {};\n this.childtabs = {};\n this.activetab = [null, null];\n\n this.originalsinglesectionid = document.querySelector(\"ul.sections\").dataset.originalsinglesectionid;\n }\n\n static init(target) {\n return new this({\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n });\n }\n\n /**\n * Initial state ready method.\n *\n */\n stateReady() {\n this._indexContents();\n }\n\n getWatchers() {\n return [\n // Sections sorting.\n {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionTabs},\n ];\n }\n\n /**\n * Refresh the section tabs.\n *\n * @param {object} param\n * @param {Object} param.element\n */\n async _refreshCourseSectionTabs({element}) {\n\n const originalSingleSection = this.reactive.get(\"section\", this.originalsinglesectionid);\n let singleSectionId;\n let singleSection;\n if (originalSingleSection) {\n singleSectionId = (originalSingleSection.levelsan < 2 ? originalSingleSection.id : originalSingleSection.pageid);\n singleSection = (singleSectionId == originalSingleSection.id) ?\n originalSingleSection : this.reactive.get(\"section\", singleSectionId);\n } else {\n singleSectionId = null;\n singleSection = null;\n }\n\n let newActiveTab0id = singleSection ?\n ((singleSection.levelsan >= 1) ? singleSection.parentid : singleSection.id)\n : null;\n\n // Add/remove the second-level tabs, if necessary.\n let tabsSecondRowDom = this.element.querySelector('ul:nth-of-type(2)');\n const tabsSecondRowShow = singleSection\n && ((element.firstsectionlist.length > 1) || (element.secondsectionlist.length > 1));\n if (tabsSecondRowShow && !tabsSecondRowDom) {\n // Create tab row.\n this.element.querySelector('ul:first-of-type').insertAdjacentElement(\n 'afterend', tabsSecondRowDom = document.createElement('ul')\n );\n tabsSecondRowDom.className = 'nav nav-tabs mb-3';\n // Create index tab.\n const activeTab0Dom = this.element.querySelector('ul:first-of-type li:first-of-type');\n let data = {\n \"sectionid\": activeTab0Dom.dataset.id,\n \"level\": 1,\n \"active\": true,\n \"inactive\": false,\n \"title\": activeTab0Dom.querySelector('a').getAttribute('title'),\n \"text\": '
'\n + activeTab0Dom.querySelector('a').getAttribute('title') + '
',\n };\n let item = document.createElement(\"li\");\n tabsSecondRowDom.insertAdjacentElement('afterbegin', item);\n let html = await Templates.render(\"format_multitopic/courseformat/contenttabs/tab\", data);\n item = Templates.replaceNode(item, html, \"\")[0];\n // Create add tab.\n const addTab0Dom = this.element.querySelector('ul:first-of-type li:last-of-type');\n data = {\n \"level\": 1,\n \"active\": false,\n \"inactive\": false,\n \"link\": [{\n \"link\": addTab0Dom.querySelector('a').getAttribute('href').replace(/\\binsertlevel=0\\b/, 'insertlevel=1'),\n }],\n \"title\": addTab0Dom.getAttribute('title'),\n \"text\": '',\n };\n item = document.createElement(\"li\");\n tabsSecondRowDom.insertAdjacentElement('beforeend', item);\n html = await Templates.render(\"format_multitopic/courseformat/contenttabs/tab\", data);\n item = Templates.replaceNode(item, html, \"\")[0];\n } else if (tabsSecondRowDom && !tabsSecondRowShow) {\n tabsSecondRowDom.remove();\n }\n\n // Do things that make the first row tabs match firstsectionlist.\n const toptabslist = element.firstsectionlist ?? [];\n const childtabslist = element.secondsectionlist ?? [];\n let toptabs = this.element.querySelector('ul:first-of-type');\n await this._fixOrder(toptabs, toptabslist, this.selectors.TAB, 0, childtabslist[toptabslist[0]].length > 1);\n\n // And the second row tabs match secondsectionlist.\n if (tabsSecondRowShow) {\n let childtabs = this.element.querySelector('ul:nth-of-type(2)');\n if (childtabs) {\n await this._fixOrder(childtabs, childtabslist[newActiveTab0id], this.selectors.CHILDTAB, 1, false);\n }\n }\n\n // Change the active top-level tab, if necessary.\n if (newActiveTab0id != this.activetab[0]) {\n let anchor = this.element.querySelector('ul:first-of-type div[data-itemid=\"' + this.activetab[0] + '\"]')?.parentElement;\n if (anchor) {\n let section = this.reactive.get(\"section\", this.activetab[0]);\n anchor.classList.remove(\"active\");\n anchor.href = section.sectionurl.replace(\"&\", \"&\");\n }\n this.activetab[0] = newActiveTab0id;\n anchor = this.element.querySelector('ul:first-of-type div[data-itemid=\"' + this.activetab[0] + '\"]')?.parentElement;\n if (anchor) {\n anchor.classList.add(\"active\");\n anchor.removeAttribute(\"href\");\n }\n if (tabsSecondRowShow) {\n const addAnchor = this.element.querySelector('ul:nth-of-type(2) li:last-of-type a');\n const addLink = addAnchor.href.replace(/\\binsertparentid=\\d+\\b/, \"insertparentid=\" + this.activetab[0]);\n addAnchor.setAttribute(\"href\", addLink);\n }\n }\n\n // Change the active second-level tab, if necessary.\n if (tabsSecondRowShow && singleSection.id != this.activetab[1]) {\n let anchor = this.element.querySelector('ul:nth-of-type(2) div[data-itemid=\"' + this.activetab[1] + '\"]')\n ?.parentElement;\n if (anchor) {\n let section = this.reactive.get(\"section\", this.activetab[1]);\n anchor.classList.remove(\"active\");\n anchor.href = section.sectionurl.replace(\"&\", \"&\");\n }\n this.activetab[1] = singleSection.id;\n anchor = this.element.querySelector('ul:nth-of-type(2) div[data-itemid=\"' + this.activetab[1] + '\"]')?.parentElement;\n if (anchor) {\n anchor.classList.add(\"active\");\n anchor.removeAttribute(\"href\");\n }\n }\n\n this._indexContents();\n }\n\n /**\n * Regenerate content indexes.\n *\n * This method is used when a legacy action refresh some content element.\n */\n _indexContents() {\n // Find unindexed tabs.\n this._scanIndex(\n this.selectors.TAB,\n this.tabs,\n (item) => {\n return new Tab(item);\n },\n 0\n );\n\n // Find unindexed child tabs.\n this._scanIndex(\n this.selectors.CHILDTAB,\n this.childtabs,\n (item) => {\n return new Tab(item);\n },\n 1\n );\n }\n\n /**\n * Reindex a tab.\n *\n * This method is used internally by _indexContents.\n *\n * @param {string} selector the DOM selector to scan\n * @param {*} index the index attribute to update\n * @param {*} creationhandler method to create a new indexed element\n * @param {int} level tab level\n */\n _scanIndex(selector, index, creationhandler, level) {\n const items = this.getElements(`${selector}:not([data-indexed])`);\n items.forEach((item) => {\n if (!item?.dataset?.id) {\n return;\n }\n // Delete previous item component.\n if (index[item.dataset.id] !== undefined) {\n index[item.dataset.id].unregister();\n }\n // Create the new component.\n index[item.dataset.id] = creationhandler({\n ...this,\n element: item,\n });\n // Update selected tab\n let classes = item.querySelector(\"a\").classList.value;\n if (classes.indexOf(this.classes.ACTIVETAB) !== -1) {\n if (level <= 0) {\n this.activetab[0] = item.dataset.id;\n }\n this.activetab[1] = item.dataset.id;\n }\n // Mark as indexed.\n item.dataset.indexed = true;\n });\n }\n\n /**\n * Fix/reorder the section or cms order.\n *\n * @param {Element} container the HTML element to reorder.\n * @param {Array} neworder an array with the ids order\n * @param {string} selector the element selector\n * @param {int} level the tab level\n * @param {boolean} hassubtree\n */\n async _fixOrder(container, neworder, selector, level, hassubtree) {\n\n // Empty lists should not be visible.\n if (!neworder.length) {\n container.classList.add('hidden');\n container.innerHTML = '';\n return;\n }\n\n // Grant the list is visible (in case it was empty).\n container.classList.remove('hidden');\n\n // Move the elements in order at the beginning of the list.\n for (const [index, itemid] of Object.entries(neworder)) {\n const section = this.reactive.get(\"section\", itemid);\n const visible = (section.visible && section.available || section.section == 0)\n && (neworder.length > 1 || hassubtree);\n const current = (section.currentnestedlevel != undefined && section.currentnestedlevel >= level);\n let item = this.getElement(selector, itemid);\n if (item === null) {\n // If we don't have an item, create it.\n let data = {\n \"sectionid\": itemid,\n \"level\": level,\n \"active\": 0,\n \"inactive\": 0,\n \"link\": [{\n \"link\": section.sectionurl\n }],\n \"title\": section.name,\n \"text\": '
' + section.title + '
'\n };\n item = document.createElement(\"li\");\n container.insertBefore(item, container.lastElementChild);\n let html = await Templates.render(\"format_multitopic/courseformat/contenttabs/tab\", data);\n item = Templates.replaceNode(item, html, \"\")[0];\n }\n\n // Update visibility & current marker\n const content = item.querySelector(\"div.tab_content\");\n if (content && content.classList.contains(\"dimmed\") == visible) {\n if (visible) {\n content.classList.remove(\"dimmed\");\n } else {\n content.classList.add(\"dimmed\");\n }\n }\n if (content && content.classList.contains(\"marker\") != current) {\n if (current) {\n content.classList.add(\"marker\");\n } else {\n content.classList.remove(\"marker\");\n }\n }\n\n // Get the current element at that position.\n const currentitem = container.children[index];\n if (currentitem === undefined) {\n container.append(item);\n return;\n }\n if (currentitem !== item) {\n container.insertBefore(item, currentitem);\n }\n }\n // Remove the remaining elements.\n // But we don't want the \"Add\" blown away.\n while (container.children.length > neworder.length + 1) {\n container.removeChild(container.lastElementChild.previousSibling);\n }\n\n }\n\n}"],"names":["Component","BaseComponent","create","name","selectors","TAB","CHILDTAB","SECTION_ITEM","classes","ACTIVETAB","tabs","childtabs","activetab","originalsinglesectionid","document","querySelector","dataset","target","this","element","getElementById","reactive","stateReady","_indexContents","getWatchers","watch","handler","_refreshCourseSectionTabs","originalSingleSection","get","singleSectionId","singleSection","levelsan","id","pageid","newActiveTab0id","parentid","tabsSecondRowDom","tabsSecondRowShow","firstsectionlist","length","secondsectionlist","insertAdjacentElement","createElement","className","activeTab0Dom","data","getAttribute","item","html","Templates","render","replaceNode","addTab0Dom","replace","remove","toptabslist","childtabslist","toptabs","_fixOrder","anchor","_this$element$querySe","parentElement","section","classList","href","sectionurl","_this$element$querySe2","add","removeAttribute","addAnchor","addLink","setAttribute","_this$element$querySe3","_this$element$querySe4","_scanIndex","Tab","selector","index","creationhandler","level","getElements","forEach","_item$dataset","undefined","unregister","value","indexOf","indexed","container","neworder","hassubtree","innerHTML","itemid","Object","entries","visible","available","current","currentnestedlevel","getElement","title","insertBefore","lastElementChild","content","contains","currentitem","children","append","removeChild","previousSibling"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/courseformat/content.js b/amd/src/courseformat/content.js index 5f2aa4e..f9bc193 100644 --- a/amd/src/courseformat/content.js +++ b/amd/src/courseformat/content.js @@ -42,6 +42,7 @@ export default class Component extends BaseComponent { create(descriptor) { super.create(descriptor); this.version = descriptor.version; + this.originalsinglesectionid = this.element.querySelector("ul.sections").dataset.originalsinglesectionid; } /** @@ -298,6 +299,10 @@ export default class Component extends BaseComponent { */ _refreshCourseSectionlist(param) { super._refreshCourseSectionlist(param); + const originalSingleSection = this.reactive.get("section", this.originalsinglesectionid); + const singleSectionId = originalSingleSection ? + (originalSingleSection.levelsan < 2 ? originalSingleSection.id : originalSingleSection.pageid) + : null; const sectionsDom = this.element.querySelectorAll(this.selectors.SECTION); for (let sdi = 0; sdi < sectionsDom.length; sdi++) { const sectionDom = sectionsDom[sdi]; @@ -306,25 +311,26 @@ export default class Component extends BaseComponent { continue; } let refreshCms = false; - const pageSectionDom = this.element.querySelector(".course-section[data-id='" + section.pageid + "']"); - if (pageSectionDom) { - const pageSectionDisplay = pageSectionDom.dataset.fmtonpage; - if (sectionDom.dataset.fmtonpage != pageSectionDisplay) { - sectionDom.dataset.fmtonpage = pageSectionDisplay; - sectionDom.style.display = (pageSectionDisplay == "1") ? "block" : "none"; - if (pageSectionDisplay == "1") { - refreshCms = true; - } + const pageSectionDisplay = (section.pageid == singleSectionId); + if (sectionDom.dataset.fmtonpage != pageSectionDisplay) { + sectionDom.dataset.fmtonpage = pageSectionDisplay; + sectionDom.style.display = (pageSectionDisplay == "1") ? "block" : "none"; + if (pageSectionDisplay == "1") { + refreshCms = true; } } if (section.visible == sectionDom.classList.contains("hidden")) { const badgeDom = sectionDom.querySelector("span.badge[data-type='hiddenfromstudents']"); if (section.visible) { sectionDom.classList.remove("hidden"); - badgeDom.classList.add("d-none"); + if (badgeDom) { + badgeDom.classList.add("d-none"); + } } else { sectionDom.classList.add("hidden"); - badgeDom.classList.remove("d-none"); + if (badgeDom) { + badgeDom.classList.remove("d-none"); + } } if (sectionDom.dataset.fmtonpage == "1") { refreshCms = true; @@ -340,6 +346,14 @@ export default class Component extends BaseComponent { }); } this._refreshAllSectionsToggler(this.reactive.stateManager.state); + + // Update Add section button if necessary. + const addSectionDom = document.querySelector("div#course-addsection > a"); + if (addSectionDom.dataset.intoId != singleSectionId) { + addSectionDom.dataset.intoId = singleSectionId; + addSectionDom.href = addSectionDom.href.replace(/\binsertparentid=\d+\b/, "insertparentid=" + singleSectionId); + } + } /** diff --git a/amd/src/courseformat/content/section.js b/amd/src/courseformat/content/section.js index 36b578b..34cb4a1 100644 --- a/amd/src/courseformat/content/section.js +++ b/amd/src/courseformat/content/section.js @@ -50,13 +50,15 @@ export default class extends SectionBase { } } - const pageSectionDom = document.querySelector(".course-section[data-id='" + this.section.pageid + "']"); - if (pageSectionDom) { - const pageSectionDisplay = pageSectionDom.dataset.fmtonpage; - if (this.element.dataset.fmtonpage != pageSectionDisplay) { - this.element.dataset.fmtonpage = pageSectionDisplay; - this.element.style.display = (pageSectionDisplay == "1") ? "block" : "none"; - } + const originalSingleSectionId = document.querySelector("ul.sections").dataset.originalsinglesectionid; + const originalSingleSection = this.reactive.get("section", originalSingleSectionId); + const singleSectionId = originalSingleSection ? + (originalSingleSection.levelsan < 2 ? originalSingleSection.id : originalSingleSection.pageid) + : null; + const pageSectionDisplay = (this.section.pageid == singleSectionId); + if (this.element.dataset.fmtonpage != pageSectionDisplay) { + this.element.dataset.fmtonpage = pageSectionDisplay; + this.element.style.display = (pageSectionDisplay == "1") ? "block" : "none"; } } diff --git a/amd/src/courseformat/contenttabs/tabtree.js b/amd/src/courseformat/contenttabs/tabtree.js index c10b20a..4e0fa4b 100644 --- a/amd/src/courseformat/contenttabs/tabtree.js +++ b/amd/src/courseformat/contenttabs/tabtree.js @@ -52,6 +52,8 @@ export default class Component extends BaseComponent { this.tabs = {}; this.childtabs = {}; this.activetab = [null, null]; + + this.originalsinglesectionid = document.querySelector("ul.sections").dataset.originalsinglesectionid; } static init(target) { @@ -84,9 +86,26 @@ export default class Component extends BaseComponent { */ async _refreshCourseSectionTabs({element}) { + const originalSingleSection = this.reactive.get("section", this.originalsinglesectionid); + let singleSectionId; + let singleSection; + if (originalSingleSection) { + singleSectionId = (originalSingleSection.levelsan < 2 ? originalSingleSection.id : originalSingleSection.pageid); + singleSection = (singleSectionId == originalSingleSection.id) ? + originalSingleSection : this.reactive.get("section", singleSectionId); + } else { + singleSectionId = null; + singleSection = null; + } + + let newActiveTab0id = singleSection ? + ((singleSection.levelsan >= 1) ? singleSection.parentid : singleSection.id) + : null; + // Add/remove the second-level tabs, if necessary. let tabsSecondRowDom = this.element.querySelector('ul:nth-of-type(2)'); - const tabsSecondRowShow = (element.firstsectionlist.length > 1) || (element.secondsectionlist.length > 1); + const tabsSecondRowShow = singleSection + && ((element.firstsectionlist.length > 1) || (element.secondsectionlist.length > 1)); if (tabsSecondRowShow && !tabsSecondRowDom) { // Create tab row. this.element.querySelector('ul:first-of-type').insertAdjacentElement( @@ -128,33 +147,56 @@ export default class Component extends BaseComponent { tabsSecondRowDom.remove(); } - // Change the active top-level tab, if necessary. - const activeTab1 = this.reactive.get('section', this.activetab[1]); - let newActiveTab0id = (activeTab1.levelsan >= 1) ? activeTab1.parentid : activeTab1.id; - if (newActiveTab0id != this.activetab[0]) { - let section = this.reactive.get("section", this.activetab[0]); - let anchor = this.element.querySelector('ul:first-of-type div[data-itemid="' + this.activetab[0] + '"]').parentElement; - anchor.classList.remove("active"); - anchor.href = section.sectionurl.replace("&", "&"); - this.activetab[0] = newActiveTab0id; - anchor = this.element.querySelector('ul:first-of-type div[data-itemid="' + this.activetab[0] + '"]').parentElement; - anchor.classList.add("active"); - anchor.removeAttribute("href"); - const addAnchor = this.element.querySelector('ul:nth-of-type(2) li:last-of-type a'); - const addLink = addAnchor.href.replace(/\binsertparentid=\d+\b/, "insertparentid=" + this.activetab[0]); - addAnchor.setAttribute("href", addLink); - } - // Do things that make the first row tabs match firstsectionlist. const toptabslist = element.firstsectionlist ?? []; const childtabslist = element.secondsectionlist ?? []; let toptabs = this.element.querySelector('ul:first-of-type'); - await this._fixOrder(toptabs, toptabslist, this.selectors.TAB, 0, childtabslist[this.activetab[0]].length > 1); + await this._fixOrder(toptabs, toptabslist, this.selectors.TAB, 0, childtabslist[toptabslist[0]].length > 1); // And the second row tabs match secondsectionlist. - let childtabs = this.element.querySelector('ul:nth-of-type(2)'); - if (childtabs) { - await this._fixOrder(childtabs, childtabslist[this.activetab[0]], this.selectors.CHILDTAB, 1, false); + if (tabsSecondRowShow) { + let childtabs = this.element.querySelector('ul:nth-of-type(2)'); + if (childtabs) { + await this._fixOrder(childtabs, childtabslist[newActiveTab0id], this.selectors.CHILDTAB, 1, false); + } + } + + // Change the active top-level tab, if necessary. + if (newActiveTab0id != this.activetab[0]) { + let anchor = this.element.querySelector('ul:first-of-type div[data-itemid="' + this.activetab[0] + '"]')?.parentElement; + if (anchor) { + let section = this.reactive.get("section", this.activetab[0]); + anchor.classList.remove("active"); + anchor.href = section.sectionurl.replace("&", "&"); + } + this.activetab[0] = newActiveTab0id; + anchor = this.element.querySelector('ul:first-of-type div[data-itemid="' + this.activetab[0] + '"]')?.parentElement; + if (anchor) { + anchor.classList.add("active"); + anchor.removeAttribute("href"); + } + if (tabsSecondRowShow) { + const addAnchor = this.element.querySelector('ul:nth-of-type(2) li:last-of-type a'); + const addLink = addAnchor.href.replace(/\binsertparentid=\d+\b/, "insertparentid=" + this.activetab[0]); + addAnchor.setAttribute("href", addLink); + } + } + + // Change the active second-level tab, if necessary. + if (tabsSecondRowShow && singleSection.id != this.activetab[1]) { + let anchor = this.element.querySelector('ul:nth-of-type(2) div[data-itemid="' + this.activetab[1] + '"]') + ?.parentElement; + if (anchor) { + let section = this.reactive.get("section", this.activetab[1]); + anchor.classList.remove("active"); + anchor.href = section.sectionurl.replace("&", "&"); + } + this.activetab[1] = singleSection.id; + anchor = this.element.querySelector('ul:nth-of-type(2) div[data-itemid="' + this.activetab[1] + '"]')?.parentElement; + if (anchor) { + anchor.classList.add("active"); + anchor.removeAttribute("href"); + } } this._indexContents(); diff --git a/classes/output/courseformat/content.php b/classes/output/courseformat/content.php index 5c38a0b..6a62364 100644 --- a/classes/output/courseformat/content.php +++ b/classes/output/courseformat/content.php @@ -268,6 +268,7 @@ public function export_for_template(\renderer_base $output) { 'format' => $format->get_format(), 'fmtpageid' => $displaysectionextra->id, 'version' => $CFG->version, + 'originalsinglesectionid' => $format->originalsinglesectionid, ]; // INCLUDED from course/format/classes/output/local/content/section/cmlist.php export_for_template() . diff --git a/lib.php b/lib.php index 9e67c47..6ce90c6 100644 --- a/lib.php +++ b/lib.php @@ -80,6 +80,8 @@ class format_multitopic extends core_courseformat\base { /** @var int|null the sectionid selected */ protected $singlesectionid = null; + + public $originalsinglesectionid = null; // END ADDED. // INCLUDED declaration /course/format/classes/base.php class base function __construct. @@ -510,11 +512,14 @@ public function set_sectionnum($section): void { if ($section === null) { $this->singlesection = null; $this->singlesectionid = null; + $this->originalsinglesectionid = null; return; } $sectionextra = $this->fmt_get_section_extra($section); + $this->originalsinglesectionid = $sectionextra->id; + // If display section is a topic, get the page it is on instead. if ($sectionextra->levelsan >= FORMAT_MULTITOPIC_SECTION_LEVEL_TOPIC) { $sectionsextra = $this->fmt_get_sections_extra(); diff --git a/templates/courseformat/content.mustache b/templates/courseformat/content.mustache index 3a39fc1..281002d 100644 --- a/templates/courseformat/content.mustache +++ b/templates/courseformat/content.mustache @@ -181,7 +181,7 @@ {{/tabs}} {{{completionhelp}}} -
    +
      {{#initialsection}} {{$ core_courseformat/local/content/section }} {{> format_multitopic/courseformat/content/section }}