1 {"version":3,"sources":["../../src/local/content.js"],"names":["Component","name","selectors","SECTION","SECTION_ITEM","SECTION_TITLE","SECTION_CMLIST","COURSE_SECTIONLIST","CM","dettachedCms","dettachedSections","reactive","supportComponents","watch","handler","_reloadCm","_refreshSectionNumber","_startProcessing","_refreshCourseSectionlist","_refreshSectionCmlist","element","cmitem","getElement","id","courseActions","refreshModule","target","Error","number","dataset","sectionid","inplace","inplaceeditable","getInplaceEditable","querySelector","currentvalue","getValue","currentitemid","getItemId","rawtitle","setValue","cmlist","section","listparent","_fixOrder","sectionlist","container","neworder","selector","dettachedelements","length","classList","add","innerHTML","remove","forEach","itemid","index","item","currentitem","children","append","insertBefore","lastchild","lastChild","removeChild","document","getElementById","BaseComponent"],"mappings":"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","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 <http://www.gnu.org/licenses/>.\n\n/**\n * Course index main component.\n *\n * @module core_courseformat/local/content\n * @class core_courseformat/local/content\n * @copyright 2020 Ferran Recio <ferran@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport inplaceeditable from 'core/inplace_editable';\n// Course actions is needed for actions that are not migrated to components.\nimport courseActions from 'core_course/actions';\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'course_format';\n // Default query selectors.\n this.selectors = {\n SECTION: `[data-for='section']`,\n SECTION_ITEM: `[data-for='section_item']`,\n SECTION_TITLE: `[data-for='section_title']`,\n SECTION_CMLIST: `[data-for='cmlist']`,\n COURSE_SECTIONLIST: `[data-for='course_sectionlist']`,\n CM: `[data-for='cmitem']`,\n };\n // Array to save dettached elements during element resorting.\n this.dettachedCms = {};\n this.dettachedSections = {};\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new Component({\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n selectors,\n });\n }\n\n /**\n * Return the component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n // Check if the course format is compatible with reactive components.\n if (!this.reactive.supportComponents) {\n return [];\n }\n return [\n // State changes that require to reload some course modules.\n {watch: `cm.visible:updated`, handler: this._reloadCm},\n // Update section number and title.\n {watch: `section.number:updated`, handler: this._refreshSectionNumber},\n // Sections and cm sorting.\n {watch: `transaction:start`, handler: this._startProcessing},\n {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionlist},\n {watch: `section.cmlist:updated`, handler: this._refreshSectionCmlist},\n ];\n }\n\n /**\n * Reload a course module.\n *\n * Most course module HTML is still strongly backend dependant.\n * Some changes require to get a new version af the module.\n *\n * @param {Object} update the state update data\n */\n _reloadCm({element}) {\n const cmitem = this.getElement(this.selectors.CM, element.id);\n if (cmitem) {\n courseActions.refreshModule(cmitem, element.id);\n }\n }\n\n /**\n * Setup the component to start a transaction.\n *\n * Some of the course actions replaces the current DOM element with a new one before updating the\n * course state. This means the component cannot preload any index properly until the transaction starts.\n *\n */\n _startProcessing() {\n // During a section or cm sorting, some elements could be dettached from the DOM and we\n // need to store somewhare in case they are needed later.\n this.dettachedCms = {};\n this.dettachedSections = {};\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} 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 throw new Error(`Unkown section with ID ${element.id}`);\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_TITLE));\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() === '') {\n // The value to send can be an empty value if it is a default name.\n if (currentitemid == element.id && (currentvalue != element.rawtitle || element.rawtitle == '')) {\n inplace.setValue(element.rawtitle);\n }\n }\n }\n }\n\n /**\n * Refresh a section cm list.\n *\n * @param {Object} details the update details.\n */\n _refreshSectionCmlist({element}) {\n const cmlist = element.cmlist ?? [];\n const section = this.getElement(this.selectors.SECTION, element.id);\n const listparent = section?.querySelector(this.selectors.SECTION_CMLIST);\n if (listparent) {\n this._fixOrder(listparent, cmlist, this.selectors.CM, this.dettachedCms);\n }\n }\n\n /**\n * Refresh the section list.\n *\n * @param {Object} details the update details.\n */\n _refreshCourseSectionlist({element}) {\n const sectionlist = element.sectionlist ?? [];\n const listparent = this.getElement(this.selectors.COURSE_SECTIONLIST);\n if (listparent) {\n this._fixOrder(listparent, sectionlist, this.selectors.SECTION, this.dettachedSections);\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 {Object} dettachedelements a list of dettached elements\n */\n _fixOrder(container, neworder, selector, dettachedelements) {\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 neworder.forEach((itemid, index) => {\n const item = this.getElement(selector, itemid) ?? dettachedelements[itemid];\n // Get the current elemnt 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 while (container.children.length > neworder.length) {\n const lastchild = container.lastChild;\n dettachedelements[lastchild?.dataset?.id ?? 0] = lastchild;\n container.removeChild(lastchild);\n }\n }\n}\n"],"file":"content.min.js"}