%PDF- %PDF-
Direktori : /home/vacivi36/ava/course/format/amd/src/local/courseeditor/ |
Current File : /home/vacivi36/ava/course/format/amd/src/local/courseeditor/contenttree.js |
// This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Course index keyboard navigation and aria-tree compatibility. * * Node tree and bootstrap collapsibles don't use the same HTML structure. However, * all keybindings and logic is compatible. This class translate the primitive opetations * to a bootstrap collapsible structure. * * @module core_courseformat/local/courseindex/keyboardnav * @class core_courseformat/local/courseindex/keyboardnav * @copyright 2021 Ferran Recio <ferran@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ // The core/tree uses jQuery to expand all nodes. import jQuery from 'jquery'; import Tree from 'core/tree'; import {getList} from 'core/normalise'; export default class extends Tree { /** * Setup the core/tree keyboard navigation. * * @param {Element|undefined} mainElement an alternative main element in case it is not from the parent component * @param {Object|undefined} selectors alternative selectors * @param {boolean} preventcache if the elements cache must be disabled. */ constructor(mainElement, selectors, preventcache) { // Init this value with the parent DOM element. super(mainElement); // Get selectors from parent. this.selectors = { SECTION: selectors.SECTION, TOGGLER: selectors.TOGGLER, COLLAPSE: selectors.COLLAPSE, ENTER: selectors.ENTER ?? selectors.TOGGLER, }; // The core/tree library saves the visible elements cache inside the main tree node. // However, in edit mode content can change suddenly so we need to refresh caches when needed. if (preventcache) { this._getVisibleItems = this.getVisibleItems; this.getVisibleItems = () => { this.refreshVisibleItemsCache(); return this._getVisibleItems(); }; } // All jQuery events can be replaced when MDL-71979 is integrated. this.treeRoot.on('hidden.bs.collapse shown.bs.collapse', () => { this.refreshVisibleItemsCache(); }); // Register a custom callback for pressing enter key. this.registerEnterCallback(this.enterCallback.bind(this)); } /** * Return the current active node. * * @return {Element|undefined} the active item if any */ getActiveItem() { const activeItem = this.treeRoot.data('activeItem'); if (activeItem) { return getList(activeItem)[0]; } return undefined; } /** * Handle enter key on a collpasible node. * * @param {JQuery} jQueryItem the jQuery object */ enterCallback(jQueryItem) { const item = getList(jQueryItem)[0]; if (this.isGroupItem(jQueryItem)) { // Group elements is like clicking a topic but without loosing the focus. const enter = item.querySelector(this.selectors.ENTER); if (enter.getAttribute('href') !== '#') { window.location.href = enter.getAttribute('href'); } enter.click(); } else { // Activity links just follow the link href. const link = item.querySelector('a'); if (link.getAttribute('href') !== '#') { window.location.href = link.getAttribute('href'); } else { link.click(); } return; } } /** * Handle an item click. * * @param {Event} event the click event * @param {jQuery} jQueryItem the item clicked */ handleItemClick(event, jQueryItem) { const isChevron = event.target.closest(this.selectors.COLLAPSE); // Only chevron clicks toogle the sections always. if (isChevron) { super.handleItemClick(event, jQueryItem); return; } // This is a title or activity name click. jQueryItem.focus(); if (this.isGroupItem(jQueryItem)) { this.expandGroup(jQueryItem); } } /** * Check if a gorup item is collapsed. * * @param {JQuery} jQueryItem the jQuery object * @returns {boolean} if the element is collapsed */ isGroupCollapsed(jQueryItem) { const item = getList(jQueryItem)[0]; const toggler = item.querySelector(`[aria-expanded]`); return toggler.getAttribute('aria-expanded') === 'false'; } /** * Toggle a group item. * * @param {JQuery} item the jQuery object */ toggleGroup(item) { // All jQuery in this segment of code can be replaced when MDL-71979 is integrated. const toggler = item.find(this.selectors.COLLAPSE); let collapsibleId = toggler.data('target') ?? toggler.attr('href'); if (!collapsibleId) { return; } collapsibleId = collapsibleId.replace('#', ''); // Bootstrap 4 uses jQuery to interact with collapsibles. const collapsible = jQuery(`#${collapsibleId}`); if (collapsible.length) { jQuery(`#${collapsibleId}`).collapse('toggle'); } } /** * Expand a group item. * * @param {JQuery} item the jQuery object */ expandGroup(item) { if (this.isGroupCollapsed(item)) { this.toggleGroup(item); } } /** * Collpase a group item. * * @param {JQuery} item the jQuery object */ collapseGroup(item) { if (!this.isGroupCollapsed(item)) { this.toggleGroup(item); } } /** * Expand all groups. */ expandAllGroups() { const togglers = getList(this.treeRoot)[0].querySelectorAll(this.selectors.SECTION); togglers.forEach(item => { this.expandGroup(jQuery(item)); }); } }