Proyectos de Subversion Moodle

Rev

Rev 1 | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |

{"version":3,"file":"actions.min.js","sources":["../../../src/local/content/actions.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 state actions dispatcher.\n *\n * This module captures all data-dispatch links in the course content and dispatch the proper\n * state mutation, including any confirmation and modal required.\n *\n * @module     core_courseformat/local/content/actions\n * @class      core_courseformat/local/content/actions\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 {eventTypes} from 'core/local/inplace_editable/events';\nimport Collapse from 'theme_boost/bootstrap/collapse';\nimport log from 'core/log';\nimport Modal from 'core/modal';\nimport ModalSaveCancel from 'core/modal_save_cancel';\nimport ModalDeleteCancel from 'core/modal_delete_cancel';\nimport ModalCopyToClipboard from 'core/modal_copy_to_clipboard';\nimport ModalEvents from 'core/modal_events';\nimport Templates from 'core/templates';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport {getFirst} from 'core/normalise';\nimport {toggleBulkSelectionAction} from 'core_courseformat/local/content/actions/bulkselection';\nimport * as CourseEvents from 'core_course/events';\nimport Pending from 'core/pending';\nimport ContentTree from 'core_courseformat/local/courseeditor/contenttree';\n// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated.\nimport Notification from \"core/notification\";\n\n// Load global strings.\nprefetchStrings('core', ['movecoursesection', 'movecoursemodule', 'confirm', 'delete']);\n\n// Mutations are dispatched by the course content actions.\n// Formats can use this module addActions static method to add custom actions.\n// Direct mutations can be simple strings (mutation) name or functions.\nconst directMutations = {\n    sectionHide: 'sectionHide',\n    sectionShow: 'sectionShow',\n    cmHide: 'cmHide',\n    cmShow: 'cmShow',\n    cmStealth: 'cmStealth',\n    cmMoveRight: 'cmMoveRight',\n    cmMoveLeft: 'cmMoveLeft',\n    cmNoGroups: 'cmNoGroups',\n    cmSeparateGroups: 'cmSeparateGroups',\n    cmVisibleGroups: 'cmVisibleGroups',\n};\n\nexport default class extends BaseComponent {\n\n    /**\n     * Constructor hook.\n     */\n    create() {\n        // Optional component name for debugging.\n        this.name = 'content_actions';\n        // Default query selectors.\n        this.selectors = {\n            ACTIONLINK: `[data-action]`,\n            // Move modal selectors.\n            SECTIONLINK: `[data-for='section']`,\n            CMLINK: `[data-for='cm']`,\n            SECTIONNODE: `[data-for='sectionnode']`,\n            MODALTOGGLER: `[data-bs-toggle='collapse']`,\n            ADDSECTION: `[data-action='addSection']`,\n            CONTENTTREE: `#destination-selector`,\n            ACTIONMENU: `.action-menu`,\n            ACTIONMENUTOGGLER: `[data-bs-toggle=\"dropdown\"]`,\n            // Availability modal selectors.\n            OPTIONSRADIO: `[type='radio']`,\n            COURSEADDSECTION: `#course-addsection`,\n            MAXSECTIONSWARNING: `[data-region='max-sections-warning']`,\n            ADDSECTIONREGION: `[data-region='section-addsection']`,\n        };\n        // Component css classes.\n        this.classes = {\n            DISABLED: `disabled`,\n            ITALIC: `fst-italic`,\n            DISPLAYNONE: `d-none`,\n        };\n    }\n\n    /**\n     * Add extra actions to the module.\n     *\n     * @param {array} actions array of methods to execute\n     */\n    static addActions(actions) {\n        for (const [action, mutationReference] of Object.entries(actions)) {\n            if (typeof mutationReference !== 'function' && typeof mutationReference !== 'string') {\n                throw new Error(`${action} action must be a mutation name or a function`);\n            }\n            directMutations[action] = mutationReference;\n        }\n    }\n\n    /**\n     * Initial state ready method.\n     *\n     * @param {Object} state the state data.\n     *\n     */\n    stateReady(state) {\n        // Delegate dispatch clicks.\n        this.addEventListener(\n            this.element,\n            'click',\n            this._dispatchClick\n        );\n        // Check section limit.\n        this._checkSectionlist({state});\n        // Add an Event listener to recalculate limits it if a section HTML is altered.\n        this.addEventListener(\n            this.element,\n            CourseEvents.sectionRefreshed,\n            () => this._checkSectionlist({state})\n        );\n        // Any inplace editable update needs state refresh.\n        this.addEventListener(\n            this.element,\n            eventTypes.elementUpdated,\n            this._inplaceEditableHandler\n        );\n    }\n\n    /**\n     * Return the component watchers.\n     *\n     * @returns {Array} of watchers\n     */\n    getWatchers() {\n        return [\n            // Check section limit.\n            {watch: `course.sectionlist:updated`, handler: this._checkSectionlist},\n        ];\n    }\n\n    _dispatchClick(event) {\n        const target = event.target.closest(this.selectors.ACTIONLINK);\n        if (!target) {\n            return;\n        }\n        if (target.classList.contains(this.classes.DISABLED)) {\n            event.preventDefault();\n            return;\n        }\n\n        // Invoke proper method.\n        const actionName = target.dataset.action;\n        const methodName = this._actionMethodName(actionName);\n\n        if (this[methodName] !== undefined) {\n            this[methodName](target, event);\n            return;\n        }\n\n        // Check direct mutations or mutations handlers.\n        if (directMutations[actionName] !== undefined) {\n            if (typeof directMutations[actionName] === 'function') {\n                directMutations[actionName](target, event);\n                return;\n            }\n            this._requestMutationAction(target, event, directMutations[actionName]);\n            return;\n        }\n    }\n\n    _actionMethodName(name) {\n        const requestName = name.charAt(0).toUpperCase() + name.slice(1);\n        return `_request${requestName}`;\n    }\n\n    /**\n     * Check the section list and disable some options if needed.\n     *\n     * @param {Object} detail the update details.\n     * @param {Object} detail.state the state object.\n     */\n    _checkSectionlist({state}) {\n        // Disable \"add section\" actions if the course max sections has been exceeded.\n        this._setAddSectionLocked(state.course.sectionlist.length > state.course.maxsections);\n    }\n\n    /**\n     * Handle inplace editable updates.\n     *\n     * @param {Event} event the triggered event\n     * @private\n     */\n    _inplaceEditableHandler(event) {\n        const itemtype = event.detail?.ajaxreturn?.itemtype;\n        const itemid = parseInt(event.detail?.ajaxreturn?.itemid);\n        if (!Number.isFinite(itemid) || !itemtype) {\n            return;\n        }\n\n        if (itemtype === 'activityname') {\n            this.reactive.dispatch('cmState', [itemid]);\n            return;\n        }\n        // Sections uses sectionname for normal sections and sectionnamenl for the no link sections.\n        if (itemtype === 'sectionname' || itemtype === 'sectionnamenl') {\n            this.reactive.dispatch('sectionState', [itemid]);\n            return;\n        }\n    }\n\n    /**\n     * Return the ids represented by this element.\n     *\n     * Depending on the dataset attributes the action could represent a single id\n     * or a bulk actions with all the current selected ids.\n     *\n     * @param {HTMLElement} target\n     * @returns {Number[]} array of Ids\n     */\n    _getTargetIds(target) {\n        let ids = [];\n        if (target?.dataset?.id) {\n            ids.push(target.dataset.id);\n        }\n        const bulkType = target?.dataset?.bulk;\n        if (!bulkType) {\n            return ids;\n        }\n        const bulk = this.reactive.get('bulk');\n        if (bulk.enabled && bulk.selectedType === bulkType) {\n            ids = [...ids, ...bulk.selection];\n        }\n        return ids;\n    }\n\n    /**\n     * Handle a move section request.\n     *\n     * @param {Element} target the dispatch action element\n     * @param {Event} event the triggered event\n     */\n    async _requestMoveSection(target, event) {\n        // Check we have an id.\n        const sectionIds = this._getTargetIds(target);\n        if (sectionIds.length == 0) {\n            return;\n        }\n\n        event.preventDefault();\n\n        const pendingModalReady = new Pending(`courseformat/actions:prepareMoveSectionModal`);\n\n        // The section edit menu to refocus on end.\n        const editTools = this._getClosestActionMenuToogler(target);\n\n        // Collect section information from the state.\n        const exporter = this.reactive.getExporter();\n        const data = exporter.course(this.reactive.state);\n        let titleText = null;\n\n        // Add the target section id and title.\n        let sectionInfo = null;\n        if (sectionIds.length == 1) {\n            sectionInfo = this.reactive.get('section', sectionIds[0]);\n            data.sectionid = sectionInfo.id;\n            data.sectiontitle = sectionInfo.title;\n            data.information = await this.reactive.getFormatString('sectionmove_info', data.sectiontitle);\n            titleText = this.reactive.getFormatString('sectionmove_title');\n        } else {\n            data.information = await this.reactive.getFormatString('sectionsmove_info', sectionIds.length);\n            titleText = this.reactive.getFormatString('sectionsmove_title');\n        }\n\n\n        // Create the modal.\n        // Build the modal parameters from the event data.\n        const modal = await this._modalBodyRenderedPromise(Modal, {\n            title: titleText,\n            body: Templates.render('core_courseformat/local/content/movesection', data),\n        });\n\n        const modalBody = getFirst(modal.getBody());\n\n        // Disable current selected section ids.\n        sectionIds.forEach(sectionId => {\n            const currentElement = modalBody.querySelector(`${this.selectors.SECTIONLINK}[data-id='${sectionId}']`);\n            this._disableLink(currentElement);\n        });\n\n        // Setup keyboard navigation.\n        new ContentTree(\n            modalBody.querySelector(this.selectors.CONTENTTREE),\n            {\n                SECTION: this.selectors.SECTIONNODE,\n                TOGGLER: this.selectors.MODALTOGGLER,\n                COLLAPSE: this.selectors.MODALTOGGLER,\n            },\n            true\n        );\n\n        // Capture click.\n        modalBody.addEventListener('click', (event) => {\n            const target = event.target.closest('a');\n            if (!target || target.dataset.for != 'section' || target.dataset.id === undefined) {\n                return;\n            }\n            if (target.getAttribute('aria-disabled')) {\n                return;\n            }\n            event.preventDefault();\n            this.reactive.dispatch('sectionMoveAfter', sectionIds, target.dataset.id);\n            this._destroyModal(modal, editTools);\n        });\n\n        pendingModalReady.resolve();\n    }\n\n    /**\n     * Handle a move cm request.\n     *\n     * @param {Element} target the dispatch action element\n     * @param {Event} event the triggered event\n     */\n    async _requestMoveCm(target, event) {\n        // Check we have an id.\n        const cmIds = this._getTargetIds(target);\n        if (cmIds.length == 0) {\n            return;\n        }\n\n        event.preventDefault();\n\n        const pendingModalReady = new Pending(`courseformat/actions:prepareMoveCmModal`);\n\n        // The section edit menu to refocus on end.\n        const editTools = this._getClosestActionMenuToogler(target);\n\n        // Collect information from the state.\n        const exporter = this.reactive.getExporter();\n        const data = exporter.course(this.reactive.state);\n\n        let titleText = null;\n        if (cmIds.length == 1) {\n            const cmInfo = this.reactive.get('cm', cmIds[0]);\n            data.cmid = cmInfo.id;\n            data.cmname = cmInfo.name;\n            data.information = await this.reactive.getFormatString('cmmove_info', data.cmname);\n            if (cmInfo.hasdelegatedsection) {\n                titleText = this.reactive.getFormatString('cmmove_subsectiontitle');\n            } else {\n                titleText = this.reactive.getFormatString('cmmove_title');\n            }\n        } else {\n            data.information = await this.reactive.getFormatString('cmsmove_info', cmIds.length);\n            titleText = this.reactive.getFormatString('cmsmove_title');\n        }\n\n        // Create the modal.\n        // Build the modal parameters from the event data.\n        const modal = await this._modalBodyRenderedPromise(Modal, {\n            title: titleText,\n            body: Templates.render('core_courseformat/local/content/movecm', data),\n        });\n\n        const modalBody = getFirst(modal.getBody());\n\n        // Disable current selected section ids.\n        cmIds.forEach(cmId => {\n            const currentElement = modalBody.querySelector(`${this.selectors.CMLINK}[data-id='${cmId}']`);\n            this._disableLink(currentElement);\n        });\n\n        // Setup keyboard navigation.\n        new ContentTree(\n            modalBody.querySelector(this.selectors.CONTENTTREE),\n            {\n                SECTION: this.selectors.SECTIONNODE,\n                TOGGLER: this.selectors.MODALTOGGLER,\n                COLLAPSE: this.selectors.MODALTOGGLER,\n                ENTER: this.selectors.SECTIONLINK,\n            }\n        );\n\n        cmIds.forEach(cmId => {\n            const cmInfo = this.reactive.get('cm', cmId);\n            let selector;\n            if (!cmInfo.hasdelegatedsection) {\n                selector = `${this.selectors.CMLINK}[data-id='${cmId}']`;\n            } else {\n                selector = `${this.selectors.SECTIONLINK}[data-id='${cmInfo.sectionid}']`;\n            }\n            const currentElement = modalBody.querySelector(selector);\n            this._expandCmMoveModalParentSections(modalBody, currentElement);\n        });\n\n        modalBody.addEventListener('click', (event) => {\n            const target = event.target.closest('a');\n            if (!target || target.dataset.for === undefined || target.dataset.id === undefined) {\n                return;\n            }\n            if (target.getAttribute('aria-disabled')) {\n                return;\n            }\n            event.preventDefault();\n\n            let targetSectionId;\n            let targetCmId;\n            let droppedCmIds = [...cmIds];\n            if (target.dataset.for == 'cm') {\n                const dropData = exporter.cmDraggableData(this.reactive.state, target.dataset.id);\n                targetSectionId = dropData.sectionid;\n                targetCmId = dropData.nextcmid;\n            } else {\n                const section = this.reactive.get('section', target.dataset.id);\n                targetSectionId = target.dataset.id;\n                targetCmId = section?.cmlist[0];\n            }\n            const section = this.reactive.get('section', targetSectionId);\n            if (section.component) {\n                // Remove cmIds which are not allowed to be moved to this delegated section (mostly\n                // all other delegated cm).\n                droppedCmIds = droppedCmIds.filter(cmId => {\n                    const cmInfo = this.reactive.get('cm', cmId);\n                    return !cmInfo.hasdelegatedsection;\n                });\n            }\n            if (droppedCmIds.length === 0) {\n                return; // No cm to move.\n            }\n            this.reactive.dispatch('cmMove', droppedCmIds, targetSectionId, targetCmId);\n            this._destroyModal(modal, editTools);\n        });\n\n        pendingModalReady.resolve();\n    }\n\n    /**\n     * Expand all the modal tree branches that contains the element.\n     *\n     * @private\n     * @param {HTMLElement} modalBody the modal body element\n     * @param {HTMLElement} element the element to display\n     */\n    _expandCmMoveModalParentSections(modalBody, element) {\n        const sectionnode = element.closest(this.selectors.SECTIONNODE);\n        if (!sectionnode) {\n            return;\n        }\n\n        const toggler = sectionnode.querySelector(this.selectors.MODALTOGGLER);\n        let collapsibleId = toggler.dataset.target ?? toggler.getAttribute('href');\n        if (collapsibleId) {\n            // We cannot be sure we have # in the id element name.\n            collapsibleId = collapsibleId.replace('#', '');\n            const expandNode = modalBody.querySelector(`#${collapsibleId}`);\n            new Collapse(expandNode, {toggle: false}).show();\n        }\n\n        // Section are a tree structure, we need to expand all the parents.\n        this._expandCmMoveModalParentSections(modalBody, sectionnode.parentElement);\n    }\n\n    /**\n     * Handle a create section request.\n     *\n     * @param {Element} target the dispatch action element\n     * @param {Event} event the triggered event\n     */\n    async _requestAddSection(target, event) {\n        event.preventDefault();\n        this.reactive.dispatch('addSection', target.dataset.id ?? 0);\n    }\n\n    /**\n     * Handle a create subsection request.\n     *\n     * @deprecated since Moodle 5.0 MDL-83469.\n     * @todo MDL-83851 This will be deleted in Moodle 6.0.\n     * @param {Element} target the dispatch action element\n     * @param {Event} event the triggered event\n     */\n    async _requestAddModule(target, event) {\n        log.debug('AddModule action is deprecated. Use newModule instead');\n        event.preventDefault();\n        this.reactive.dispatch('addModule', target.dataset.modname, target.dataset.sectionnum, target.dataset.beforemod);\n    }\n\n    /**\n     * Handle a new create subsection request.\n     *\n     * @param {Element} target the dispatch action element\n     * @param {Event} event the triggered event\n     */\n    async _requestNewModule(target, event) {\n        event.preventDefault();\n        this.reactive.dispatch('newModule', target.dataset.modname, target.dataset.sectionid, target.dataset.beforemod);\n    }\n\n    /**\n     * Handle a delete section request.\n     *\n     * @param {Element} target the dispatch action element\n     * @param {Event} event the triggered event\n     */\n    async _requestDeleteSection(target, event) {\n        const sectionIds = this._getTargetIds(target);\n        if (sectionIds.length == 0) {\n            return;\n        }\n\n        event.preventDefault();\n\n        // We don't need confirmation to delete empty sections.\n        let needsConfirmation = sectionIds.some(sectionId => {\n            const sectionInfo = this.reactive.get('section', sectionId);\n            const cmList = sectionInfo.cmlist ?? [];\n            return (cmList.length || sectionInfo.hassummary || sectionInfo.rawtitle);\n        });\n        if (!needsConfirmation) {\n            this._dispatchSectionDelete(sectionIds, target);\n            return;\n        }\n\n        let bodyText = null;\n        let titleText = null;\n        if (sectionIds.length == 1) {\n            titleText = this.reactive.getFormatString('sectiondelete_title');\n            const sectionInfo = this.reactive.get('section', sectionIds[0]);\n            bodyText = this.reactive.getFormatString('sectiondelete_info', {name: sectionInfo.title});\n        } else {\n            titleText = this.reactive.getFormatString('sectionsdelete_title');\n            bodyText = this.reactive.getFormatString('sectionsdelete_info', {count: sectionIds.length});\n        }\n\n        const modal = await this._modalBodyRenderedPromise(ModalDeleteCancel, {\n            title: titleText,\n            body: bodyText,\n        });\n\n        modal.getRoot().on(\n            ModalEvents.delete,\n            e => {\n                // Stop the default save button behaviour which is to close the modal.\n                e.preventDefault();\n                modal.destroy();\n                this._dispatchSectionDelete(sectionIds, target);\n            }\n        );\n    }\n\n    /**\n     * Dispatch the section delete action and handle the redirection if necessary.\n     *\n     * @param {Array} sectionIds  the IDs of the sections to delete.\n     * @param {Element} target the dispatch action element\n     */\n    async _dispatchSectionDelete(sectionIds, target) {\n        await this.reactive.dispatch('sectionDelete', sectionIds);\n        if (target.baseURI.includes('section.php')) {\n            // Redirect to the course main page if the section is the current page.\n            window.location.href = this.reactive.get('course').baseurl;\n        }\n    }\n\n    /**\n     * Handle a toggle cm selection.\n     *\n     * @param {Element} target the dispatch action element\n     * @param {Event} event the triggered event\n     */\n    async _requestToggleSelectionCm(target, event) {\n        toggleBulkSelectionAction(this.reactive, target, event, 'cm');\n    }\n\n    /**\n     * Handle a toggle section selection.\n     *\n     * @param {Element} target the dispatch action element\n     * @param {Event} event the triggered event\n     */\n    async _requestToggleSelectionSection(target, event) {\n        toggleBulkSelectionAction(this.reactive, target, event, 'section');\n    }\n\n    /**\n     * Basic mutation action helper.\n     *\n     * @param {Element} target the dispatch action element\n     * @param {Event} event the triggered event\n     * @param {string} mutationName the mutation name\n     */\n    async _requestMutationAction(target, event, mutationName) {\n        if (!target.dataset.id && target.dataset.for !== 'bulkaction') {\n            return;\n        }\n        event.preventDefault();\n        if (target.dataset.for === 'bulkaction') {\n            // If the mutation is a bulk action we use the current selection.\n            this.reactive.dispatch(mutationName, this.reactive.get('bulk').selection);\n        } else {\n            this.reactive.dispatch(mutationName, [target.dataset.id]);\n        }\n    }\n\n    /**\n     * Handle a course permalink modal request.\n     *\n     * @param {Element} target the dispatch action element\n     * @param {Event} event the triggered event\n     */\n    _requestPermalink(target, event) {\n        event.preventDefault();\n        ModalCopyToClipboard.create(\n            {\n                text: target.getAttribute('href'),\n            },\n            getString('sectionlink', 'course')\n        );\n        return;\n    }\n\n    /**\n     * Handle a course module duplicate request.\n     *\n     * @param {Element} target the dispatch action element\n     * @param {Event} event the triggered event\n     */\n    async _requestCmDuplicate(target, event) {\n        const cmIds = this._getTargetIds(target);\n        if (cmIds.length == 0) {\n            return;\n        }\n        const sectionId = target.dataset.sectionid ?? null;\n        event.preventDefault();\n        this.reactive.dispatch('cmDuplicate', cmIds, sectionId);\n    }\n\n    /**\n     * Handle a delete cm request.\n     *\n     * @param {Element} target the dispatch action element\n     * @param {Event} event the triggered event\n     */\n    async _requestCmDelete(target, event) {\n        const cmIds = this._getTargetIds(target);\n        if (cmIds.length == 0) {\n            return;\n        }\n\n        event.preventDefault();\n\n        let bodyText = null;\n        let titleText = null;\n        let delegatedsection = null;\n        if (cmIds.length == 1) {\n            const cmInfo = this.reactive.get('cm', cmIds[0]);\n            if (cmInfo.hasdelegatedsection) {\n                delegatedsection = cmInfo.delegatesectionid;\n                titleText = this.reactive.getFormatString('cmdelete_subsectiontitle');\n                bodyText = getString(\n                    'sectiondelete_info',\n                    'core_courseformat',\n                    {\n                        type: cmInfo.modname,\n                        name: cmInfo.name,\n                    }\n                );\n            } else {\n                titleText = this.reactive.getFormatString('cmdelete_title');\n                bodyText = getString(\n                    'cmdelete_info',\n                    'core_courseformat',\n                    {\n                        type: cmInfo.modname,\n                        name: cmInfo.name,\n                    }\n                );\n            }\n        } else {\n            titleText = getString('cmsdelete_title', 'core_courseformat');\n            bodyText = getString(\n                'cmsdelete_info',\n                'core_courseformat',\n                {count: cmIds.length}\n            );\n        }\n\n        const modal = await this._modalBodyRenderedPromise(ModalDeleteCancel, {\n            title: titleText,\n            body: bodyText,\n        });\n\n        modal.getRoot().on(\n            ModalEvents.delete,\n            e => {\n                // Stop the default save button behaviour which is to close the modal.\n                e.preventDefault();\n                modal.destroy();\n                this.reactive.dispatch('cmDelete', cmIds);\n                if (cmIds.length == 1 && delegatedsection && target.baseURI.includes('section.php')) {\n                    // Redirect to the course main page if the subsection is the current page.\n                    let parameters = new URLSearchParams(window.location.search);\n                    if (parameters.has('id') && parameters.get('id') == delegatedsection) {\n                        this._dispatchSectionDelete([delegatedsection], target);\n                    }\n                }\n            }\n        );\n    }\n\n    /**\n     * Handle a cm availability change request.\n     *\n     * @param {Element} target the dispatch action element\n     */\n    async _requestCmAvailability(target) {\n        const cmIds = this._getTargetIds(target);\n        if (cmIds.length == 0) {\n            return;\n        }\n        // Show the availability modal to decide which action to trigger.\n        const exporter = this.reactive.getExporter();\n        const data = {\n            allowstealth: exporter.canUseStealth(this.reactive.state, cmIds),\n        };\n        const modal = await this._modalBodyRenderedPromise(ModalSaveCancel, {\n            title: getString('availability', 'core'),\n            body: Templates.render('core_courseformat/local/content/cm/availabilitymodal', data),\n            saveButtonText: getString('apply', 'core'),\n        });\n\n        this._setupMutationRadioButtonModal(modal, cmIds);\n    }\n\n    /**\n     * Handle a section availability change request.\n     *\n     * @param {Element} target the dispatch action element\n     */\n    async _requestSectionAvailability(target) {\n        const sectionIds = this._getTargetIds(target);\n        if (sectionIds.length == 0) {\n            return;\n        }\n        const title = (sectionIds.length == 1) ? 'sectionavailability_title' : 'sectionsavailability_title';\n        // Show the availability modal to decide which action to trigger.\n        const modal = await this._modalBodyRenderedPromise(ModalSaveCancel, {\n            title: this.reactive.getFormatString(title),\n            body: Templates.render('core_courseformat/local/content/section/availabilitymodal', []),\n            saveButtonText: getString('apply', 'core'),\n        });\n\n        this._setupMutationRadioButtonModal(modal, sectionIds);\n    }\n\n    /**\n     * Add events to a mutation selector radio buttons modal.\n     * @param {Modal} modal\n     * @param {Number[]} ids the section or cm ids to apply the mutation\n     */\n    _setupMutationRadioButtonModal(modal, ids) {\n        // The save button is not enabled until the user selects an option.\n        modal.setButtonDisabled('save', true);\n\n        const submitFunction = (radio) => {\n            const mutation = radio?.value;\n            if (!mutation) {\n                return false;\n            }\n            this.reactive.dispatch(mutation, ids);\n            return true;\n        };\n\n        const modalBody = getFirst(modal.getBody());\n        const radioOptions = modalBody.querySelectorAll(this.selectors.OPTIONSRADIO);\n        radioOptions.forEach(radio => {\n            radio.addEventListener('change', () => {\n                modal.setButtonDisabled('save', false);\n            });\n            radio.parentNode.addEventListener('click', () => {\n                radio.checked = true;\n                modal.setButtonDisabled('save', false);\n            });\n            radio.parentNode.addEventListener('dblclick', dbClickEvent => {\n                if (submitFunction(radio)) {\n                    dbClickEvent.preventDefault();\n                    modal.destroy();\n                }\n            });\n        });\n\n        modal.getRoot().on(\n            ModalEvents.save,\n            () => {\n                const radio = modalBody.querySelector(`${this.selectors.OPTIONSRADIO}:checked`);\n                submitFunction(radio);\n            }\n        );\n    }\n\n    /**\n     * Disable all add sections actions.\n     *\n     * @param {boolean} locked the new locked value.\n     */\n    _setAddSectionLocked(locked) {\n        const targets = this.getElements(this.selectors.ADDSECTIONREGION);\n        targets.forEach(element => {\n            element.classList.toggle(this.classes.DISABLED, locked);\n            const addSectionElement = element.querySelector(this.selectors.ADDSECTION);\n            addSectionElement.classList.toggle(this.classes.DISABLED, locked);\n            this.setElementLocked(addSectionElement, locked);\n            // We tweak the element to show a tooltip as a title attribute.\n            if (locked) {\n                getString('sectionaddmax', 'core_courseformat')\n                    .then((text) => addSectionElement.setAttribute('title', text))\n                    .catch(Notification.exception);\n                addSectionElement.style.pointerEvents = null; // Unlocks the pointer events.\n                addSectionElement.style.userSelect = null; // Unlocks the pointer events.\n            } else {\n                addSectionElement.setAttribute('title', addSectionElement.dataset.addSections);\n            }\n        });\n        const courseAddSection = this.getElement(this.selectors.COURSEADDSECTION);\n        if (courseAddSection) {\n            const addSection = courseAddSection.querySelector(this.selectors.ADDSECTION);\n            addSection.classList.toggle(this.classes.DISPLAYNONE, locked);\n            const noMoreSections = courseAddSection.querySelector(this.selectors.MAXSECTIONSWARNING);\n            noMoreSections.classList.toggle(this.classes.DISPLAYNONE, !locked);\n        }\n    }\n\n    /**\n     * Replace an element with a copy with a different tag name.\n     *\n     * @param {Element} element the original element\n     */\n    _disableLink(element) {\n        if (element) {\n            element.style.pointerEvents = 'none';\n            element.style.userSelect = 'none';\n            element.classList.add(this.classes.DISABLED);\n            element.classList.add(this.classes.ITALIC);\n            element.setAttribute('aria-disabled', true);\n            element.addEventListener('click', event => event.preventDefault());\n        }\n    }\n\n    /**\n     * Render a modal and return a body ready promise.\n     *\n     * @param {Modal} ModalClass the modal class\n     * @param {object} modalParams the modal params\n     * @return {Promise} the modal body ready promise\n     */\n    _modalBodyRenderedPromise(ModalClass, modalParams) {\n        return new Promise((resolve, reject) => {\n            ModalClass.create(modalParams).then((modal) => {\n                modal.setRemoveOnClose(true);\n                // Handle body loading event.\n                modal.getRoot().on(ModalEvents.bodyRendered, () => {\n                    resolve(modal);\n                });\n                // Configure some extra modal params.\n                if (modalParams.saveButtonText !== undefined) {\n                    modal.setSaveButtonText(modalParams.saveButtonText);\n                }\n                if (modalParams.deleteButtonText !== undefined) {\n                    modal.setDeleteButtonText(modalParams.saveButtonText);\n                }\n                modal.show();\n                return;\n            }).catch(() => {\n                reject(`Cannot load modal content`);\n            });\n        });\n    }\n\n    /**\n     * Hide and later destroy a modal.\n     *\n     * Behat will fail if we remove the modal while some boostrap collapse is executing.\n     *\n     * @param {Modal} modal\n     * @param {HTMLElement} element the dom element to focus on.\n     */\n    _destroyModal(modal, element) {\n        modal.hide();\n        const pendingDestroy = new Pending(`courseformat/actions:destroyModal`);\n        if (element) {\n            element.focus();\n        }\n        setTimeout(() =>{\n            modal.destroy();\n            pendingDestroy.resolve();\n        }, 500);\n    }\n\n    /**\n     * Get the closest actions menu toggler to an action element.\n     *\n     * @param {HTMLElement} element the action link element\n     * @returns {HTMLElement|undefined}\n     */\n    _getClosestActionMenuToogler(element) {\n        const actionMenu = element.closest(this.selectors.ACTIONMENU);\n        if (!actionMenu) {\n            return undefined;\n        }\n        return actionMenu.querySelector(this.selectors.ACTIONMENUTOGGLER);\n    }\n}\n"],"names":["directMutations","sectionHide","sectionShow","cmHide","cmShow","cmStealth","cmMoveRight","cmMoveLeft","cmNoGroups","cmSeparateGroups","cmVisibleGroups","BaseComponent","create","name","selectors","ACTIONLINK","SECTIONLINK","CMLINK","SECTIONNODE","MODALTOGGLER","ADDSECTION","CONTENTTREE","ACTIONMENU","ACTIONMENUTOGGLER","OPTIONSRADIO","COURSEADDSECTION","MAXSECTIONSWARNING","ADDSECTIONREGION","classes","DISABLED","ITALIC","DISPLAYNONE","actions","action","mutationReference","Object","entries","Error","stateReady","state","addEventListener","this","element","_dispatchClick","_checkSectionlist","CourseEvents","sectionRefreshed","eventTypes","elementUpdated","_inplaceEditableHandler","getWatchers","watch","handler","event","target","closest","classList","contains","preventDefault","actionName","dataset","methodName","_actionMethodName","undefined","_requestMutationAction","requestName","charAt","toUpperCase","slice","_setAddSectionLocked","course","sectionlist","length","maxsections","itemtype","detail","_event$detail","ajaxreturn","_event$detail$ajaxret","itemid","parseInt","_event$detail2","_event$detail2$ajaxre","Number","isFinite","reactive","dispatch","_getTargetIds","ids","_target$dataset","id","push","bulkType","_target$dataset2","bulk","get","enabled","selectedType","selection","sectionIds","pendingModalReady","Pending","editTools","_getClosestActionMenuToogler","data","getExporter","titleText","sectionInfo","sectionid","sectiontitle","title","information","getFormatString","modal","_modalBodyRenderedPromise","Modal","body","Templates","render","modalBody","getBody","forEach","sectionId","currentElement","querySelector","_disableLink","ContentTree","SECTION","TOGGLER","COLLAPSE","for","getAttribute","_destroyModal","resolve","cmIds","exporter","cmInfo","cmid","cmname","hasdelegatedsection","cmId","ENTER","selector","_expandCmMoveModalParentSections","targetSectionId","targetCmId","droppedCmIds","dropData","cmDraggableData","nextcmid","section","cmlist","component","filter","sectionnode","toggler","collapsibleId","replace","expandNode","Collapse","toggle","show","parentElement","debug","modname","sectionnum","beforemod","some","hassummary","rawtitle","_dispatchSectionDelete","bodyText","count","ModalDeleteCancel","getRoot","on","ModalEvents","delete","e","destroy","baseURI","includes","window","location","href","baseurl","mutationName","_requestPermalink","text","delegatedsection","delegatesectionid","type","parameters","URLSearchParams","search","has","allowstealth","canUseStealth","ModalSaveCancel","saveButtonText","_setupMutationRadioButtonModal","setButtonDisabled","submitFunction","radio","mutation","value","querySelectorAll","parentNode","checked","dbClickEvent","save","locked","getElements","addSectionElement","setElementLocked","then","setAttribute","catch","Notification","exception","style","pointerEvents","userSelect","addSections","courseAddSection","getElement","add","ModalClass","modalParams","Promise","reject","setRemoveOnClose","bodyRendered","setSaveButtonText","deleteButtonText","setDeleteButtonText","hide","pendingDestroy","focus","setTimeout","actionMenu"],"mappings":"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"}