Proyectos de Subversion Moodle

Rev

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

{"version":3,"file":"debugpanel.min.js","sources":["../../../src/local/reactive/debugpanel.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 * Reactive module debug panel.\n *\n * This module contains all the UI components for the reactive debug tools.\n * Those tools are only available if the debug is enables and could be used\n * from the footer.\n *\n * @module     core/local/reactive/debugpanel\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, DragDrop, debug} from 'core/reactive';\nimport log from 'core/log';\nimport {debounce} from 'core/utils';\n\n/**\n * Init the main reactive panel.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n */\nexport const init = (target, selectors) => {\n    const element = document.getElementById(target);\n    // Check if the debug reactive module is available.\n    if (debug === undefined) {\n        element.remove();\n        return;\n    }\n    // Create the main component.\n    new GlobalDebugPanel({\n        element,\n        reactive: debug,\n        selectors,\n    });\n};\n\n/**\n * Init an instance reactive subpanel.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n */\nexport const initsubpanel = (target, selectors) => {\n    const element = document.getElementById(target);\n    // Check if the debug reactive module is available.\n    if (debug === undefined) {\n        element.remove();\n        return;\n    }\n    // Create the main component.\n    new DebugInstanceSubpanel({\n        element,\n        reactive: debug,\n        selectors,\n    });\n};\n\n/**\n * Component for the main reactive dev panel.\n *\n * This component shows the list of reactive instances and handle the buttons\n * to open a specific instance panel.\n */\nclass GlobalDebugPanel extends BaseComponent {\n\n    /**\n     * Constructor hook.\n     */\n    create() {\n        // Optional component name for debugging.\n        this.name = 'GlobalDebugPanel';\n        // Default query selectors.\n        this.selectors = {\n            LOADERS: `[data-for='loaders']`,\n            SUBPANEL: `[data-for='subpanel']`,\n            NOINSTANCES: `[data-for='noinstances']`,\n            LOG: `[data-for='log']`,\n        };\n        this.classes = {\n            HIDE: `d-none`,\n        };\n        // The list of loaded debuggers.\n        this.subPanels = new Set();\n    }\n\n    /**\n     * Initial state ready method.\n     *\n     * @param {object} state the initial state\n     */\n    stateReady(state) {\n        this._updateReactivesPanels({state});\n        // Remove loading wheel.\n        this.getElement(this.selectors.SUBPANEL).innerHTML = '';\n    }\n\n    /**\n     * Component watchers.\n     *\n     * @returns {Array} of watchers\n     */\n    getWatchers() {\n        return [\n            {watch: `reactives:created`, handler: this._updateReactivesPanels},\n        ];\n    }\n\n    /**\n     * Update the list of reactive instances.\n     * @param {Object} args\n     * @param {Object} args.state the current state\n     */\n    _updateReactivesPanels({state}) {\n        this.getElement(this.selectors.NOINSTANCES)?.classList?.toggle(\n            this.classes.HIDE,\n            state.reactives.size > 0\n        );\n        // Generate loading buttons.\n        state.reactives.forEach(\n            instance => {\n                this._createLoader(instance);\n            }\n        );\n    }\n\n    /**\n     * Create a debug panel button for a specific reactive instance.\n     *\n     * @param {object} instance hte instance data\n     */\n    _createLoader(instance) {\n        if (this.subPanels.has(instance.id)) {\n            return;\n        }\n        this.subPanels.add(instance.id);\n        const loaders = this.getElement(this.selectors.LOADERS);\n        const btn = document.createElement(\"button\");\n        btn.innerHTML = instance.id;\n        btn.dataset.id = instance.id;\n        loaders.appendChild(btn);\n        // Add click event.\n        this.addEventListener(btn, 'click', () => this._openPanel(btn, instance));\n    }\n\n    /**\n     * Open a debug panel.\n     *\n     * @param {Element} btn the button element\n     * @param {object} instance the instance data\n     */\n    async _openPanel(btn, instance) {\n        try {\n            const target = this.getElement(this.selectors.SUBPANEL);\n            const data = {...instance};\n            await this.renderComponent(target, 'core/local/reactive/debuginstancepanel', data);\n        } catch (error) {\n            log.error('Cannot load reactive debug subpanel');\n            throw error;\n        }\n    }\n}\n\n/**\n * Component for the main reactive dev panel.\n *\n * This component shows the list of reactive instances and handle the buttons\n * to open a specific instance panel.\n */\nclass DebugInstanceSubpanel extends BaseComponent {\n\n    /**\n     * Constructor hook.\n     */\n    create() {\n        // Optional component name for debugging.\n        this.name = 'DebugInstanceSubpanel';\n        // Default query selectors.\n        this.selectors = {\n            NAME: `[data-for='name']`,\n            CLOSE: `[data-for='close']`,\n            READMODE: `[data-for='readmode']`,\n            HIGHLIGHT: `[data-for='highlight']`,\n            LOG: `[data-for='log']`,\n            STATE: `[data-for='state']`,\n            CLEAN: `[data-for='clean']`,\n            PIN: `[data-for='pin']`,\n            SAVE: `[data-for='save']`,\n            INVALID: `[data-for='invalid']`,\n        };\n        this.id = this.element.dataset.id;\n        this.controller = M.reactive[this.id];\n\n        // The component is created always pinned.\n        this.draggable = false;\n        // We want the element to be dragged like modal.\n        this.relativeDrag = true;\n        // Save warning (will be loaded when state is ready.\n        this.strings = {\n            savewarning: '',\n        };\n    }\n\n    /**\n     * Initial state ready method.\n     *\n     */\n    stateReady() {\n        // Enable drag and drop.\n        this.dragdrop = new DragDrop(this);\n\n        // Close button.\n        this.addEventListener(\n            this.getElement(this.selectors.CLOSE),\n            'click',\n            this.remove\n        );\n        // Highlight button.\n        if (this.controller.highlight) {\n            this._toggleButtonText(this.getElement(this.selectors.HIGHLIGHT));\n        }\n        this.addEventListener(\n            this.getElement(this.selectors.HIGHLIGHT),\n            'click',\n            () => {\n                this.controller.highlight = !this.controller.highlight;\n                this._toggleButtonText(this.getElement(this.selectors.HIGHLIGHT));\n            }\n        );\n        // Edit mode button.\n        this.addEventListener(\n            this.getElement(this.selectors.READMODE),\n            'click',\n            this._toggleEditMode\n        );\n        // Clean log and state.\n        this.addEventListener(\n            this.getElement(this.selectors.CLEAN),\n            'click',\n            this._cleanAreas\n        );\n        // Unpin panel butotn.\n        this.addEventListener(\n            this.getElement(this.selectors.PIN),\n            'click',\n            this._togglePin\n        );\n        // Save button, state format error message and state textarea.\n        this.getElement(this.selectors.SAVE).disabled = true;\n\n        this.addEventListener(\n            this.getElement(this.selectors.STATE),\n            'keyup',\n            debounce(this._checkJSON.bind(this), 500)\n        );\n\n        this.addEventListener(\n            this.getElement(this.selectors.SAVE),\n            'click',\n            this._saveState\n        );\n        // Save the default save warning message.\n        this.strings.savewarning = this.getElement(this.selectors.INVALID)?.innerHTML ?? '';\n        // Add current state.\n        this._refreshState();\n    }\n\n    /**\n     * Remove all subcomponents dependencies.\n     */\n    destroy() {\n        if (this.dragdrop !== undefined) {\n            this.dragdrop.unregister();\n        }\n    }\n\n    /**\n     * Component watchers.\n     *\n     * @returns {Array} of watchers\n     */\n    getWatchers() {\n        return [\n            {watch: `reactives[${this.id}].lastChanges:updated`, handler: this._refreshLog},\n            {watch: `reactives[${this.id}].modified:updated`, handler: this._refreshState},\n            {watch: `reactives[${this.id}].readOnly:updated`, handler: this._refreshReadOnly},\n        ];\n    }\n\n    /**\n     * Wtacher method to refresh the log panel.\n     *\n     * @param {object} args\n     * @param {HTMLElement} args.element\n     */\n    _refreshLog({element}) {\n        const list = element?.lastChanges ?? [];\n\n        const logContent = list.join(\"\\n\");\n        // Append last log.\n        const target = this.getElement(this.selectors.LOG);\n        target.value += `\\n\\n= Transaction =\\n ${logContent}`;\n        target.scrollTop = target.scrollHeight;\n    }\n\n    /**\n     * Listener method to clean the log area.\n     */\n    _cleanAreas() {\n        let target = this.getElement(this.selectors.LOG);\n        target.value = '';\n\n        this._refreshState();\n    }\n\n    /**\n     * Watcher to refresh the state information.\n     */\n    _refreshState() {\n        const target = this.getElement(this.selectors.STATE);\n        target.value = JSON.stringify(this.controller.state, null, 4);\n    }\n\n    /**\n     * Watcher to update the read only information.\n     */\n    _refreshReadOnly() {\n        // Toggle the read mode button.\n        const target = this.getElement(this.selectors.READMODE);\n        if (target.dataset.readonly === undefined) {\n            target.dataset.readonly = target.innerHTML;\n        }\n        if (this.controller.readOnly) {\n            target.innerHTML = target.dataset.readonly;\n        } else {\n            target.innerHTML = target.dataset.alt;\n        }\n    }\n\n    /**\n     * Listener to toggle the edit mode of the component.\n     */\n    _toggleEditMode() {\n        this.controller.readOnly = !this.controller.readOnly;\n    }\n\n    /**\n     * Check that the edited state JSON is valid.\n     *\n     * Not all valid JSON are suitable for transforming the state. For example,\n     * the first level attributes cannot change the type.\n     *\n     * @return {undefined|array} Array of state updates.\n     */\n    _checkJSON() {\n        const invalid = this.getElement(this.selectors.INVALID);\n        const save = this.getElement(this.selectors.SAVE);\n\n        const edited = this.getElement(this.selectors.STATE).value;\n\n        const currentStateData = this.controller.stateData;\n\n        // Check if the json is tha same as state.\n        if (edited == JSON.stringify(this.controller.state, null, 4)) {\n            invalid.style.color = '';\n            invalid.innerHTML = '';\n            save.disabled = true;\n            return undefined;\n        }\n\n        // Check if the json format is valid.\n        try {\n            const newState = JSON.parse(edited);\n            // Check the first level did not change types.\n            const result = this._generateStateUpdates(currentStateData, newState);\n            // Enable save button.\n            invalid.style.color = '';\n            invalid.innerHTML = this.strings.savewarning;\n            save.disabled = false;\n            return result;\n        } catch (error) {\n            invalid.style.color = 'red';\n            invalid.innerHTML = error.message ?? 'Invalid JSON sctructure';\n            save.disabled = true;\n            return undefined;\n        }\n    }\n\n    /**\n     * Listener to save the current edited state into the real state.\n     */\n    _saveState() {\n        const updates = this._checkJSON();\n        if (!updates) {\n            return;\n        }\n        // Sent the updates to the state manager.\n        this.controller.processUpdates(updates);\n    }\n\n    /**\n     * Check that the edited state JSON is valid.\n     *\n     * Not all valid JSON are suitable for transforming the state. For example,\n     * the first level attributes cannot change the type. This method do a two\n     * steps comparison between the current state data and the new state data.\n     *\n     * A reactive state cannot be overridden like any other variable. To keep\n     * the watchers updated is necessary to transform the current state into\n     * the new one. As a result, this method generates all the necessary state\n     * updates to convert the state into the new state.\n     *\n     * @param {object} currentStateData\n     * @param {object} newStateData\n     * @return {array} Array of state updates.\n     * @throws {Error} is the structure is not compatible\n     */\n    _generateStateUpdates(currentStateData, newStateData) {\n\n        const updates = [];\n\n        const ids = {};\n\n        // Step 1: Add all overrides newStateData.\n        for (const [key, newValue] of Object.entries(newStateData)) {\n            // Check is it is new.\n            if (Array.isArray(newValue)) {\n                ids[key] = {};\n                newValue.forEach(element => {\n                    if (element.id === undefined) {\n                        throw Error(`Array ${key} element without id attribute`);\n                    }\n                    updates.push({\n                        name: key,\n                        action: 'override',\n                        fields: element,\n                    });\n                    const index = String(element.id).valueOf();\n                    ids[key][index] = true;\n                });\n            } else {\n                updates.push({\n                    name: key,\n                    action: 'override',\n                    fields: newValue,\n                });\n            }\n        }\n        // Step 2: delete unnecesary data from currentStateData.\n        for (const [key, oldValue] of Object.entries(currentStateData)) {\n            let deleteField = false;\n            // Check if the attribute is still there.\n            if (newStateData[key] === undefined) {\n                deleteField = true;\n            }\n            if (Array.isArray(oldValue)) {\n                if (!deleteField && ids[key] === undefined) {\n                    throw Error(`Array ${key} cannot change to object.`);\n                }\n                oldValue.forEach(element => {\n                    const index = String(element.id).valueOf();\n                    let deleteEntry = deleteField;\n                    // Check if the id is there.\n                    if (!deleteEntry && ids[key][index] === undefined) {\n                        deleteEntry = true;\n                    }\n                    if (deleteEntry) {\n                        updates.push({\n                            name: key,\n                            action: 'delete',\n                            fields: element,\n                        });\n                    }\n                });\n            } else {\n                if (!deleteField && ids[key] !== undefined) {\n                    throw Error(`Object ${key} cannot change to array.`);\n                }\n                if (deleteField) {\n                    updates.push({\n                        name: key,\n                        action: 'delete',\n                        fields: oldValue,\n                    });\n                }\n            }\n        }\n        // Delete all elements without action.\n        return updates;\n    }\n\n    // Drag and drop methods.\n\n    /**\n     * Get the draggable data of this component.\n     *\n     * @returns {Object} exported course module drop data\n     */\n    getDraggableData() {\n        return this.draggable;\n    }\n\n    /**\n     * The element drop end hook.\n     *\n     * @param {Object} dropdata the dropdata\n     * @param {Event} event the dropdata\n     */\n    dragEnd(dropdata, event) {\n        this.element.style.top = `${event.newFixedTop}px`;\n        this.element.style.left = `${event.newFixedLeft}px`;\n    }\n\n    /**\n     * Pin and unpin the panel.\n     */\n    _togglePin() {\n        this.draggable = !this.draggable;\n        this.dragdrop.setDraggable(this.draggable);\n        if (this.draggable) {\n            this._unpin();\n        } else {\n            this._pin();\n        }\n    }\n\n    /**\n     * Unpin the panel form the footer.\n     */\n    _unpin() {\n        // Find the initial spot.\n        const pageCenterY = window.innerHeight / 2;\n        const pageCenterX = window.innerWidth / 2;\n        // Put the element in the middle of the screen\n        const style = {\n            position: 'fixed',\n            resize: 'both',\n            overflow: 'auto',\n            height: '400px',\n            width: '400px',\n            top: `${pageCenterY - 200}px`,\n            left: `${pageCenterX - 200}px`,\n        };\n        Object.assign(this.element.style, style);\n        // Small also the text areas.\n        this.getElement(this.selectors.STATE).style.height = '50px';\n        this.getElement(this.selectors.LOG).style.height = '50px';\n\n        this._toggleButtonText(this.getElement(this.selectors.PIN));\n    }\n\n    /**\n     * Pin the panel into the footer.\n     */\n    _pin() {\n        const props = [\n            'position',\n            'resize',\n            'overflow',\n            'top',\n            'left',\n            'height',\n            'width',\n        ];\n        props.forEach(\n            prop => this.element.style.removeProperty(prop)\n        );\n        this._toggleButtonText(this.getElement(this.selectors.PIN));\n    }\n\n    /**\n     * Toogle the button text with the data-alt value.\n     *\n     * @param {Element} element the button element\n     */\n    _toggleButtonText(element) {\n        [element.innerHTML, element.dataset.alt] = [element.dataset.alt, element.innerHTML];\n    }\n\n}\n"],"names":["target","selectors","element","document","getElementById","undefined","debug","GlobalDebugPanel","reactive","remove","DebugInstanceSubpanel","BaseComponent","create","name","LOADERS","SUBPANEL","NOINSTANCES","LOG","classes","HIDE","subPanels","Set","stateReady","state","_updateReactivesPanels","getElement","this","innerHTML","getWatchers","watch","handler","classList","toggle","reactives","size","forEach","instance","_createLoader","has","id","add","loaders","btn","createElement","dataset","appendChild","addEventListener","_openPanel","data","renderComponent","error","NAME","CLOSE","READMODE","HIGHLIGHT","STATE","CLEAN","PIN","SAVE","INVALID","controller","M","draggable","relativeDrag","strings","savewarning","dragdrop","DragDrop","highlight","_toggleButtonText","_toggleEditMode","_cleanAreas","_togglePin","disabled","_checkJSON","bind","_saveState","_this$getElement2","_refreshState","destroy","unregister","_refreshLog","_refreshReadOnly","logContent","lastChanges","join","value","scrollTop","scrollHeight","JSON","stringify","readonly","readOnly","alt","invalid","save","edited","currentStateData","stateData","style","color","newState","parse","result","_generateStateUpdates","message","updates","processUpdates","newStateData","ids","key","newValue","Object","entries","Array","isArray","Error","push","action","fields","index","String","valueOf","oldValue","deleteField","deleteEntry","getDraggableData","dragEnd","dropdata","event","top","newFixedTop","left","newFixedLeft","setDraggable","_unpin","_pin","pageCenterY","window","innerHeight","pageCenterX","innerWidth","position","resize","overflow","height","width","assign","prop","removeProperty"],"mappings":"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"}