{"version":3,"file":"statemanager.min.js","sources":["https:\/\/www.alsg.org\/home\/lib\/amd\/src\/local\/reactive\/statemanager.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 .\n\n\/**\n * Reactive simple state manager.\n *\n * The state manager contains the state data, trigger update events and\n * can lock and unlock the state data.\n *\n * This file contains the three main elements of the state manager:\n * - State manager: the public class to alter the state, dispatch events and process update messages.\n * - Proxy handler: a private class to keep track of the state object changes.\n * - StateMap class: a private class extending Map class that triggers event when a state list is modifed.\n *\n * @module core\/local\/reactive\/stateManager\n * @class core\/local\/reactive\/stateManager\n * @copyright 2021 Ferran Recio \n * @license http:\/\/www.gnu.org\/copyleft\/gpl.html GNU GPL v3 or later\n *\/\n\nimport Logger from 'core\/local\/reactive\/logger';\n\n\/**\n * State manager class.\n *\n * This class handle the reactive state and ensure only valid mutations can modify the state.\n * It also provide methods to apply batch state update messages (see processUpdates function doc\n * for more details on update messages).\n *\n * Implementing a deep state manager is complex and will require many frontend resources. To keep\n * the state fast and simple, the state can ONLY store two kind of data:\n * - Object with attributes\n * - Sets of objects with id attributes.\n *\n * This is an example of a valid state:\n *\n * {\n * course: {\n * name: 'course name',\n * shortname: 'courseshort',\n * sectionlist: [21, 34]\n * },\n * sections: [\n * {id: 21, name: 'Topic 1', visible: true},\n * {id: 34, name: 'Topic 2', visible: false,\n * ],\n * }\n *\n * The following cases are NOT allowed at a state ROOT level (throws an exception if they are assigned):\n * - Simple values (strings, boolean...).\n * - Arrays of simple values.\n * - Array of objects without ID attribute (all arrays will be converted to maps and requires an ID).\n *\n * Thanks to those limitations it can simplify the state update messages and the event names. If You\n * need to store simple data, just group them in an object.\n *\n * To grant any state change triggers the proper events, the class uses two private structures:\n * - proxy handler: any object stored in the state is proxied using this class.\n * - StateMap class: any object set in the state will be converted to StateMap using the\n * objects id attribute.\n *\/\nexport default class StateManager {\n\n \/**\n * Create a basic reactive state store.\n *\n * The state manager is meant to work with native JS events. To ensure each reactive module can use\n * it in its own way, the parent element must provide a valid event dispatcher function and an optional\n * DOM element to anchor the event.\n *\n * @param {function} dispatchEvent the function to dispatch the custom event when the state changes.\n * @param {element} target the state changed custom event target (document if none provided)\n *\/\n constructor(dispatchEvent, target) {\n\n \/\/ The dispatch event function.\n \/** @package *\/\n this.dispatchEvent = dispatchEvent;\n\n \/\/ The DOM container to trigger events.\n \/** @package *\/\n this.target = target ?? document;\n\n \/\/ State can be altered freely until initial state is set.\n \/** @package *\/\n this.readonly = false;\n\n \/\/ List of state changes pending to be published as events.\n \/** @package *\/\n this.eventsToPublish = [];\n\n \/\/ The update state types functions.\n \/** @package *\/\n this.updateTypes = {\n \"create\": this.defaultCreate.bind(this),\n \"update\": this.defaultUpdate.bind(this),\n \"delete\": this.defaultDelete.bind(this),\n \"put\": this.defaultPut.bind(this),\n \"override\": this.defaultOverride.bind(this),\n \"remove\": this.defaultRemove.bind(this),\n \"prepareFields\": this.defaultPrepareFields.bind(this),\n };\n\n \/\/ The state_loaded event is special because it only happens one but all components\n \/\/ may react to that state, even if they are registered after the setIinitialState.\n \/\/ For these reason we use a promise for that event.\n this.initialPromise = new Promise((resolve) => {\n const initialStateDone = (event) => {\n resolve(event.detail.state);\n };\n this.target.addEventListener('state:loaded', initialStateDone);\n });\n\n this.logger = new Logger();\n }\n\n \/**\n * Loads the initial state.\n *\n * Note this method will trigger a state changed event with \"state:loaded\" actionname.\n *\n * The state mode will be set to read only when the initial state is loaded.\n *\n * @param {object} initialState\n *\/\n setInitialState(initialState) {\n\n if (this.state !== undefined) {\n throw Error('Initial state can only be initialized ones');\n }\n\n \/\/ Create the state object.\n const state = new Proxy({}, new Handler('state', this, true));\n for (const [prop, propValue] of Object.entries(initialState)) {\n state[prop] = propValue;\n }\n this.state = state;\n\n \/\/ When the state is loaded we can lock it to prevent illegal changes.\n this.readonly = true;\n\n this.dispatchEvent({\n action: 'state:loaded',\n state: this.state,\n }, this.target);\n }\n\n \/**\n * Generate a promise that will be resolved when the initial state is loaded.\n *\n * In most cases the final state will be loaded using an ajax call. This is the reason\n * why states manager are created unlocked and won't be reactive until the initial state is set.\n *\n * @return {Promise} the resulting promise\n *\/\n getInitialPromise() {\n return this.initialPromise;\n }\n\n \/**\n * Locks or unlocks the state to prevent illegal updates.\n *\n * Mutations use this method to modify the state. Once the state is updated, they must\n * block again the state.\n *\n * All changes done while the state is writable will be registered using registerStateAction.\n * When the state is set again to read only the method will trigger _publishEvents to communicate\n * changes to all watchers.\n *\n * @param {bool} readonly if the state is in read only mode enabled\n *\/\n setReadOnly(readonly) {\n\n this.readonly = readonly;\n\n let mode = 'off';\n\n \/\/ When the state is in readonly again is time to publish all pending events.\n if (this.readonly) {\n mode = 'on';\n this._publishEvents();\n }\n\n \/\/ Dispatch a read only event.\n this.dispatchEvent({\n action: `readmode:${mode}`,\n state: this.state,\n element: null,\n }, this.target);\n }\n\n \/**\n * Add methods to process update state messages.\n *\n * The state manager provide a default update, create and delete methods. However,\n * some applications may require to override the default methods or even add new ones\n * like \"refresh\" or \"error\".\n *\n * @param {Object} newFunctions the new update types functions.\n *\/\n addUpdateTypes(newFunctions) {\n for (const [updateType, updateFunction] of Object.entries(newFunctions)) {\n if (typeof updateFunction === 'function') {\n this.updateTypes[updateType] = updateFunction.bind(newFunctions);\n }\n }\n }\n\n \/**\n * Process a state updates array and do all the necessary changes.\n *\n * Note this method unlocks the state while it is executing and relocks it\n * when finishes.\n *\n * @param {array} updates\n * @param {Object} updateTypes optional functions to override the default update types.\n *\/\n processUpdates(updates, updateTypes) {\n if (!Array.isArray(updates)) {\n throw Error('State updates must be an array');\n }\n this.setReadOnly(false);\n updates.forEach((update) => {\n if (update.name === undefined) {\n throw Error('Missing state update name');\n }\n this.processUpdate(\n update.name,\n update.action,\n update.fields,\n updateTypes\n );\n });\n this.setReadOnly(true);\n }\n\n \/**\n * Process a single state update.\n *\n * Note this method will not lock or unlock the state by itself.\n *\n * @param {string} updateName the state element to update\n * @param {string} action to action to perform\n * @param {object} fields the new data\n * @param {Object} updateTypes optional functions to override the default update types.\n *\/\n processUpdate(updateName, action, fields, updateTypes) {\n\n if (!fields) {\n throw Error('Missing state update fields');\n }\n\n if (updateTypes === undefined) {\n updateTypes = {};\n }\n\n action = action ?? 'update';\n\n const method = updateTypes[action] ?? this.updateTypes[action];\n\n if (method === undefined) {\n throw Error(`Unkown update action ${action}`);\n }\n\n \/\/ Some state data may require some cooking before sending to the\n \/\/ state. Reactive instances can overrdide the default fieldDefaults\n \/\/ method to add extra logic to all updates.\n const prepareFields = updateTypes.prepareFields ?? this.updateTypes.prepareFields;\n\n method(this, updateName, prepareFields(this, updateName, fields));\n }\n\n \/**\n * Prepare fields for processing.\n *\n * This method is used to add default values or calculations from the frontend side.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n * @returns {Object} final fields data\n *\/\n defaultPrepareFields(stateManager, updateName, fields) {\n return fields;\n }\n\n\n \/**\n * Process a create state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n *\/\n defaultCreate(stateManager, updateName, fields) {\n\n let state = stateManager.state;\n\n \/\/ Create can be applied only to lists, not to objects.\n if (state[updateName] instanceof StateMap) {\n state[updateName].add(fields);\n return;\n }\n state[updateName] = fields;\n }\n\n \/**\n * Process a delete state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n *\/\n defaultDelete(stateManager, updateName, fields) {\n\n \/\/ Get the current value.\n let current = stateManager.get(updateName, fields.id);\n if (!current) {\n throw Error(`Inexistent ${updateName} ${fields.id}`);\n }\n\n \/\/ Process deletion.\n let state = stateManager.state;\n\n if (state[updateName] instanceof StateMap) {\n state[updateName].delete(fields.id);\n return;\n }\n delete state[updateName];\n }\n\n \/**\n * Process a remove state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n *\/\n defaultRemove(stateManager, updateName, fields) {\n\n \/\/ Get the current value.\n let current = stateManager.get(updateName, fields.id);\n if (!current) {\n return;\n }\n\n \/\/ Process deletion.\n let state = stateManager.state;\n\n if (state[updateName] instanceof StateMap) {\n state[updateName].delete(fields.id);\n return;\n }\n delete state[updateName];\n }\n\n \/**\n * Process a update state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n *\/\n defaultUpdate(stateManager, updateName, fields) {\n\n \/\/ Get the current value.\n let current = stateManager.get(updateName, fields.id);\n if (!current) {\n throw Error(`Inexistent ${updateName} ${fields.id}`);\n }\n\n \/\/ Execute updates.\n for (const [fieldName, fieldValue] of Object.entries(fields)) {\n current[fieldName] = fieldValue;\n }\n }\n\n \/**\n * Process a put state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n *\/\n defaultPut(stateManager, updateName, fields) {\n\n \/\/ Get the current value.\n let current = stateManager.get(updateName, fields.id);\n if (current) {\n \/\/ Update attributes.\n for (const [fieldName, fieldValue] of Object.entries(fields)) {\n current[fieldName] = fieldValue;\n }\n } else {\n \/\/ Create new object.\n let state = stateManager.state;\n if (state[updateName] instanceof StateMap) {\n state[updateName].add(fields);\n return;\n }\n state[updateName] = fields;\n }\n }\n\n \/**\n * Process an override state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n *\/\n defaultOverride(stateManager, updateName, fields) {\n\n \/\/ Get the current value.\n let current = stateManager.get(updateName, fields.id);\n if (current) {\n \/\/ Remove any unnecessary fields.\n for (const [fieldName] of Object.entries(current)) {\n if (fields[fieldName] === undefined) {\n delete current[fieldName];\n }\n }\n \/\/ Update field.\n for (const [fieldName, fieldValue] of Object.entries(fields)) {\n current[fieldName] = fieldValue;\n }\n } else {\n \/\/ Create the element if not exists.\n let state = stateManager.state;\n if (state[updateName] instanceof StateMap) {\n state[updateName].add(fields);\n return;\n }\n state[updateName] = fields;\n }\n }\n\n \/**\n * Set the logger class instance.\n *\n * Reactive instances can provide alternative loggers to provide advanced logging.\n * @param {Logger} logger\n *\/\n setLogger(logger) {\n this.logger = logger;\n }\n\n \/**\n * Add a new log entry into the reactive logger.\n * @param {LoggerEntry} entry\n *\/\n addLoggerEntry(entry) {\n this.logger.add(entry);\n }\n\n \/**\n * Get an element from the state or form an alternative state object.\n *\n * The altstate param is used by external update functions that gets the current\n * state as param.\n *\n * @param {String} name the state object name\n * @param {*} id and object id for state maps.\n * @return {Object|undefined} the state object found\n *\/\n get(name, id) {\n const state = this.state;\n\n let current = state[name];\n if (current instanceof StateMap) {\n if (id === undefined) {\n throw Error(`Missing id for ${name} state update`);\n }\n current = state[name].get(id);\n }\n\n return current;\n }\n\n \/**\n * Register a state modification and generate the necessary events.\n *\n * This method is used mainly by proxy helpers to dispatch state change event.\n * However, mutations can use it to inform components about non reactive changes\n * in the state (only the two first levels of the state are reactive).\n *\n * Each action can produce several events:\n * - The specific attribute updated, created or deleter (example: \"cm.visible:updated\")\n * - The general state object updated, created or deleted (example: \"cm:updated\")\n * - If the element has an ID attribute, the specific event with id (example: \"cm[42].visible:updated\")\n * - If the element has an ID attribute, the general event with id (example: \"cm[42]:updated\")\n * - A generic state update event \"state:update\"\n *\n * @param {string} field the affected state field name\n * @param {string|null} prop the affecter field property (null if affect the full object)\n * @param {string} action the action done (created\/updated\/deleted)\n * @param {*} data the affected data\n *\/\n registerStateAction(field, prop, action, data) {\n\n let parentAction = 'updated';\n\n if (prop !== null) {\n this.eventsToPublish.push({\n eventName: `${field}.${prop}:${action}`,\n eventData: data,\n action,\n });\n } else {\n parentAction = action;\n }\n\n \/\/ Trigger extra events if the element has an ID attribute.\n if (data.id !== undefined) {\n if (prop !== null) {\n this.eventsToPublish.push({\n eventName: `${field}[${data.id}].${prop}:${action}`,\n eventData: data,\n action,\n });\n }\n this.eventsToPublish.push({\n eventName: `${field}[${data.id}]:${parentAction}`,\n eventData: data,\n action: parentAction,\n });\n }\n\n \/\/ Register the general change.\n this.eventsToPublish.push({\n eventName: `${field}:${parentAction}`,\n eventData: data,\n action: parentAction,\n });\n\n \/\/ Register state updated event.\n this.eventsToPublish.push({\n eventName: `state:updated`,\n eventData: data,\n action: 'updated',\n });\n }\n\n \/**\n * Internal method to publish events.\n *\n * This is a private method, it will be invoked when the state is set back to read only mode.\n *\/\n _publishEvents() {\n const fieldChanges = this.eventsToPublish;\n this.eventsToPublish = [];\n\n \/\/ Dispatch a transaction start event.\n this.dispatchEvent({\n action: 'transaction:start',\n state: this.state,\n element: null,\n changes: fieldChanges,\n }, this.target);\n\n \/\/ State changes can be registered in any order. However it will avoid many\n \/\/ components errors if they are sorted to have creations-updates-deletes in case\n \/\/ some component needs to create or destroy DOM elements before updating them.\n fieldChanges.sort((a, b) => {\n const weights = {\n created: 0,\n updated: 1,\n deleted: 2,\n };\n const aweight = weights[a.action] ?? 0;\n const bweight = weights[b.action] ?? 0;\n \/\/ In case both have the same weight, the eventName length decide.\n if (aweight === bweight) {\n return a.eventName.length - b.eventName.length;\n }\n return aweight - bweight;\n });\n\n \/\/ List of the published events to prevent redundancies.\n let publishedEvents = new Set();\n\n fieldChanges.forEach((event) => {\n\n const eventkey = `${event.eventName}.${event.eventData.id ?? 0}`;\n\n if (!publishedEvents.has(eventkey)) {\n this.dispatchEvent({\n action: event.eventName,\n state: this.state,\n element: event.eventData\n }, this.target);\n\n publishedEvents.add(eventkey);\n }\n });\n\n \/\/ Dispatch a transaction end event.\n this.dispatchEvent({\n action: 'transaction:end',\n state: this.state,\n element: null,\n }, this.target);\n }\n}\n\n\/\/ Proxy helpers.\n\n\/**\n * The proxy handler.\n *\n * This class will inform any value change directly to the state manager.\n *\n * The proxied variable will throw an error if it is altered when the state manager is\n * in read only mode.\n *\/\nclass Handler {\n\n \/**\n * Class constructor.\n *\n * @param {string} name the variable name used for identify triggered actions\n * @param {StateManager} stateManager the state manager object\n * @param {boolean} proxyValues if new values must be proxied (used only at state root level)\n *\/\n constructor(name, stateManager, proxyValues) {\n this.name = name;\n this.stateManager = stateManager;\n this.proxyValues = proxyValues ?? false;\n }\n\n \/**\n * Set trap to trigger events when the state changes.\n *\n * @param {object} obj the source object (not proxied)\n * @param {string} prop the attribute to set\n * @param {*} value the value to save\n * @param {*} receiver the proxied element to be attached to events\n * @returns {boolean} if the value is set\n *\/\n set(obj, prop, value, receiver) {\n\n \/\/ Only mutations should be able to set state values.\n if (this.stateManager.readonly) {\n throw new Error(`State locked. Use mutations to change ${prop} value in ${this.name}.`);\n }\n\n \/\/ Check any data change.\n if (JSON.stringify(obj[prop]) === JSON.stringify(value)) {\n return true;\n }\n\n const action = (obj[prop] !== undefined) ? 'updated' : 'created';\n\n \/\/ Proxy value if necessary (used at state root level).\n if (this.proxyValues) {\n if (Array.isArray(value)) {\n obj[prop] = new StateMap(prop, this.stateManager).loadValues(value);\n } else {\n obj[prop] = new Proxy(value, new Handler(prop, this.stateManager));\n }\n } else {\n obj[prop] = value;\n }\n\n \/\/ If the state is not ready yet means the initial state is not yet loaded.\n if (this.stateManager.state === undefined) {\n return true;\n }\n\n this.stateManager.registerStateAction(this.name, prop, action, receiver);\n\n return true;\n }\n\n \/**\n * Delete property trap to trigger state change events.\n *\n * @param {*} obj the affected object (not proxied)\n * @param {*} prop the prop to delete\n * @returns {boolean} if prop is deleted\n *\/\n deleteProperty(obj, prop) {\n \/\/ Only mutations should be able to set state values.\n if (this.stateManager.readonly) {\n throw new Error(`State locked. Use mutations to delete ${prop} in ${this.name}.`);\n }\n if (prop in obj) {\n\n delete obj[prop];\n\n this.stateManager.registerStateAction(this.name, prop, 'deleted', obj);\n }\n return true;\n }\n}\n\n\/**\n * Class to add events dispatching to the JS Map class.\n *\n * When the state has a list of objects (with IDs) it will be converted into a StateMap.\n * StateMap is used almost in the same way as a regular JS map. Because all elements have an\n * id attribute, it has some specific methods:\n * - add: a convenient method to add an element without specifying the key (\"id\" attribute will be used as a key).\n * - loadValues: to add many elements at once wihout specifying keys (\"id\" attribute will be used).\n *\n * Apart, the main difference between regular Map and MapState is that this one will inform any change to the\n * state manager.\n *\/\nclass StateMap extends Map {\n\n \/**\n * Create a reactive Map.\n *\n * @param {string} name the property name\n * @param {StateManager} stateManager the state manager\n * @param {iterable} iterable an iterable object to create the Map\n *\/\n constructor(name, stateManager, iterable) {\n \/\/ We don't have any \"this\" until be call super.\n super(iterable);\n this.name = name;\n this.stateManager = stateManager;\n }\n\n \/**\n * Set an element into the map.\n *\n * Each value needs it's own id attribute. Objects without id will be rejected.\n * The function will throw an error if the value id and the key are not the same.\n *\n * @param {*} key the key to store\n * @param {*} value the value to store\n * @returns {Map} the resulting Map object\n *\/\n set(key, value) {\n\n \/\/ Only mutations should be able to set state values.\n if (this.stateManager.readonly) {\n throw new Error(`State locked. Use mutations to change ${key} value in ${this.name}.`);\n }\n\n \/\/ Normalize keys as string to prevent json decoding errors.\n key = this.normalizeKey(key);\n\n this.checkValue(value);\n\n if (key === undefined || key === null) {\n throw Error('State lists keys cannot be null or undefined');\n }\n\n \/\/ ID is mandatory and should be the same as the key.\n if (this.normalizeKey(value.id) !== key) {\n throw new Error(`State error: ${this.name} list element ID (${value.id}) and key (${key}) mismatch`);\n }\n\n const action = (super.has(key)) ? 'updated' : 'created';\n\n \/\/ Save proxied data into the list.\n const result = super.set(key, new Proxy(value, new Handler(this.name, this.stateManager)));\n\n \/\/ If the state is not ready yet means the initial state is not yet loaded.\n if (this.stateManager.state === undefined) {\n return result;\n }\n\n this.stateManager.registerStateAction(this.name, null, action, super.get(key));\n\n return result;\n }\n\n \/**\n * Check if a value is valid to be stored in a a State List.\n *\n * Only objects with id attribute can be stored in State lists.\n *\n * This method throws an error if the value is not valid.\n *\n * @param {object} value (with ID)\n *\/\n checkValue(value) {\n if (!typeof value === 'object' && value !== null) {\n throw Error('State lists can contain objects only');\n }\n\n if (value.id === undefined) {\n throw Error('State lists elements must contain at least an id attribute');\n }\n }\n\n \/**\n * Return a normalized key value for state map.\n *\n * Regular maps uses strict key comparissons but state maps are indexed by ID.JSON conversions\n * and webservices sometimes do unexpected types conversions so we convert any integer key to string.\n *\n * @param {*} key the provided key\n * @returns {string}\n *\/\n normalizeKey(key) {\n return String(key).valueOf();\n }\n\n \/**\n * Insert a new element int a list.\n *\n * Each value needs it's own id attribute. Objects withouts id will be rejected.\n *\n * @param {object} value the value to add (needs an id attribute)\n * @returns {Map} the resulting Map object\n *\/\n add(value) {\n this.checkValue(value);\n return this.set(value.id, value);\n }\n\n \/**\n * Return a state map element.\n *\n * @param {*} key the element id\n * @return {Object}\n *\/\n get(key) {\n return super.get(this.normalizeKey(key));\n }\n\n \/**\n * Check whether an element with the specified key exists or not.\n *\n * @param {*} key the key to find\n * @return {boolean}\n *\/\n has(key) {\n return super.has(this.normalizeKey(key));\n }\n\n \/**\n * Delete an element from the map.\n *\n * @param {*} key\n * @returns {boolean}\n *\/\n delete(key) {\n \/\/ State maps uses only string keys to avoid strict comparisons.\n key = this.normalizeKey(key);\n\n \/\/ Only mutations should be able to set state values.\n if (this.stateManager.readonly) {\n throw new Error(`State locked. Use mutations to change ${key} value in ${this.name}.`);\n }\n\n const previous = super.get(key);\n\n const result = super.delete(key);\n if (!result) {\n return result;\n }\n\n this.stateManager.registerStateAction(this.name, null, 'deleted', previous);\n\n return result;\n }\n\n \/**\n * Return a suitable structure for JSON conversion.\n *\n * This function is needed because new values are compared in JSON. StateMap has Private\n * attributes which cannot be stringified (like this.stateManager which will produce an\n * infinite recursivity).\n *\n * @returns {array}\n *\/\n toJSON() {\n let result = [];\n this.forEach((value) => {\n result.push(value);\n });\n return result;\n }\n\n \/**\n * Insert a full list of values using the id attributes as keys.\n *\n * This method is used mainly to initialize the list. Note each element is indexed by its \"id\" attribute.\n * This is a basic restriction of StateMap. All elements need an id attribute, otherwise it won't be saved.\n *\n * @param {iterable} values the values to load\n * @returns {StateMap} return the this value\n *\/\n loadValues(values) {\n values.forEach((data) => {\n this.checkValue(data);\n let key = data.id;\n let newvalue = new Proxy(data, new Handler(this.name, this.stateManager));\n this.set(key, newvalue);\n });\n return this;\n }\n}\n"],"names":["constructor","dispatchEvent","target","document","readonly","eventsToPublish","updateTypes","this","defaultCreate","bind","defaultUpdate","defaultDelete","defaultPut","defaultOverride","defaultRemove","defaultPrepareFields","initialPromise","Promise","resolve","addEventListener","event","detail","state","logger","Logger","setInitialState","initialState","undefined","Error","Proxy","Handler","prop","propValue","Object","entries","action","getInitialPromise","setReadOnly","mode","_publishEvents","element","addUpdateTypes","newFunctions","updateType","updateFunction","processUpdates","updates","Array","isArray","forEach","update","name","processUpdate","fields","updateName","method","prepareFields","stateManager","StateMap","add","get","id","delete","current","fieldName","fieldValue","setLogger","addLoggerEntry","entry","registerStateAction","field","data","parentAction","push","eventName","eventData","fieldChanges","changes","sort","a","b","weights","created","updated","deleted","aweight","bweight","length","publishedEvents","Set","eventkey","has","proxyValues","set","obj","value","receiver","JSON","stringify","loadValues","deleteProperty","Map","iterable","key","normalizeKey","checkValue","super","result","String","valueOf","previous","toJSON","values","newvalue"],"mappings":"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\/E8C,KAAKC,UAAUJ,IAAI\/D,SAAWkE,KAAKC,UAAUH,cACtC,QAGL5D,YAAwBR,IAAdmE,IAAI\/D,MAAuB,UAAY,iBAGnDxB,KAAKqF,YACD7C,MAAMC,QAAQ+C,OACdD,IAAI\/D,MAAQ,IAAI2B,SAAS3B,KAAMxB,KAAKkD,cAAc0C,WAAWJ,OAE7DD,IAAI\/D,MAAQ,IAAIF,MAAMkE,MAAO,IAAIjE,QAAQC,KAAMxB,KAAKkD,eAGxDqC,IAAI\/D,MAAQgE,WAIgBpE,IAA5BpB,KAAKkD,aAAanC,YAIjBmC,aAAaY,oBAAoB9D,KAAK4C,KAAMpB,KAAMI,OAAQ6D,WAHpD,EAefI,eAAeN,IAAK\/D,SAEZxB,KAAKkD,aAAarD,eACZ,IAAIwB,sDAA+CG,oBAAWxB,KAAK4C,kBAEzEpB,QAAQ+D,aAEDA,IAAI\/D,WAEN0B,aAAaY,oBAAoB9D,KAAK4C,KAAMpB,KAAM,UAAW+D,OAE\/D,SAgBTpC,iBAAiB2C,IASnBrG,YAAYmD,KAAMM,aAAc6C,gBAEtBA,eACDnD,KAAOA,UACPM,aAAeA,aAaxBoC,IAAIU,IAAKR,UAGDxF,KAAKkD,aAAarD,eACZ,IAAIwB,sDAA+C2E,yBAAgBhG,KAAK4C,cAIlFoD,IAAMhG,KAAKiG,aAAaD,UAEnBE,WAAWV,OAEZQ,MAAAA,UACM3E,MAAM,mDAIZrB,KAAKiG,aAAaT,MAAMlC,MAAQ0C,UAC1B,IAAI3E,6BAAsBrB,KAAK4C,kCAAyB4C,MAAMlC,yBAAgB0C,yBAGlFpE,OAAUuE,MAAMf,IAAIY,KAAQ,UAAY,UAGxCI,OAASD,MAAMb,IAAIU,IAAK,IAAI1E,MAAMkE,MAAO,IAAIjE,QAAQvB,KAAK4C,KAAM5C,KAAKkD,4BAG3C9B,IAA5BpB,KAAKkD,aAAanC,YAIjBmC,aAAaY,oBAAoB9D,KAAK4C,KAAM,KAAMhB,OAAQuE,MAAM9C,IAAI2C,MAH9DI,OAiBfF,WAAWV,eAKUpE,IAAboE,MAAMlC,SACAjC,MAAM,8DAapB4E,aAAaD,YACFK,OAAOL,KAAKM,UAWvBlD,IAAIoC,mBACKU,WAAWV,OACTxF,KAAKsF,IAAIE,MAAMlC,GAAIkC,OAS9BnC,IAAI2C,YACOG,MAAM9C,IAAIrD,KAAKiG,aAAaD,MASvCZ,IAAIY,YACOG,MAAMf,IAAIpF,KAAKiG,aAAaD,MASvCzC,OAAOyC,QAEHA,IAAMhG,KAAKiG,aAAaD,KAGpBhG,KAAKkD,aAAarD,eACZ,IAAIwB,sDAA+C2E,yBAAgBhG,KAAK4C,iBAG5E2D,SAAWJ,MAAM9C,IAAI2C,KAErBI,OAASD,MAAM5C,OAAOyC,YACvBI,aAIAlD,aAAaY,oBAAoB9D,KAAK4C,KAAM,KAAM,UAAW2D,UAE3DH,QALIA,OAiBfI,aACQJ,OAAS,eACR1D,SAAS8C,QACVY,OAAOlC,KAAKsB,UAETY,OAYXR,WAAWa,eACPA,OAAO\/D,SAASsB,YACPkC,WAAWlC,UACZgC,IAAMhC,KAAKV,GACXoD,SAAW,IAAIpF,MAAM0C,KAAM,IAAIzC,QAAQvB,KAAK4C,KAAM5C,KAAKkD,oBACtDoC,IAAIU,IAAKU,aAEX1G"}