Ir a la última revisión | 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 f
ooter.\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} select
ors 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='noinstanc
es']`,\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.HID
E,\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 i
nstance 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 H
IGHLIGHT: `[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.remov
e\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, 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 stat
e 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 exam
ple,\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.col
or = '';\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 sta
te 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(`Arr
ay ${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: 'del
ete',\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","_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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"}