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 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.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 refre
sh 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 stat
e. 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 invali
d.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 r
eactive 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 a
ction: '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._p
in();\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 'resiz
e',\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","_createLoad
er","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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
QAAQiD,IAAK3F,QAAQyB"}