Proyectos de Subversion Moodle

Rev

Rev 1 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 1 Rev 11
Línea 1... Línea -...
1
{"version":3,"file":"courseindex.min.js","sources":["../../../src/local/courseindex/courseindex.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 <http://www.gnu.org/licenses/>.\n\n/**\n * Course index main component.\n *\n * @module     core_courseformat/local/courseindex/courseindex\n * @class     core_courseformat/local/courseindex/courseindex\n * @copyright  2021 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 jQuery from 'jquery';\nimport ContentTree from 'core_courseformat/local/courseeditor/contenttree';\n\nexport default class Component extends BaseComponent {\n\n    /**\n     * Constructor hook.\n     */\n    create() {\n        // Optional component name for debugging.\n        this.name = 'courseindex';\n        // Default query selectors.\n        this.selectors = {\n            SECTION: `[data-for='section']`,\n            SECTION_CMLIST: `[data-for='cmlist']`,\n            CM: `[data-for='cm']`,\n            TOGGLER: `[data-action=\"togglecourseindexsection\"]`,\n            COLLAPSE: `[data-toggle=\"collapse\"]`,\n            DRAWER: `.drawer`,\n        };\n        // Default classes to toggle on refresh.\n        this.classes = {\n            SECTIONHIDDEN: 'dimmed',\n            CMHIDDEN: 'dimmed',\n            SECTIONCURRENT: 'current',\n            COLLAPSED: `collapsed`,\n            SHOW: `show`,\n        };\n        // Arrays to keep cms and sections elements.\n        this.sections = {};\n        this.cms = {};\n    }\n\n    /**\n     * Static method to create a component instance form the mustache template.\n     *\n     * @param {element|string} target the DOM main element or its ID\n     * @param {object} selectors optional css selector overrides\n     * @return {Component}\n     */\n    static init(target, selectors) {\n        return new this({\n            element: document.getElementById(target),\n            reactive: getCurrentCourseEditor(),\n            selectors,\n        });\n    }\n\n    /**\n     * Initial state ready method.\n     *\n     * @param {Object} state the state data\n     */\n    stateReady(state) {\n        // Activate section togglers.\n        this.addEventListener(this.element, 'click', this._sectionTogglers);\n\n        // Get cms and sections elements.\n        const sections = this.getElements(this.selectors.SECTION);\n        sections.forEach((section) => {\n            this.sections[section.dataset.id] = section;\n        });\n        const cms = this.getElements(this.selectors.CM);\n        cms.forEach((cm) => {\n            this.cms[cm.dataset.id] = cm;\n        });\n\n        // Set the page item if any.\n        this._refreshPageItem({element: state.course, state});\n\n        // Configure Aria Tree.\n        this.contentTree = new ContentTree(this.element, this.selectors, this.reactive.isEditing);\n    }\n\n    getWatchers() {\n        return [\n            {watch: `section.indexcollapsed:updated`, handler: this._refreshSectionCollapsed},\n            {watch: `cm:created`, handler: this._createCm},\n            {watch: `cm:deleted`, handler: this._deleteCm},\n            {watch: `section:created`, handler: this._createSection},\n            {watch: `section:deleted`, handler: this._deleteSection},\n            {watch: `course.pageItem:created`, handler: this._refreshPageItem},\n            {watch: `course.pageItem:updated`, handler: this._refreshPageItem},\n            // Sections and cm sorting.\n            {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionlist},\n            {watch: `section.cmlist:updated`, handler: this._refreshSectionCmlist},\n        ];\n    }\n\n    /**\n     * Setup sections toggler.\n     *\n     * Toggler click is delegated to the main course index 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    _sectionTogglers(event) {\n        const sectionlink = event.target.closest(this.selectors.TOGGLER);\n        const isChevron = event.target.closest(this.selectors.COLLAPSE);\n\n        if (sectionlink || isChevron) {\n\n            const section = event.target.closest(this.selectors.SECTION);\n            const toggler = section.querySelector(this.selectors.COLLAPSE);\n            const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n            // Update the state.\n            const sectionId = section.getAttribute('data-id');\n            if (!sectionlink || isCollapsed) {\n                this.reactive.dispatch(\n                    'sectionIndexCollapsed',\n                    [sectionId],\n                    !isCollapsed\n                );\n            }\n        }\n    }\n\n    /**\n     * Update section collapsed.\n     *\n     * @param {object} args\n     * @param {object} args.element The leement to be expanded\n     */\n    _refreshSectionCollapsed({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        // Check if it is already done.\n        const toggler = target.querySelector(this.selectors.COLLAPSE);\n        const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n        if (element.indexcollapsed !== isCollapsed) {\n            this._expandSectionNode(element);\n        }\n    }\n\n    /**\n     * Expand a section node.\n     *\n     * By default the method will use element.indexcollapsed to decide if the\n     * section is opened or closed. However, using forceValue it is possible\n     * to open or close a section independant from the indexcollapsed attribute.\n     *\n     * @param {Object} element the course module state element\n     * @param {boolean} forceValue optional forced expanded value\n     */\n    _expandSectionNode(element, forceValue) {\n        const target = this.getElement(this.selectors.SECTION, element.id);\n        const toggler = target.querySelector(this.selectors.COLLAPSE);\n        let collapsibleId = toggler.dataset.target ?? toggler.getAttribute(\"href\");\n        if (!collapsibleId) {\n            return;\n        }\n        collapsibleId = collapsibleId.replace('#', '');\n        const collapsible = document.getElementById(collapsibleId);\n        if (!collapsible) {\n            return;\n        }\n\n        if (forceValue === undefined) {\n            forceValue = (element.indexcollapsed) ? false : true;\n        }\n\n        // Course index is based on Bootstrap 4 collapsibles. To collapse them we need jQuery to\n        // interact with collapsibles methods. Hopefully, this will change in Bootstrap 5 because\n        // it does not require jQuery anymore (when MDL-71979 is integrated).\n        const togglerValue = (forceValue) ? 'show' : 'hide';\n        jQuery(collapsible).collapse(togglerValue);\n    }\n\n    /**\n     * Handle a page item update.\n     *\n     * @param {Object} details the update details\n     * @param {Object} details.state the state data.\n     * @param {Object} details.element the course state data.\n     */\n    _refreshPageItem({element, state}) {\n        if (!element?.pageItem?.isStatic || element.pageItem.type != 'cm') {\n            return;\n        }\n        // Check if we need to uncollapse the section and scroll to the element.\n        const section = state.section.get(element.pageItem.sectionId);\n        if (section.indexcollapsed) {\n            this._expandSectionNode(section, true);\n            setTimeout(\n                () => this.cms[element.pageItem.id]?.scrollIntoView({block: \"nearest\"}),\n                250\n            );\n        }\n    }\n\n    /**\n     * Create a newcm instance.\n     *\n     * @param {object} param\n     * @param {Object} param.state\n     * @param {Object} param.element\n     */\n    async _createCm({state, element}) {\n        // Create a fake node while the component is loading.\n        const fakeelement = document.createElement('li');\n        fakeelement.classList.add('bg-pulse-grey', 'w-100');\n        fakeelement.innerHTML = '&nbsp;';\n        this.cms[element.id] = fakeelement;\n        // Place the fake node on the correct position.\n        this._refreshSectionCmlist({\n            state,\n            element: state.section.get(element.sectionid),\n        });\n        // Collect render data.\n        const exporter = this.reactive.getExporter();\n        const data = exporter.cm(state, element);\n        // Create the new content.\n        const newcomponent = await this.renderComponent(fakeelement, 'core_courseformat/local/courseindex/cm', data);\n        // Replace the fake node with the real content.\n        const newelement = newcomponent.getElement();\n        this.cms[element.id] = newelement;\n        fakeelement.parentNode.replaceChild(newelement, fakeelement);\n    }\n\n    /**\n     * Create a new section instance.\n     *\n     * @param {Object} details the update details.\n     * @param {Object} details.state the state data.\n     * @param {Object} details.element the element data.\n     */\n    async _createSection({state, element}) {\n        // Create a fake node while the component is loading.\n        const fakeelement = document.createElement('div');\n        fakeelement.classList.add('bg-pulse-grey', 'w-100');\n        fakeelement.innerHTML = '&nbsp;';\n        this.sections[element.id] = fakeelement;\n        // Place the fake node on the correct position.\n        this._refreshCourseSectionlist({\n            state,\n            element: state.course,\n        });\n        // Collect render data.\n        const exporter = this.reactive.getExporter();\n        const data = exporter.section(state, element);\n        // Create the new content.\n        const newcomponent = await this.renderComponent(fakeelement, 'core_courseformat/local/courseindex/section', data);\n        // Replace the fake node with the real content.\n        const newelement = newcomponent.getElement();\n        this.sections[element.id] = newelement;\n        fakeelement.parentNode.replaceChild(newelement, fakeelement);\n    }\n\n    /**\n     * Refresh a section cm list.\n     *\n     * @param {object} param\n     * @param {Object} param.element\n     */\n    _refreshSectionCmlist({element}) {\n        const cmlist = element.cmlist ?? [];\n        const listparent = this.getElement(this.selectors.SECTION_CMLIST, element.id);\n        this._fixOrder(listparent, cmlist, this.cms);\n    }\n\n    /**\n     * Refresh the section list.\n     *\n     * @param {object} param\n     * @param {Object} param.state\n     */\n    _refreshCourseSectionlist({state}) {\n        const sectionlist = this.reactive.getExporter().listedSectionIds(state);\n        this._fixOrder(this.element, sectionlist, this.sections);\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 {Array} allitems the list of html elements that can be placed in the container\n     */\n    _fixOrder(container, neworder, allitems) {\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 = allitems[itemid];\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 && item) {\n                container.insertBefore(item, currentitem);\n            }\n        });\n        // Remove the remaining elements.\n        while (container.children.length > neworder.length) {\n            container.removeChild(container.lastChild);\n        }\n    }\n\n    /**\n     * Remove a cm from the list.\n     *\n     * The actual DOM element removal is delegated to the cm component.\n     *\n     * @param {object} param\n     * @param {Object} param.element\n     */\n    _deleteCm({element}) {\n        delete this.cms[element.id];\n    }\n\n    /**\n     * Remove a section from the list.\n     *\n     * The actual DOM element removal is delegated to the section component.\n     *\n     * @param {Object} details the update details.\n     * @param {Object} details.element the element data.\n     */\n    _deleteSection({element}) {\n        delete this.sections[element.id];\n    }\n}\n"],"names":["Component","BaseComponent","create","name","selectors","SECTION","SECTION_CMLIST","CM","TOGGLER","COLLAPSE","DRAWER","classes","SECTIONHIDDEN","CMHIDDEN","SECTIONCURRENT","COLLAPSED","SHOW","sections","cms","target","this","element","document","getElementById","reactive","stateReady","state","addEventListener","_sectionTogglers","getElements","forEach","section","dataset","id","cm","_refreshPageItem","course","contentTree","ContentTree","isEditing","getWatchers","watch","handler","_refreshSectionCollapsed","_createCm","_deleteCm","_createSection","_deleteSection","_refreshCourseSectionlist","_refreshSectionCmlist","event","sectionlink","closest","isChevron","toggler","querySelector","isCollapsed","classList","contains","sectionId","getAttribute","dispatch","getElement","Error","indexcollapsed","_expandSectionNode","forceValue","collapsibleId","replace","collapsible","undefined","togglerValue","collapse","pageItem","_element$pageItem","isStatic","type","get","setTimeout","_this$cms$element$pag","scrollIntoView","block","fakeelement","createElement","add","innerHTML","sectionid","data","getExporter","newelement","renderComponent","parentNode","replaceChild","cmlist","listparent","_fixOrder","sectionlist","listedSectionIds","container","neworder","allitems","length","remove","itemid","index","item","currentitem","children","insertBefore","append","removeChild","lastChild"],"mappings":"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"}
-
 
2
1
{"version":3,"file":"courseindex.min.js","sources":["../../../src/local/courseindex/courseindex.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 <http://www.gnu.org/licenses/>.\n\n/**\n * Course index main component.\n *\n * @module     core_courseformat/local/courseindex/courseindex\n * @class     core_courseformat/local/courseindex/courseindex\n * @copyright  2021 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 jQuery from 'jquery';\nimport ContentTree from 'core_courseformat/local/courseeditor/contenttree';\n\nexport default class Component extends BaseComponent {\n\n    /**\n     * Constructor hook.\n     */\n    create() {\n        // Optional component name for debugging.\n        this.name = 'courseindex';\n        // Default query selectors.\n        this.selectors = {\n            SECTION: `[data-for='section']`,\n            SECTION_CMLIST: `[data-for='cmlist']`,\n            CM: `[data-for='cm']`,\n            TOGGLER: `[data-action=\"togglecourseindexsection\"]`,\n            COLLAPSE: `[data-toggle=\"collapse\"]`,\n            DRAWER: `.drawer`,\n        };\n        // Default classes to toggle on refresh.\n        this.classes = {\n            SECTIONHIDDEN: 'dimmed',\n            CMHIDDEN: 'dimmed',\n            SECTIONCURRENT: 'current',\n            COLLAPSED: `collapsed`,\n            SHOW: `show`,\n        };\n        // Arrays to keep cms and sections elements.\n        this.sections = {};\n        this.cms = {};\n    }\n\n    /**\n     * Static method to create a component instance form the mustache template.\n     *\n     * @param {element|string} target the DOM main element or its ID\n     * @param {object} selectors optional css selector overrides\n     * @return {Component}\n     */\n    static init(target, selectors) {\n        return new this({\n            element: document.getElementById(target),\n            reactive: getCurrentCourseEditor(),\n            selectors,\n        });\n    }\n\n    /**\n     * Initial state ready method.\n     *\n     * @param {Object} state the state data\n     */\n    stateReady(state) {\n        // Activate section togglers.\n        this.addEventListener(this.element, 'click', this._sectionTogglers);\n\n        // Get cms and sections elements.\n        const sections = this.getElements(this.selectors.SECTION);\n        sections.forEach((section) => {\n            this.sections[section.dataset.id] = section;\n        });\n        const cms = this.getElements(this.selectors.CM);\n        cms.forEach((cm) => {\n            this.cms[cm.dataset.id] = cm;\n        });\n\n        this._expandPageCmSectionIfNecessary(state);\n        this._refreshPageItem({element: state.course, state});\n\n        // Configure Aria Tree.\n        this.contentTree = new ContentTree(this.element, this.selectors, this.reactive.isEditing);\n    }\n\n    getWatchers() {\n        return [\n            {watch: `section.indexcollapsed:updated`, handler: this._refreshSectionCollapsed},\n            {watch: `cm:created`, handler: this._createCm},\n            {watch: `cm:deleted`, handler: this._deleteCm},\n            {watch: `section:created`, handler: this._createSection},\n            {watch: `section:deleted`, handler: this._deleteSection},\n            {watch: `course.pageItem:created`, handler: this._refreshPageItem},\n            {watch: `course.pageItem:updated`, handler: this._refreshPageItem},\n            // Sections and cm sorting.\n            {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionlist},\n            {watch: `section.cmlist:updated`, handler: this._refreshSectionCmlist},\n        ];\n    }\n\n    /**\n     * Setup sections toggler.\n     *\n     * Toggler click is delegated to the main course index 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    _sectionTogglers(event) {\n        const sectionlink = event.target.closest(this.selectors.TOGGLER);\n        const isChevron = event.target.closest(this.selectors.COLLAPSE);\n\n        if (sectionlink || isChevron) {\n\n            const section = event.target.closest(this.selectors.SECTION);\n            const toggler = section.querySelector(this.selectors.COLLAPSE);\n            const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n            // Update the state.\n            const sectionId = section.getAttribute('data-id');\n            if (!sectionlink || isCollapsed) {\n                this.reactive.dispatch(\n                    'sectionIndexCollapsed',\n                    [sectionId],\n                    !isCollapsed\n                );\n            }\n        }\n    }\n\n    /**\n     * Update section collapsed.\n     *\n     * @param {object} args\n     * @param {object} args.element The leement to be expanded\n     */\n    _refreshSectionCollapsed({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        // Check if it is already done.\n        const toggler = target.querySelector(this.selectors.COLLAPSE);\n        const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n        if (element.indexcollapsed !== isCollapsed) {\n            this._expandSectionNode(element);\n        }\n    }\n\n    /**\n     * Expand a section node.\n     *\n     * By default the method will use element.indexcollapsed to decide if the\n     * section is opened or closed. However, using forceValue it is possible\n     * to open or close a section independant from the indexcollapsed attribute.\n     *\n     * @param {Object} element the course module state element\n     * @param {boolean} forceValue optional forced expanded value\n     */\n    _expandSectionNode(element, forceValue) {\n        const target = this.getElement(this.selectors.SECTION, element.id);\n        const toggler = target.querySelector(this.selectors.COLLAPSE);\n        let collapsibleId = toggler.dataset.target ?? toggler.getAttribute(\"href\");\n        if (!collapsibleId) {\n            return;\n        }\n        collapsibleId = collapsibleId.replace('#', '');\n        const collapsible = document.getElementById(collapsibleId);\n        if (!collapsible) {\n            return;\n        }\n\n        if (forceValue === undefined) {\n            forceValue = (element.indexcollapsed) ? false : true;\n        }\n\n        // Course index is based on Bootstrap 4 collapsibles. To collapse them we need jQuery to\n        // interact with collapsibles methods. Hopefully, this will change in Bootstrap 5 because\n        // it does not require jQuery anymore (when MDL-71979 is integrated).\n        const togglerValue = (forceValue) ? 'show' : 'hide';\n        jQuery(collapsible).collapse(togglerValue);\n    }\n\n    /**\n     * Handle a page item update.\n     *\n     * @param {Object} details the update details\n     * @param {Object} details.state the state data.\n     * @param {Object} details.element the course state data.\n     */\n    _refreshPageItem({element, state}) {\n        if (!element?.pageItem?.isStatic || element.pageItem.type != 'cm') {\n            return;\n        }\n        // Check if we need to uncollapse the section and scroll to the element.\n        const section = state.section.get(element.pageItem.sectionId);\n        if (section.indexcollapsed) {\n            this._expandSectionNode(section, true);\n            setTimeout(\n                () => this.cms[element.pageItem.id]?.scrollIntoView({block: \"nearest\"}),\n                250\n            );\n        }\n    }\n\n    /**\n     * Expand a section if the current page is a section's cm.\n     *\n     * @private\n     * @param {Object} state the course state.\n     */\n    _expandPageCmSectionIfNecessary(state) {\n        const pageCmInfo = this.reactive.getPageAnchorCmInfo();\n        if (!pageCmInfo) {\n            return;\n        }\n        this._expandSectionNode(state.section.get(pageCmInfo.sectionid), true);\n    }\n\n    /**\n     * Create a newcm instance.\n     *\n     * @param {object} param\n     * @param {Object} param.state\n     * @param {Object} param.element\n     */\n    async _createCm({state, element}) {\n        // Create a fake node while the component is loading.\n        const fakeelement = document.createElement('li');\n        fakeelement.classList.add('bg-pulse-grey', 'w-100');\n        fakeelement.innerHTML = '&nbsp;';\n        this.cms[element.id] = fakeelement;\n        // Place the fake node on the correct position.\n        this._refreshSectionCmlist({\n            state,\n            element: state.section.get(element.sectionid),\n        });\n        // Collect render data.\n        const exporter = this.reactive.getExporter();\n        const data = exporter.cm(state, element);\n        // Create the new content.\n        const newcomponent = await this.renderComponent(fakeelement, 'core_courseformat/local/courseindex/cm', data);\n        // Replace the fake node with the real content.\n        const newelement = newcomponent.getElement();\n        this.cms[element.id] = newelement;\n        fakeelement.parentNode.replaceChild(newelement, fakeelement);\n    }\n\n    /**\n     * Create a new section instance.\n     *\n     * @param {Object} details the update details.\n     * @param {Object} details.state the state data.\n     * @param {Object} details.element the element data.\n     */\n    async _createSection({state, element}) {\n        // Create a fake node while the component is loading.\n        const fakeelement = document.createElement('div');\n        fakeelement.classList.add('bg-pulse-grey', 'w-100');\n        fakeelement.innerHTML = '&nbsp;';\n        this.sections[element.id] = fakeelement;\n        // Place the fake node on the correct position.\n        this._refreshCourseSectionlist({\n            state,\n            element: state.course,\n        });\n        // Collect render data.\n        const exporter = this.reactive.getExporter();\n        const data = exporter.section(state, element);\n        // Create the new content.\n        const newcomponent = await this.renderComponent(fakeelement, 'core_courseformat/local/courseindex/section', data);\n        // Replace the fake node with the real content.\n        const newelement = newcomponent.getElement();\n        this.sections[element.id] = newelement;\n        fakeelement.parentNode.replaceChild(newelement, fakeelement);\n    }\n\n    /**\n     * Refresh a section cm list.\n     *\n     * @param {object} param\n     * @param {Object} param.element\n     */\n    _refreshSectionCmlist({element}) {\n        const cmlist = element.cmlist ?? [];\n        const listparent = this.getElement(this.selectors.SECTION_CMLIST, element.id);\n        this._fixOrder(listparent, cmlist, this.cms);\n    }\n\n    /**\n     * Refresh the section list.\n     *\n     * @param {object} param\n     * @param {Object} param.state\n     */\n    _refreshCourseSectionlist({state}) {\n        const sectionlist = this.reactive.getExporter().listedSectionIds(state);\n        this._fixOrder(this.element, sectionlist, this.sections);\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 {Array} allitems the list of html elements that can be placed in the container\n     */\n    _fixOrder(container, neworder, allitems) {\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 = allitems[itemid];\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 && item) {\n                container.insertBefore(item, currentitem);\n            }\n        });\n        // Remove the remaining elements.\n        while (container.children.length > neworder.length) {\n            container.removeChild(container.lastChild);\n        }\n    }\n\n    /**\n     * Remove a cm from the list.\n     *\n     * The actual DOM element removal is delegated to the cm component.\n     *\n     * @param {object} param\n     * @param {Object} param.element\n     */\n    _deleteCm({element}) {\n        delete this.cms[element.id];\n    }\n\n    /**\n     * Remove a section from the list.\n     *\n     * The actual DOM element removal is delegated to the section component.\n     *\n     * @param {Object} details the update details.\n     * @param {Object} details.element the element data.\n     */\n    _deleteSection({element}) {\n        delete this.sections[element.id];\n    }\n}\n"],"names":["Component","BaseComponent","create","name","selectors","SECTION","SECTION_CMLIST","CM","TOGGLER","COLLAPSE","DRAWER","classes","SECTIONHIDDEN","CMHIDDEN","SECTIONCURRENT","COLLAPSED","SHOW","sections","cms","target","this","element","document","getElementById","reactive","stateReady","state","addEventListener","_sectionTogglers","getElements","forEach","section","dataset","id","cm","_expandPageCmSectionIfNecessary","_refreshPageItem","course","contentTree","ContentTree","isEditing","getWatchers","watch","handler","_refreshSectionCollapsed","_createCm","_deleteCm","_createSection","_deleteSection","_refreshCourseSectionlist","_refreshSectionCmlist","event","sectionlink","closest","isChevron","toggler","querySelector","isCollapsed","classList","contains","sectionId","getAttribute","dispatch","getElement","Error","indexcollapsed","_expandSectionNode","forceValue","collapsibleId","replace","collapsible","undefined","togglerValue","collapse","pageItem","_element$pageItem","isStatic","type","get","setTimeout","_this$cms$element$pag","scrollIntoView","block","pageCmInfo","getPageAnchorCmInfo","sectionid","fakeelement","createElement","add","innerHTML","data","getExporter","newelement","renderComponent","parentNode","replaceChild","cmlist","listparent","_fixOrder","sectionlist","listedSectionIds","container","neworder","allitems","length","remove","itemid","index","item","currentitem","children","insertBefore","append","removeChild","lastChild"],"mappings":"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"}
-
 
2
3
3