From d8312003a4af6b14c45d1b27a6184926f8a87b4d Mon Sep 17 00:00:00 2001 From: Mathew May Date: Wed, 24 Jul 2024 15:38:25 +0800 Subject: [PATCH] centalise display funcs and add more testing --- lib/form/amd/build/form.min.js | 2 +- lib/form/amd/build/form.min.js.map | 2 +- lib/form/amd/build/form/display.min.js | 3 + lib/form/amd/build/form/display.min.js.map | 1 + lib/form/amd/build/form/dom.min.js | 3 - lib/form/amd/build/form/dom.min.js.map | 1 - lib/form/amd/build/form/rules.min.js | 2 +- lib/form/amd/build/form/rules.min.js.map | 2 +- lib/form/amd/build/form/util.min.js | 3 - lib/form/amd/build/form/util.min.js.map | 1 - lib/form/amd/src/form.js | 2 +- lib/form/amd/src/form/{dom.js => display.js} | 35 +++++++++++ lib/form/amd/src/form/rules.js | 29 ++++----- lib/form/amd/src/form/util.js | 59 ------------------- lib/form/tests/behat/fixtures/form_rules.php | 24 ++++---- lib/form/tests/behat/form_rules.feature | 42 ++++++++++--- ...aderescale_for_database_pointscale.feature | 2 +- 17 files changed, 107 insertions(+), 106 deletions(-) create mode 100644 lib/form/amd/build/form/display.min.js create mode 100644 lib/form/amd/build/form/display.min.js.map delete mode 100644 lib/form/amd/build/form/dom.min.js delete mode 100644 lib/form/amd/build/form/dom.min.js.map delete mode 100644 lib/form/amd/build/form/util.min.js delete mode 100644 lib/form/amd/build/form/util.min.js.map rename lib/form/amd/src/form/{dom.js => display.js} (67%) delete mode 100644 lib/form/amd/src/form/util.js diff --git a/lib/form/amd/build/form.min.js b/lib/form/amd/build/form.min.js index dfb867ed7faf8..84b6e98f60daf 100644 --- a/lib/form/amd/build/form.min.js +++ b/lib/form/amd/build/form.min.js @@ -1,3 +1,3 @@ -define("core_form/form",["exports","./changechecker","./submit","./form/rules","./form/dom","core/pending"],(function(_exports,FormChangeChecker,Submit,_rules,MutateDom,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,FormChangeChecker=_interopRequireWildcard(FormChangeChecker),Submit=_interopRequireWildcard(Submit),_rules=_interopRequireDefault(_rules),MutateDom=_interopRequireWildcard(MutateDom),_pending=_interopRequireDefault(_pending);class Form{constructor(formID,dependencies){_defineProperty(this,"form",void 0),_defineProperty(this,"dependencies",void 0),_defineProperty(this,"editors",new Map),_defineProperty(this,"initialDisabledHidden",[]),this.form=document.querySelector("#".concat(formID));const pendingPromise=new _pending.default("construction");this.dependencies=this.getDependencyMapper(dependencies),this.findSetEditors(),this.rules=new _rules.default(this),this.applyInitialState(),this.registerEventListeners(),FormChangeChecker.watchForm(this.form),pendingPromise.resolve()}applyInitialState(){[...this.form.elements].forEach((element=>{(element.disabled||element.hidden)&&""!==element.name&&this.initialDisabledHidden.push(element.name)})),[...this.form.elements].forEach((element=>{this.dependencies.has(element.name)&&this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(element)))}))}registerEventListeners(){this.form.addEventListener("change",(async e=>{if("submit"===e.target.type&&(FormChangeChecker.resetFormDirtyState(this.form),Submit.init(e.target.id)),"reset"===e.target.type&&(FormChangeChecker.resetFormDirtyState(this.form),this.form.reset()),this.dependencies.has(e.target.name)){FormChangeChecker.markFormChangedFromNode(e.target);const pendingPromise=new _pending.default("update");await this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(e.target))),pendingPromise.resolve()}}));new MutationObserver((async mutationList=>{for(const mutation of mutationList)if("attributes"===mutation.type){[...mutation.target.querySelectorAll("input, select, textarea")].forEach((element=>{if(this.dependencies.has(element.name)){const pendingPromise=new _pending.default("update");this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(element))),pendingPromise.resolve()}}))}})).observe(this.form,{attributes:!0,childList:!0,subtree:!0})}dispatchDependencyRules(target){const displayMap=this.mapTemplate();return this.dependencies.get(target.name).forEach(((dependants,ruleName)=>{(this.rules[ruleName]?this.rules[ruleName](target):this.rules.neq(target)).forEach(((nodeNames,displayOption)=>{const spreadFlat=[...displayMap.get(displayOption),...nodeNames.values()].flat();displayMap.set(displayOption,spreadFlat)}))})),displayMap}displayMapPrune(displayMap){const removeunlockifhidden=displayMap.get("unlock").filter((x=>!displayMap.get("hide").toString().includes(x.toString())));displayMap.set("unlock",removeunlockifhidden);for(const[key,value]of displayMap)0===value.length&&displayMap.delete(key);return displayMap}getDependantsOfType(element,type){var _this$dependencies$ge;return"undefined"!==this.dependencies.get(element)&&null!==(_this$dependencies$ge=this.dependencies.get(element).get(type))&&void 0!==_this$dependencies$ge?_this$dependencies$ge:[]}domDispatch(){(arguments.length>0&&void 0!==arguments[0]?arguments[0]:[]).forEach(((elements,domUpdateOpt)=>{if(!MutateDom[domUpdateOpt])return;const elCopy=elements.filter((el=>!this.initialDisabledHidden.includes(el)));this.elementNamesToDomNodes(elCopy).forEach((node=>{null!==node&&MutateDom[domUpdateOpt](node)}))}))}elementNamesToDomNodes(elementNames){return elementNames.map((element=>this.form.querySelector('[data-groupname="'.concat(element,'"]'))?this.form.querySelector('[data-groupname="'.concat(element,'"]')):this.findSetEditors(element)?this.form.elements.namedItem("".concat(element,"[text]")):this.form.elements.namedItem(element)?this.form.elements.namedItem(element):this.form.elements.namedItem("id_".concat(element))))}findSetEditors(){let elementName=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";if(0===this.editors.size){const fEditors=this.form.querySelectorAll('[data-fieldtype="editor"] textarea');Array.from(fEditors).forEach((node=>{this.editors.set(node.name,!0)}))}return this.editors.get("".concat(elementName,"[text]"))||!1}getDependencyMapper(dependencies){const elementMap=new Map(Object.entries(dependencies));return elementMap.forEach(((elementrules,key)=>{const ruleMap=new Map(Object.entries(elementrules));ruleMap.forEach(((ruleComparisons,key)=>{const hideDefine=new Map(Object.entries(ruleComparisons));hideDefine.forEach(((action,compVal)=>{Array.isArray(action)&&(action={...action}),hideDefine.set(compVal,action)})),ruleMap.set(key,hideDefine)})),elementMap.set(key,ruleMap)})),elementMap}mapTemplate(){return new Map([["hide",[]],["show",[]],["lock",[]],["unlock",[]]])}static init(formID,dependencies){return new Form(formID,dependencies)}}return _exports.default=Form,_exports.default})); +define("core_form/form",["exports","./changechecker","./submit","./form/rules","./form/display","core/pending"],(function(_exports,FormChangeChecker,Submit,_rules,MutateDom,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,FormChangeChecker=_interopRequireWildcard(FormChangeChecker),Submit=_interopRequireWildcard(Submit),_rules=_interopRequireDefault(_rules),MutateDom=_interopRequireWildcard(MutateDom),_pending=_interopRequireDefault(_pending);class Form{constructor(formID,dependencies){_defineProperty(this,"form",void 0),_defineProperty(this,"dependencies",void 0),_defineProperty(this,"editors",new Map),_defineProperty(this,"initialDisabledHidden",[]),this.form=document.querySelector("#".concat(formID));const pendingPromise=new _pending.default("construction");this.dependencies=this.getDependencyMapper(dependencies),this.findSetEditors(),this.rules=new _rules.default(this),this.applyInitialState(),this.registerEventListeners(),FormChangeChecker.watchForm(this.form),pendingPromise.resolve()}applyInitialState(){[...this.form.elements].forEach((element=>{(element.disabled||element.hidden)&&""!==element.name&&this.initialDisabledHidden.push(element.name)})),[...this.form.elements].forEach((element=>{this.dependencies.has(element.name)&&this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(element)))}))}registerEventListeners(){this.form.addEventListener("change",(async e=>{if("submit"===e.target.type&&(FormChangeChecker.resetFormDirtyState(this.form),Submit.init(e.target.id)),"reset"===e.target.type&&(FormChangeChecker.resetFormDirtyState(this.form),this.form.reset()),this.dependencies.has(e.target.name)){FormChangeChecker.markFormChangedFromNode(e.target);const pendingPromise=new _pending.default("update");await this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(e.target))),pendingPromise.resolve()}}));new MutationObserver((async mutationList=>{for(const mutation of mutationList)if("attributes"===mutation.type){[...mutation.target.querySelectorAll("input, select, textarea")].forEach((element=>{if(this.dependencies.has(element.name)){const pendingPromise=new _pending.default("update");this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(element))),pendingPromise.resolve()}}))}})).observe(this.form,{attributes:!0,childList:!0,subtree:!0})}dispatchDependencyRules(target){const displayMap=this.mapTemplate();return this.dependencies.get(target.name).forEach(((dependants,ruleName)=>{(this.rules[ruleName]?this.rules[ruleName](target):this.rules.neq(target)).forEach(((nodeNames,displayOption)=>{const spreadFlat=[...displayMap.get(displayOption),...nodeNames.values()].flat();displayMap.set(displayOption,spreadFlat)}))})),displayMap}displayMapPrune(displayMap){const removeunlockifhidden=displayMap.get("unlock").filter((x=>!displayMap.get("hide").toString().includes(x.toString())));displayMap.set("unlock",removeunlockifhidden);for(const[key,value]of displayMap)0===value.length&&displayMap.delete(key);return displayMap}getDependantsOfType(element,type){var _this$dependencies$ge;return"undefined"!==this.dependencies.get(element)&&null!==(_this$dependencies$ge=this.dependencies.get(element).get(type))&&void 0!==_this$dependencies$ge?_this$dependencies$ge:[]}domDispatch(){(arguments.length>0&&void 0!==arguments[0]?arguments[0]:[]).forEach(((elements,domUpdateOpt)=>{if(!MutateDom[domUpdateOpt])return;const elCopy=elements.filter((el=>!this.initialDisabledHidden.includes(el)));this.elementNamesToDomNodes(elCopy).forEach((node=>{null!==node&&MutateDom[domUpdateOpt](node)}))}))}elementNamesToDomNodes(elementNames){return elementNames.map((element=>this.form.querySelector('[data-groupname="'.concat(element,'"]'))?this.form.querySelector('[data-groupname="'.concat(element,'"]')):this.findSetEditors(element)?this.form.elements.namedItem("".concat(element,"[text]")):this.form.elements.namedItem(element)?this.form.elements.namedItem(element):this.form.elements.namedItem("id_".concat(element))))}findSetEditors(){let elementName=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";if(0===this.editors.size){const fEditors=this.form.querySelectorAll('[data-fieldtype="editor"] textarea');Array.from(fEditors).forEach((node=>{this.editors.set(node.name,!0)}))}return this.editors.get("".concat(elementName,"[text]"))||!1}getDependencyMapper(dependencies){const elementMap=new Map(Object.entries(dependencies));return elementMap.forEach(((elementrules,key)=>{const ruleMap=new Map(Object.entries(elementrules));ruleMap.forEach(((ruleComparisons,key)=>{const hideDefine=new Map(Object.entries(ruleComparisons));hideDefine.forEach(((action,compVal)=>{Array.isArray(action)&&(action={...action}),hideDefine.set(compVal,action)})),ruleMap.set(key,hideDefine)})),elementMap.set(key,ruleMap)})),elementMap}mapTemplate(){return new Map([["hide",[]],["show",[]],["lock",[]],["unlock",[]]])}static init(formID,dependencies){return new Form(formID,dependencies)}}return _exports.default=Form,_exports.default})); //# sourceMappingURL=form.min.js.map \ No newline at end of file diff --git a/lib/form/amd/build/form.min.js.map b/lib/form/amd/build/form.min.js.map index 7afab55e59292..b18bf4e52ab05 100644 --- a/lib/form/amd/build/form.min.js.map +++ b/lib/form/amd/build/form.min.js.map @@ -1 +1 @@ -{"version":3,"file":"form.min.js","sources":["../src/form.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 * This file contains JS functionality required by mforms and is included automatically\n * when required.\n *\n * @see /lib/formslib.php#L2548 Candidate for removal, depends on grouped rules.\n * @see /lib/amd/src/showhidesettings.js Candidate for removal.\n *\n * @module core_form/form\n * @copyright 2024 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport * as FormChangeChecker from './changechecker';\nimport * as Submit from './submit';\nimport Rules from './form/rules';\nimport * as MutateDom from './form/dom';\nimport Pending from 'core/pending';\n\nexport default class Form {\n /**\n * @var {HTMLFormElement} form Our very own form to work on.\n */\n form;\n\n /**\n * @var {Map} dependencies Our map of form dependencies.\n */\n dependencies;\n\n /**\n * @var {Map} editors Our map of form editors used get the right selector.\n */\n editors = new Map();\n\n initialDisabledHidden = [];\n\n /**\n * Create a new form instance.\n *\n * @param {String} formID The ID of the form to be managed.\n * @param {Object} dependencies The passed object of form dependencies.\n */\n constructor(formID, dependencies) {\n\n // Set class properties.\n this.form = document.querySelector(`#${formID}`);\n\n const pendingPromise = new Pending('construction');\n // TODO: Food for thought: Display a loader?\n this.dependencies = this.getDependencyMapper(dependencies);\n this.findSetEditors();\n this.rules = new Rules(this);\n\n // Apply the initial state of the form.\n this.applyInitialState();\n\n // Handle mutations within the form.\n this.registerEventListeners();\n FormChangeChecker.watchForm(this.form);\n pendingPromise.resolve();\n }\n\n /**\n * Given the page has loaded, apply the initial state of the form.\n */\n applyInitialState() {\n [...this.form.elements].forEach((element) => {\n if ((element.disabled || element.hidden) && element.name !== '') {\n this.initialDisabledHidden.push(element.name);\n }\n });\n [...this.form.elements].forEach((element) => {\n if (this.dependencies.has(element.name)) {\n this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(element)));\n }\n });\n }\n\n /**\n * Add event listeners to the form.\n */\n registerEventListeners() {\n this.form.addEventListener('change', async(e) => {\n if (e.target.type === 'submit') {\n FormChangeChecker.resetFormDirtyState(this.form);\n Submit.init(e.target.id);\n }\n if (e.target.type === 'reset') {\n FormChangeChecker.resetFormDirtyState(this.form);\n this.form.reset();\n }\n // Something changes based on this element.\n if (this.dependencies.has(e.target.name)) {\n FormChangeChecker.markFormChangedFromNode(e.target);\n const pendingPromise = new Pending('update');\n await this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(e.target)));\n pendingPromise.resolve();\n }\n });\n\n // Check if changed items themselves have rules attached. i.e. mod_data ratings returns scale
which\n // contains scale[modgrade_type] rule that in turn dedicates max grade or scale display.\n const observer = new MutationObserver(async(mutationList) => {\n for (const mutation of mutationList) {\n // Something in the form has changed, so confirm the items against their own rules.\n if (mutation.type === \"attributes\") {\n const inputs = mutation.target.querySelectorAll('input, select, textarea');\n [...inputs].forEach((element) => {\n if (this.dependencies.has(element.name)) {\n const pendingPromise = new Pending('update');\n // Look up any rules associated with the returned items.\n this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(element)));\n pendingPromise.resolve();\n }\n });\n }\n }\n });\n\n // Start observing the target node for configured mutations\n observer.observe(this.form, {attributes: true, childList: true, subtree: true});\n }\n\n /**\n * Dispatch the dependency rules to the appropriate rule handler.\n *\n * @param {HTMLFormElement} target The name associated to the element that has changed.\n * @returns {Map} Actions to be taken along with elements that should be affected.\n */\n dispatchDependencyRules(target) {\n const displayMap = this.mapTemplate();\n this.dependencies.get(target.name).forEach((dependants, ruleName) => {\n // If the rule exists, use it, otherwise fallback to 'neq' which seems to be the \"default\" rule originally.\n const elNamesMap = this.rules[ruleName] ? this.rules[ruleName](target) : this.rules.neq(target);\n // Merge the current rule map with the final display map.\n elNamesMap.forEach((nodeNames, displayOption) => {\n // We want to merge in the new values into the existing value into a tight single array.\n const spreadFlat = [...displayMap.get(displayOption), ...nodeNames.values()].flat();\n displayMap.set(displayOption, spreadFlat);\n });\n });\n return displayMap;\n }\n\n /**\n * By default, the full display map contains empty entries and potential duplicated DOM node names.\n * Here we will get rid of any empty entries and review the locked array. If a node name exists in both\n * the locked and hidden array, it should be removed from the locked array as nodes are locked / disabled when hidden.\n *\n * @param {Map} displayMap Map of elements and their associated rules to prune.\n * @returns {Map|Map<>} The pruned map or map even a fully pruned map if noting has to change.\n */\n displayMapPrune(displayMap) {\n // Filter any unlocked items that pegged to be hidden as they must be locked if they are hidden.\n const removeunlockifhidden = displayMap.get('unlock').filter(x => {\n return !displayMap.get('hide').toString().includes(x.toString());\n });\n displayMap.set('unlock', removeunlockifhidden);\n\n // Remove any empty entries.\n for (const [key, value] of displayMap) {\n if (value.length === 0) {\n displayMap.delete(key);\n }\n }\n return displayMap;\n }\n\n /**\n * For a given element, get the names of DOM nodes that can change based on the given rule type name.\n *\n * @param {String} element The name of the element to get the dependants for.\n * @param {String} type The rule type to get the dependants for.\n * @returns {Array|[]}\n */\n getDependantsOfType(element, type) {\n return this.dependencies.get(element) !== 'undefined' ? this.dependencies.get(element).get(type) ?? [] : [];\n }\n\n /**\n * Dispatch the DOM manipulation to the appropriate function.\n *\n * @param {Map} elNamesMap What needs to change.\n */\n domDispatch(elNamesMap = []) {\n elNamesMap.forEach((elements, domUpdateOpt) => {\n if (!MutateDom[domUpdateOpt]) {\n return;\n }\n // If something was hidden or disabled by default, we don't want to touch it.\n const elCopy = elements.filter((el) => !this.initialDisabledHidden.includes(el));\n const nodes = this.elementNamesToDomNodes(elCopy);\n nodes.forEach((node) => {\n if (node === null) {\n return;\n }\n MutateDom[domUpdateOpt](node);\n });\n });\n }\n\n /**\n * Convert the element names into DOM nodes based on the element names.\n *\n * @param {Array} elementNames The name of dependent elements to get associated DOM nodes.\n * @returns {Array}\n */\n elementNamesToDomNodes(elementNames) {\n return elementNames.map((element) => {\n if (this.form.querySelector(`[data-groupname=\"${element}\"]`)) {\n return this.form.querySelector(`[data-groupname=\"${element}\"]`);\n }\n if (this.findSetEditors(element)) {\n // Text editors are stupid.\n return this.form.elements.namedItem(`${element}[text]`);\n } else if (!this.form.elements.namedItem(element)) {\n // Grouped items are stupid.\n return this.form.elements.namedItem(`id_${element}`);\n }\n // Regular happy plain form item.\n return this.form.elements.namedItem(element);\n });\n }\n\n /**\n * During init, look through the form and identify which elements are editors.\n * Then when given an element name, we can check if it is an editor.\n *\n * @param {String} elementName A name of an element to check if it is an editor.\n * @returns {Boolean} Whether the element is an editor or not.\n */\n findSetEditors(elementName = '') {\n if (this.editors.size === 0) {\n const fEditors = this.form.querySelectorAll('[data-fieldtype=\"editor\"] textarea');\n Array.from(fEditors).forEach((node) => {\n this.editors.set(node.name, true);\n });\n }\n return this.editors.get(`${elementName}[text]`) || false;\n }\n\n /**\n * Convert the dependencies object into a map of elements and their associated rules.\n *\n * @example\n * Note: This is a simplified example of the returned map showing the rules for the grade type element in assign.\n *\n * \"grade[modgrade_type]\" => Map {\n * \"eq\" => Map {\n * \"none\" => Object {\n * 1 => Array [\n * \"advancedgradingmethod_submissions\",\n * \"gradecat\",\n * \"gradepass\",\n * \"completionusegrade\",\n * \"completionusegrade\",\n * ]\n * }\n * },\n * \"neq\" => Map {\n * \"point\" => Object {\n * 1 => Array [\n * \"grade[modgrade_point]\",\n * \"grade[modgrade_rescalegrades]\"\n * ]\n * },\n * \"scale\" => Object {\n * 1 => Array [\n * \"grade[modgrade_scale]\"\n * ]\n * }\n * }\n * }\n *\n * Note: If the value of grade[modgrade_type] === \"none\" then the array of elements defined should be hidden.\n * Note: If the value of grade[modgrade_type] !== \"point\" then the array of elements defined within the following:\n * \"eq\" => \"none\" && \"neq\" => \"scale\" should be hidden.\n *\n * Note: The object within the \"rule\" map can contain either 0 or 1 this helps determine if the element should be:\n * hidden or locked if the rule is met.\n * @See /lib/formslib.php DEP_DISABLE & DEP_HIDE.\n *\n * @param {Object} dependencies The supplied object of form dependencies to migrate into a map.\n * @returns {Map} A map of elements and their associated rules.\n */\n getDependencyMapper(dependencies) {\n /**\n * Convert the object into a first level map. i.e. elementName => ruleType.\n *\n * @type {Map} The map of rules associated to the given element.\n * @example \"grade[modgrade_type]\" => Map<\"eq\", \"neq\">\n */\n const elementMap = new Map(Object.entries(dependencies));\n elementMap.forEach((elementrules, key) => {\n /**\n * Convert the element rules object into a map.\n *\n * @type {Map} The map of rules associated to the given element.\n * @example \"eq\" => Map<\"none\" => Object>\n * @example \"neq\" => Map<\"point\" => Object, \"scale\" => Object>\n */\n const ruleMap = new Map(Object.entries(elementrules));\n ruleMap.forEach((ruleComparisons, key) => {\n /**\n * Convert any disabledIf rules into objects, so we can manage them the same as hideIf items.\n *\n * @type {Map} The map of comparison values t.\n * @example \"none\" => \"none\" => Object\n * @example \"neq\" => \"point\" => Object\n */\n const hideDefine = new Map(Object.entries(ruleComparisons));\n hideDefine.forEach((action, compVal) => {\n if (Array.isArray(action)) {\n action = {...action};\n }\n hideDefine.set(compVal, action);\n });\n ruleMap.set(key, hideDefine);\n });\n elementMap.set(key, ruleMap);\n });\n return elementMap;\n }\n\n /**\n * A standard map that we'll be using to figure out what has to change and how.\n *\n * @returns {Map}\n */\n mapTemplate() {\n return new Map([\n ['hide', []],\n ['show', []],\n ['lock', []],\n ['unlock', []],\n ]);\n }\n\n /**\n * Initialize the form and its dependencies.\n *\n * @param {String} formID The ID of the form to be managed.\n * @param {Object} dependencies The passed object of form dependencies.\n * @returns {Form} An instance associated to a specific form on a given page.\n */\n static init(formID, dependencies) {\n return new Form(formID, dependencies);\n }\n}\n"],"names":["Form","constructor","formID","dependencies","Map","form","document","querySelector","pendingPromise","Pending","this","getDependencyMapper","findSetEditors","rules","Rules","applyInitialState","registerEventListeners","FormChangeChecker","watchForm","resolve","elements","forEach","element","disabled","hidden","name","initialDisabledHidden","push","has","domDispatch","displayMapPrune","dispatchDependencyRules","addEventListener","async","e","target","type","resetFormDirtyState","Submit","init","id","reset","markFormChangedFromNode","MutationObserver","mutation","mutationList","querySelectorAll","observe","attributes","childList","subtree","displayMap","mapTemplate","get","dependants","ruleName","neq","nodeNames","displayOption","spreadFlat","values","flat","set","removeunlockifhidden","filter","x","toString","includes","key","value","length","delete","getDependantsOfType","domUpdateOpt","MutateDom","elCopy","el","elementNamesToDomNodes","node","elementNames","map","namedItem","elementName","editors","size","fEditors","Array","from","elementMap","Object","entries","elementrules","ruleMap","ruleComparisons","hideDefine","action","compVal","isArray"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"form.min.js","sources":["../src/form.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 * This file contains JS functionality required by mforms and is included automatically\n * when required.\n *\n * @see /lib/formslib.php#L2548 Candidate for removal, depends on grouped rules.\n * @see /lib/amd/src/showhidesettings.js Candidate for removal.\n *\n * @module core_form/form\n * @copyright 2024 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport * as FormChangeChecker from './changechecker';\nimport * as Submit from './submit';\nimport Rules from './form/rules';\nimport * as MutateDom from './form/display';\nimport Pending from 'core/pending';\n\nexport default class Form {\n /**\n * @var {HTMLFormElement} form Our very own form to work on.\n */\n form;\n\n /**\n * @var {Map} dependencies Our map of form dependencies.\n */\n dependencies;\n\n /**\n * @var {Map} editors Our map of form editors used get the right selector.\n */\n editors = new Map();\n\n initialDisabledHidden = [];\n\n /**\n * Create a new form instance.\n *\n * @param {String} formID The ID of the form to be managed.\n * @param {Object} dependencies The passed object of form dependencies.\n */\n constructor(formID, dependencies) {\n\n // Set class properties.\n this.form = document.querySelector(`#${formID}`);\n\n const pendingPromise = new Pending('construction');\n // TODO: Food for thought: Display a loader?\n this.dependencies = this.getDependencyMapper(dependencies);\n this.findSetEditors();\n this.rules = new Rules(this);\n\n // Apply the initial state of the form.\n this.applyInitialState();\n\n // Handle mutations within the form.\n this.registerEventListeners();\n FormChangeChecker.watchForm(this.form);\n pendingPromise.resolve();\n }\n\n /**\n * Given the page has loaded, apply the initial state of the form.\n */\n applyInitialState() {\n [...this.form.elements].forEach((element) => {\n if ((element.disabled || element.hidden) && element.name !== '') {\n this.initialDisabledHidden.push(element.name);\n }\n });\n [...this.form.elements].forEach((element) => {\n if (this.dependencies.has(element.name)) {\n this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(element)));\n }\n });\n }\n\n /**\n * Add event listeners to the form.\n */\n registerEventListeners() {\n this.form.addEventListener('change', async(e) => {\n if (e.target.type === 'submit') {\n FormChangeChecker.resetFormDirtyState(this.form);\n Submit.init(e.target.id);\n }\n if (e.target.type === 'reset') {\n FormChangeChecker.resetFormDirtyState(this.form);\n this.form.reset();\n }\n // Something changes based on this element.\n if (this.dependencies.has(e.target.name)) {\n FormChangeChecker.markFormChangedFromNode(e.target);\n const pendingPromise = new Pending('update');\n await this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(e.target)));\n pendingPromise.resolve();\n }\n });\n\n // Check if changed items themselves have rules attached. i.e. mod_data ratings returns scale
which\n // contains scale[modgrade_type] rule that in turn dedicates max grade or scale display.\n const observer = new MutationObserver(async(mutationList) => {\n for (const mutation of mutationList) {\n // Something in the form has changed, so confirm the items against their own rules.\n if (mutation.type === \"attributes\") {\n const inputs = mutation.target.querySelectorAll('input, select, textarea');\n [...inputs].forEach((element) => {\n if (this.dependencies.has(element.name)) {\n const pendingPromise = new Pending('update');\n // Look up any rules associated with the returned items.\n this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(element)));\n pendingPromise.resolve();\n }\n });\n }\n }\n });\n\n // Start observing the target node for configured mutations\n observer.observe(this.form, {attributes: true, childList: true, subtree: true});\n }\n\n /**\n * Dispatch the dependency rules to the appropriate rule handler.\n *\n * @param {HTMLFormElement} target The name associated to the element that has changed.\n * @returns {Map} Actions to be taken along with elements that should be affected.\n */\n dispatchDependencyRules(target) {\n const displayMap = this.mapTemplate();\n this.dependencies.get(target.name).forEach((dependants, ruleName) => {\n // If the rule exists, use it, otherwise fallback to 'neq' which seems to be the \"default\" rule originally.\n const elNamesMap = this.rules[ruleName] ? this.rules[ruleName](target) : this.rules.neq(target);\n // Merge the current rule map with the final display map.\n elNamesMap.forEach((nodeNames, displayOption) => {\n // We want to merge in the new values into the existing value into a tight single array.\n const spreadFlat = [...displayMap.get(displayOption), ...nodeNames.values()].flat();\n displayMap.set(displayOption, spreadFlat);\n });\n });\n return displayMap;\n }\n\n /**\n * By default, the full display map contains empty entries and potential duplicated DOM node names.\n * Here we will get rid of any empty entries and review the locked array. If a node name exists in both\n * the locked and hidden array, it should be removed from the locked array as nodes are locked / disabled when hidden.\n *\n * @param {Map} displayMap Map of elements and their associated rules to prune.\n * @returns {Map|Map<>} The pruned map or map even a fully pruned map if noting has to change.\n */\n displayMapPrune(displayMap) {\n // Filter any unlocked items that pegged to be hidden as they must be locked if they are hidden.\n const removeunlockifhidden = displayMap.get('unlock').filter(x => {\n return !displayMap.get('hide').toString().includes(x.toString());\n });\n displayMap.set('unlock', removeunlockifhidden);\n\n // Remove any empty entries.\n for (const [key, value] of displayMap) {\n if (value.length === 0) {\n displayMap.delete(key);\n }\n }\n return displayMap;\n }\n\n /**\n * For a given element, get the names of DOM nodes that can change based on the given rule type name.\n *\n * @param {String} element The name of the element to get the dependants for.\n * @param {String} type The rule type to get the dependants for.\n * @returns {Array|[]}\n */\n getDependantsOfType(element, type) {\n return this.dependencies.get(element) !== 'undefined' ? this.dependencies.get(element).get(type) ?? [] : [];\n }\n\n /**\n * Dispatch the DOM manipulation to the appropriate function.\n *\n * @param {Map} elNamesMap What needs to change.\n */\n domDispatch(elNamesMap = []) {\n elNamesMap.forEach((elements, domUpdateOpt) => {\n if (!MutateDom[domUpdateOpt]) {\n return;\n }\n // If something was hidden or disabled by default, we don't want to touch it.\n const elCopy = elements.filter((el) => !this.initialDisabledHidden.includes(el));\n const nodes = this.elementNamesToDomNodes(elCopy);\n nodes.forEach((node) => {\n if (node === null) {\n return;\n }\n MutateDom[domUpdateOpt](node);\n });\n });\n }\n\n /**\n * Convert the element names into DOM nodes based on the element names.\n *\n * @param {Array} elementNames The name of dependent elements to get associated DOM nodes.\n * @returns {Array}\n */\n elementNamesToDomNodes(elementNames) {\n return elementNames.map((element) => {\n if (this.form.querySelector(`[data-groupname=\"${element}\"]`)) {\n return this.form.querySelector(`[data-groupname=\"${element}\"]`);\n }\n if (this.findSetEditors(element)) {\n // Text editors are stupid.\n return this.form.elements.namedItem(`${element}[text]`);\n } else if (!this.form.elements.namedItem(element)) {\n // Grouped items are stupid.\n return this.form.elements.namedItem(`id_${element}`);\n }\n // Regular happy plain form item.\n return this.form.elements.namedItem(element);\n });\n }\n\n /**\n * During init, look through the form and identify which elements are editors.\n * Then when given an element name, we can check if it is an editor.\n *\n * @param {String} elementName A name of an element to check if it is an editor.\n * @returns {Boolean} Whether the element is an editor or not.\n */\n findSetEditors(elementName = '') {\n if (this.editors.size === 0) {\n const fEditors = this.form.querySelectorAll('[data-fieldtype=\"editor\"] textarea');\n Array.from(fEditors).forEach((node) => {\n this.editors.set(node.name, true);\n });\n }\n return this.editors.get(`${elementName}[text]`) || false;\n }\n\n /**\n * Convert the dependencies object into a map of elements and their associated rules.\n *\n * @example\n * Note: This is a simplified example of the returned map showing the rules for the grade type element in assign.\n *\n * \"grade[modgrade_type]\" => Map {\n * \"eq\" => Map {\n * \"none\" => Object {\n * 1 => Array [\n * \"advancedgradingmethod_submissions\",\n * \"gradecat\",\n * \"gradepass\",\n * \"completionusegrade\",\n * \"completionusegrade\",\n * ]\n * }\n * },\n * \"neq\" => Map {\n * \"point\" => Object {\n * 1 => Array [\n * \"grade[modgrade_point]\",\n * \"grade[modgrade_rescalegrades]\"\n * ]\n * },\n * \"scale\" => Object {\n * 1 => Array [\n * \"grade[modgrade_scale]\"\n * ]\n * }\n * }\n * }\n *\n * Note: If the value of grade[modgrade_type] === \"none\" then the array of elements defined should be hidden.\n * Note: If the value of grade[modgrade_type] !== \"point\" then the array of elements defined within the following:\n * \"eq\" => \"none\" && \"neq\" => \"scale\" should be hidden.\n *\n * Note: The object within the \"rule\" map can contain either 0 or 1 this helps determine if the element should be:\n * hidden or locked if the rule is met.\n * @See /lib/formslib.php DEP_DISABLE & DEP_HIDE.\n *\n * @param {Object} dependencies The supplied object of form dependencies to migrate into a map.\n * @returns {Map} A map of elements and their associated rules.\n */\n getDependencyMapper(dependencies) {\n /**\n * Convert the object into a first level map. i.e. elementName => ruleType.\n *\n * @type {Map} The map of rules associated to the given element.\n * @example \"grade[modgrade_type]\" => Map<\"eq\", \"neq\">\n */\n const elementMap = new Map(Object.entries(dependencies));\n elementMap.forEach((elementrules, key) => {\n /**\n * Convert the element rules object into a map.\n *\n * @type {Map} The map of rules associated to the given element.\n * @example \"eq\" => Map<\"none\" => Object>\n * @example \"neq\" => Map<\"point\" => Object, \"scale\" => Object>\n */\n const ruleMap = new Map(Object.entries(elementrules));\n ruleMap.forEach((ruleComparisons, key) => {\n /**\n * Convert any disabledIf rules into objects, so we can manage them the same as hideIf items.\n *\n * @type {Map} The map of comparison values t.\n * @example \"none\" => \"none\" => Object\n * @example \"neq\" => \"point\" => Object\n */\n const hideDefine = new Map(Object.entries(ruleComparisons));\n hideDefine.forEach((action, compVal) => {\n if (Array.isArray(action)) {\n action = {...action};\n }\n hideDefine.set(compVal, action);\n });\n ruleMap.set(key, hideDefine);\n });\n elementMap.set(key, ruleMap);\n });\n return elementMap;\n }\n\n /**\n * A standard map that we'll be using to figure out what has to change and how.\n *\n * @returns {Map}\n */\n mapTemplate() {\n return new Map([\n ['hide', []],\n ['show', []],\n ['lock', []],\n ['unlock', []],\n ]);\n }\n\n /**\n * Initialize the form and its dependencies.\n *\n * @param {String} formID The ID of the form to be managed.\n * @param {Object} dependencies The passed object of form dependencies.\n * @returns {Form} An instance associated to a specific form on a given page.\n */\n static init(formID, dependencies) {\n return new Form(formID, dependencies);\n }\n}\n"],"names":["Form","constructor","formID","dependencies","Map","form","document","querySelector","pendingPromise","Pending","this","getDependencyMapper","findSetEditors","rules","Rules","applyInitialState","registerEventListeners","FormChangeChecker","watchForm","resolve","elements","forEach","element","disabled","hidden","name","initialDisabledHidden","push","has","domDispatch","displayMapPrune","dispatchDependencyRules","addEventListener","async","e","target","type","resetFormDirtyState","Submit","init","id","reset","markFormChangedFromNode","MutationObserver","mutation","mutationList","querySelectorAll","observe","attributes","childList","subtree","displayMap","mapTemplate","get","dependants","ruleName","neq","nodeNames","displayOption","spreadFlat","values","flat","set","removeunlockifhidden","filter","x","toString","includes","key","value","length","delete","getDependantsOfType","domUpdateOpt","MutateDom","elCopy","el","elementNamesToDomNodes","node","elementNames","map","namedItem","elementName","editors","size","fEditors","Array","from","elementMap","Object","entries","elementrules","ruleMap","ruleComparisons","hideDefine","action","compVal","isArray"],"mappings":"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"} \ No newline at end of file diff --git a/lib/form/amd/build/form/display.min.js b/lib/form/amd/build/form/display.min.js new file mode 100644 index 0000000000000..6f0549b76a066 --- /dev/null +++ b/lib/form/amd/build/form/display.min.js @@ -0,0 +1,3 @@ +define("core_form/form/display",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.unlock=_exports.show=_exports.lock=_exports.hide=_exports.determineDisplayMap=void 0;_exports.lock=element=>{null===element||element instanceof RadioNodeList||(element.setAttribute("disabled","disabled"),"editor"===element.dataset.fieldtype&&(element.setAttribute("readonly","readonly"),element.dispatchEvent(new Event("form:editorUpdated"))))};_exports.unlock=element=>{null===element||element instanceof RadioNodeList||(element.removeAttribute("disabled"),"editor"===element.dataset.fieldtype&&(element.removeAttribute("readonly"),element.dispatchEvent(new Event("form:editorUpdated"))))};_exports.hide=element=>{if(null!==element&&!(element instanceof RadioNodeList)){element.setAttribute("disabled","disabled");const parent=element.closest(".fitem");if(parent){parent.setAttribute("hidden","hidden"),parent.classList.add("d-none");const label=document.querySelector('label[for="'+element.id+'"]');label&&(label.setAttribute("hidden","hidden"),label.classList.add("d-none"))}}};_exports.show=element=>{if(null!==element&&!(element instanceof RadioNodeList)){element.removeAttribute("disabled");const parent=element.closest(".fitem");if(parent){parent.removeAttribute("hidden"),parent.classList.remove("d-none");const label=document.querySelector('label[for="'+element.id+'"]');label&&(label.removeAttribute("hidden"),label.classList.remove("d-none"))}}};const dependencyBehaviour_disable=0,dependencyBehaviour_hide=1;_exports.determineDisplayMap=(dependant,displayMap,lock)=>{const hide=!!dependant.hasOwnProperty(dependencyBehaviour_hide)&&lock;dependant.hasOwnProperty(dependencyBehaviour_disable)?lock?displayMap.get("lock").push(dependant[dependencyBehaviour_disable]):displayMap.get("unlock").push(dependant[dependencyBehaviour_disable]):dependant.hasOwnProperty(dependencyBehaviour_hide)&&(hide||displayMap.get("hide").toString().includes(dependant[dependencyBehaviour_hide].toString())?displayMap.get("hide").push(dependant[dependencyBehaviour_hide]):displayMap.get("show").push(dependant[dependencyBehaviour_hide]))}})); + +//# sourceMappingURL=display.min.js.map \ No newline at end of file diff --git a/lib/form/amd/build/form/display.min.js.map b/lib/form/amd/build/form/display.min.js.map new file mode 100644 index 0000000000000..c6063cfb4c2a5 --- /dev/null +++ b/lib/form/amd/build/form/display.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"display.min.js","sources":["../../src/form/display.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 * This file contains some helper functions to change the visual state of the form elements.\n *\n * @module core_form/form/dom\n * @copyright 2024 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\n/**\n * Disable an element.\n *\n * @param {HTMLElement} element The element to be disabled.\n */\nexport const lock = (element) => {\n if (element !== null && !(element instanceof RadioNodeList)) {\n element.setAttribute('disabled', 'disabled');\n if (element.dataset.fieldtype === 'editor') {\n element.setAttribute('readonly', 'readonly');\n element.dispatchEvent(new Event('form:editorUpdated'));\n }\n }\n};\n\n/**\n * Enable an element.\n *\n * @param {HTMLElement} element The element to be enabled.\n */\nexport const unlock = (element) => {\n if (element !== null && !(element instanceof RadioNodeList)) {\n element.removeAttribute('disabled');\n if (element.dataset.fieldtype === 'editor') {\n element.removeAttribute('readonly');\n element.dispatchEvent(new Event('form:editorUpdated'));\n }\n }\n};\n\n/**\n * Hide an element.\n *\n * @param {HTMLElement} element The element to be hidden.\n */\nexport const hide = (element) => {\n if (element !== null && !(element instanceof RadioNodeList)) {\n element.setAttribute('disabled', 'disabled');\n const parent = element.closest('.fitem');\n if (parent) {\n parent.setAttribute('hidden', 'hidden');\n parent.classList.add('d-none');\n\n // Hide the label as well.\n const label = document.querySelector('label[for=\"' + element.id + '\"]');\n if (label) {\n label.setAttribute('hidden', 'hidden');\n label.classList.add('d-none');\n }\n }\n }\n};\n\n/**\n * Show an element.\n *\n * @param {HTMLElement} element The elements to be shown.\n */\nexport const show = (element) => {\n if (element !== null && !(element instanceof RadioNodeList)) {\n element.removeAttribute('disabled');\n const parent = element.closest('.fitem');\n if (parent) {\n parent.removeAttribute('hidden');\n parent.classList.remove('d-none');\n\n // Show the label as well.\n const label = document.querySelector('label[for=\"' + element.id + '\"]');\n if (label) {\n label.removeAttribute('hidden');\n label.classList.remove('d-none');\n }\n }\n }\n};\n\n/**\n * A small object that defines the behaviour of the dependency rules for readability.\n *\n * @type {{hide: number, disable: number}}\n */\nconst dependencyBehaviour = {\n disable: 0,\n hide: 1,\n};\n\n/**\n * Considering the dependant object and if we need to lock it, assign the elements to the correct displayMap key.\n *\n * @param {Object} dependant The dependant object that contains the rules for hiding and locking.\n * @param {Map} displayMap The aggregation of elements that should be shown, hidden, locked, or unlocked.\n * @param {Boolean} lock According to the rules, should the element be locked or unlocked.\n */\nexport const determineDisplayMap = (dependant, displayMap, lock) => {\n const hide = dependant.hasOwnProperty(dependencyBehaviour.hide) ? lock : false;\n if (dependant.hasOwnProperty(dependencyBehaviour.disable)) {\n if (lock) {\n displayMap.get('lock').push(dependant[dependencyBehaviour.disable]);\n } else {\n displayMap.get('unlock').push(dependant[dependencyBehaviour.disable]);\n }\n } else if (dependant.hasOwnProperty(dependencyBehaviour.hide)) {\n // Prevent showing an element if it has already been defined hidden.\n if (!hide && !displayMap.get('hide').toString().includes(dependant[dependencyBehaviour.hide].toString())) {\n displayMap.get('show').push(dependant[dependencyBehaviour.hide]);\n } else {\n displayMap.get('hide').push(dependant[dependencyBehaviour.hide]);\n }\n }\n};\n"],"names":["element","RadioNodeList","setAttribute","dataset","fieldtype","dispatchEvent","Event","removeAttribute","parent","closest","classList","add","label","document","querySelector","id","remove","dependencyBehaviour","dependant","displayMap","lock","hide","hasOwnProperty","get","push","toString","includes"],"mappings":"oOA8BqBA,UACD,OAAZA,SAAsBA,mBAAmBC,gBACzCD,QAAQE,aAAa,WAAY,YACC,WAA9BF,QAAQG,QAAQC,YAChBJ,QAAQE,aAAa,WAAY,YACjCF,QAAQK,cAAc,IAAIC,MAAM,0CAUrBN,UACH,OAAZA,SAAsBA,mBAAmBC,gBACzCD,QAAQO,gBAAgB,YACU,WAA9BP,QAAQG,QAAQC,YAChBJ,QAAQO,gBAAgB,YACxBP,QAAQK,cAAc,IAAIC,MAAM,wCAUvBN,aACD,OAAZA,WAAsBA,mBAAmBC,eAAgB,CACzDD,QAAQE,aAAa,WAAY,kBAC3BM,OAASR,QAAQS,QAAQ,aAC3BD,OAAQ,CACRA,OAAON,aAAa,SAAU,UAC9BM,OAAOE,UAAUC,IAAI,gBAGfC,MAAQC,SAASC,cAAc,cAAgBd,QAAQe,GAAK,MAC9DH,QACAA,MAAMV,aAAa,SAAU,UAC7BU,MAAMF,UAAUC,IAAI,4BAWfX,aACD,OAAZA,WAAsBA,mBAAmBC,eAAgB,CACzDD,QAAQO,gBAAgB,kBAClBC,OAASR,QAAQS,QAAQ,aAC3BD,OAAQ,CACRA,OAAOD,gBAAgB,UACvBC,OAAOE,UAAUM,OAAO,gBAGlBJ,MAAQC,SAASC,cAAc,cAAgBd,QAAQe,GAAK,MAC9DH,QACAA,MAAML,gBAAgB,UACtBK,MAAMF,UAAUM,OAAO,oBAWjCC,4BACO,EADPA,yBAEI,+BAUyB,CAACC,UAAWC,WAAYC,cACjDC,OAAOH,UAAUI,eAAeL,2BAA4BG,KAC9DF,UAAUI,eAAeL,6BACrBG,KACAD,WAAWI,IAAI,QAAQC,KAAKN,UAAUD,8BAEtCE,WAAWI,IAAI,UAAUC,KAAKN,UAAUD,8BAErCC,UAAUI,eAAeL,4BAE3BI,MAASF,WAAWI,IAAI,QAAQE,WAAWC,SAASR,UAAUD,0BAA0BQ,YAGzFN,WAAWI,IAAI,QAAQC,KAAKN,UAAUD,2BAFtCE,WAAWI,IAAI,QAAQC,KAAKN,UAAUD"} \ No newline at end of file diff --git a/lib/form/amd/build/form/dom.min.js b/lib/form/amd/build/form/dom.min.js deleted file mode 100644 index cd40a1a25f413..0000000000000 --- a/lib/form/amd/build/form/dom.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("core_form/form/dom",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.unlock=_exports.show=_exports.lock=_exports.hide=void 0;_exports.lock=element=>{null===element||element instanceof RadioNodeList||(element.setAttribute("disabled","disabled"),"editor"===element.dataset.fieldtype&&(element.setAttribute("readonly","readonly"),element.dispatchEvent(new Event("form:editorUpdated"))))};_exports.unlock=element=>{null===element||element instanceof RadioNodeList||(element.removeAttribute("disabled"),"editor"===element.dataset.fieldtype&&(element.removeAttribute("readonly"),element.dispatchEvent(new Event("form:editorUpdated"))))};_exports.hide=element=>{if(null!==element&&!(element instanceof RadioNodeList)){element.setAttribute("disabled","disabled");const parent=element.closest(".fitem");if(parent){parent.setAttribute("hidden","hidden"),parent.classList.add("d-none");const label=document.querySelector('label[for="'+element.id+'"]');label&&(label.setAttribute("hidden","hidden"),label.classList.add("d-none"))}}};_exports.show=element=>{if(null!==element&&!(element instanceof RadioNodeList)){element.removeAttribute("disabled");const parent=element.closest(".fitem");if(parent){parent.removeAttribute("hidden"),parent.classList.remove("d-none");const label=document.querySelector('label[for="'+element.id+'"]');label&&(label.removeAttribute("hidden"),label.classList.remove("d-none"))}}}})); - -//# sourceMappingURL=dom.min.js.map \ No newline at end of file diff --git a/lib/form/amd/build/form/dom.min.js.map b/lib/form/amd/build/form/dom.min.js.map deleted file mode 100644 index 12103ea81c914..0000000000000 --- a/lib/form/amd/build/form/dom.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"dom.min.js","sources":["../../src/form/dom.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 * This file contains some helper functions to change the visual state of the form elements.\n *\n * @module core_form/form/dom\n * @copyright 2024 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\n/**\n * Disable an element.\n *\n * @param {HTMLElement} element The element to be disabled.\n */\nexport const lock = (element) => {\n if (element !== null && !(element instanceof RadioNodeList)) {\n element.setAttribute('disabled', 'disabled');\n if (element.dataset.fieldtype === 'editor') {\n element.setAttribute('readonly', 'readonly');\n element.dispatchEvent(new Event('form:editorUpdated'));\n }\n }\n};\n\n/**\n * Enable an element.\n *\n * @param {HTMLElement} element The element to be enabled.\n */\nexport const unlock = (element) => {\n if (element !== null && !(element instanceof RadioNodeList)) {\n element.removeAttribute('disabled');\n if (element.dataset.fieldtype === 'editor') {\n element.removeAttribute('readonly');\n element.dispatchEvent(new Event('form:editorUpdated'));\n }\n }\n};\n\n/**\n * Hide an element.\n *\n * @param {HTMLElement} element The element to be hidden.\n */\nexport const hide = (element) => {\n if (element !== null && !(element instanceof RadioNodeList)) {\n element.setAttribute('disabled', 'disabled');\n const parent = element.closest('.fitem');\n if (parent) {\n parent.setAttribute('hidden', 'hidden');\n parent.classList.add('d-none');\n\n // Hide the label as well.\n const label = document.querySelector('label[for=\"' + element.id + '\"]');\n if (label) {\n label.setAttribute('hidden', 'hidden');\n label.classList.add('d-none');\n }\n }\n }\n};\n\n/**\n * Show an element.\n *\n * @param {HTMLElement} element The elements to be shown.\n */\nexport const show = (element) => {\n if (element !== null && !(element instanceof RadioNodeList)) {\n element.removeAttribute('disabled');\n const parent = element.closest('.fitem');\n if (parent) {\n parent.removeAttribute('hidden');\n parent.classList.remove('d-none');\n\n // Show the label as well.\n const label = document.querySelector('label[for=\"' + element.id + '\"]');\n if (label) {\n label.removeAttribute('hidden');\n label.classList.remove('d-none');\n }\n }\n }\n};\n"],"names":["element","RadioNodeList","setAttribute","dataset","fieldtype","dispatchEvent","Event","removeAttribute","parent","closest","classList","add","label","document","querySelector","id","remove"],"mappings":"mMA8BqBA,UACD,OAAZA,SAAsBA,mBAAmBC,gBACzCD,QAAQE,aAAa,WAAY,YACC,WAA9BF,QAAQG,QAAQC,YAChBJ,QAAQE,aAAa,WAAY,YACjCF,QAAQK,cAAc,IAAIC,MAAM,0CAUrBN,UACH,OAAZA,SAAsBA,mBAAmBC,gBACzCD,QAAQO,gBAAgB,YACU,WAA9BP,QAAQG,QAAQC,YAChBJ,QAAQO,gBAAgB,YACxBP,QAAQK,cAAc,IAAIC,MAAM,wCAUvBN,aACD,OAAZA,WAAsBA,mBAAmBC,eAAgB,CACzDD,QAAQE,aAAa,WAAY,kBAC3BM,OAASR,QAAQS,QAAQ,aAC3BD,OAAQ,CACRA,OAAON,aAAa,SAAU,UAC9BM,OAAOE,UAAUC,IAAI,gBAGfC,MAAQC,SAASC,cAAc,cAAgBd,QAAQe,GAAK,MAC9DH,QACAA,MAAMV,aAAa,SAAU,UAC7BU,MAAMF,UAAUC,IAAI,4BAWfX,aACD,OAAZA,WAAsBA,mBAAmBC,eAAgB,CACzDD,QAAQO,gBAAgB,kBAClBC,OAASR,QAAQS,QAAQ,aAC3BD,OAAQ,CACRA,OAAOD,gBAAgB,UACvBC,OAAOE,UAAUM,OAAO,gBAGlBJ,MAAQC,SAASC,cAAc,cAAgBd,QAAQe,GAAK,MAC9DH,QACAA,MAAML,gBAAgB,UACtBK,MAAMF,UAAUM,OAAO"} \ No newline at end of file diff --git a/lib/form/amd/build/form/rules.min.js b/lib/form/amd/build/form/rules.min.js index 6387fe7435d27..d2b449bcc083d 100644 --- a/lib/form/amd/build/form/rules.min.js +++ b/lib/form/amd/build/form/rules.min.js @@ -1,3 +1,3 @@ -define("core_form/form/rules",["exports","./util"],(function(_exports,util){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,util=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(util);return _exports.default=class{notchecked(target){const displayMap=this.form.mapTemplate();let lock=!1;return this.getDependantsOfType(target.name,"notchecked").forEach(((dependant,key)=>{lock=Boolean(key)!==target.checked,util.determineDisplayMap(dependant,displayMap,lock)})),this.form.displayMapPrune(displayMap)}checked(target){const displayMap=this.form.mapTemplate();let lock=!1;return this.getDependantsOfType(target.name,"checked").forEach(((dependant,key)=>{lock=Boolean(key)===target.checked,util.determineDisplayMap(dependant,displayMap,lock)})),this.form.displayMapPrune(displayMap)}eq(target){const displayMap=this.form.mapTemplate(),rTarget=this.getRadioFieldVal(target);let lock=!1;return this.getDependantsOfType(target.name,"eq").forEach(((dependant,key)=>"radio"===target.type?(lock=String(key)===String(rTarget.value),void util.determineDisplayMap(dependant,displayMap,lock)):"hidden"===target.type&&this.getHiddenCkbs(target)?(lock=target.checked===Boolean(key),void util.determineDisplayMap(dependant,displayMap,lock)):"checkbox"!==target.type||target.checked?void(target.classList.contains("filepickerhidden")?(lock=!M.form_filepicker.instances[target.id].fileadded,util.determineDisplayMap(dependant,displayMap,lock)):"select"===target.tagName.toLowerCase()&&target.multiple?window.console.error("This is a multiple select",target):(lock=target.value===key,util.determineDisplayMap(dependant,displayMap,lock))):(lock=target.checked===Boolean(key),void util.determineDisplayMap(dependant,displayMap,lock)))),this.form.displayMapPrune(displayMap)}neq(target){var _this$getDependantsOf,_this$getDependantsOf2,_this$getDependantsOf3,_this$getDependantsOf4,_this$getDependantsOf5,_this$getDependantsOf6;const displayMap=this.form.mapTemplate(),rTarget=this.getRadioFieldVal(target);let lock=!1;const maps=[...null!==(_this$getDependantsOf=null===(_this$getDependantsOf2=this.getDependantsOfType(target.name,"neq"))||void 0===_this$getDependantsOf2?void 0:_this$getDependantsOf2.entries())&&void 0!==_this$getDependantsOf?_this$getDependantsOf:[],...null!==(_this$getDependantsOf3=null===(_this$getDependantsOf4=this.getDependantsOfType(target.name,"ne"))||void 0===_this$getDependantsOf4?void 0:_this$getDependantsOf4.entries())&&void 0!==_this$getDependantsOf3?_this$getDependantsOf3:[],...null!==(_this$getDependantsOf5=null===(_this$getDependantsOf6=this.getDependantsOfType(target.name,"noteq"))||void 0===_this$getDependantsOf6?void 0:_this$getDependantsOf6.entries())&&void 0!==_this$getDependantsOf5?_this$getDependantsOf5:[]];return new Map(maps).forEach(((dependant,key)=>"radio"===target.type?(lock=String(key)!==String(rTarget.value),void util.determineDisplayMap(dependant,displayMap,lock)):"hidden"===target.type&&this.getHiddenCkbs(target)?(lock=target.checked!==Boolean(key),void util.determineDisplayMap(dependant,displayMap,lock)):"checkbox"!==target.type||target.checked?void(target.classList.contains("filepickerhidden")?(lock=!!M.form_filepicker.instances[target.id].fileadded,util.determineDisplayMap(dependant,displayMap,lock)):"select"===target.tagName.toLowerCase()&&target.multiple?window.console.error("This is a multiple select",target):(lock=target.value!==key,util.determineDisplayMap(dependant,displayMap,lock))):(lock=target.checked===Boolean(key),void util.determineDisplayMap(dependant,displayMap,lock)))),this.form.displayMapPrune(displayMap)}in(target){const displayMap=this.form.mapTemplate();let lock=!1;return this.getDependantsOfType(target.name,"in").forEach(((dependant,key)=>{lock=key.split("|").includes(target.value),util.determineDisplayMap(dependant,displayMap,lock)})),this.form.displayMapPrune(displayMap)}getDependantsOfType(element,type){return this.form.getDependantsOfType(element,type)}getRadioFieldVal(target){return"radio"===target.type?this.form.form.elements.namedItem(target.name):target}getHiddenCkbs(target){return 0!==this.form.form.querySelectorAll('input[type=checkbox][name="'+target.name+'"]').length}constructor(form){var obj,key,value;value=void 0,(key="form")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.form=form}},_exports.default})); +define("core_form/form/rules",["exports","./display"],(function(_exports,_display){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default=class{notchecked(target){const displayMap=this.form.mapTemplate();let lock=!1;return this.getDependantsOfType(target.name,"notchecked").forEach(((dependant,key)=>{lock=Boolean(key)!==target.checked,(0,_display.determineDisplayMap)(dependant,displayMap,lock)})),this.form.displayMapPrune(displayMap)}checked(target){const displayMap=this.form.mapTemplate();let lock=!1;return this.getDependantsOfType(target.name,"checked").forEach(((dependant,key)=>{lock=Boolean(key)===target.checked,(0,_display.determineDisplayMap)(dependant,displayMap,lock)})),this.form.displayMapPrune(displayMap)}eq(target){const displayMap=this.form.mapTemplate(),rTarget=this.getRadioFieldVal(target);let lock=!1;return this.getDependantsOfType(target.name,"eq").forEach(((dependant,key)=>"radio"===target.type?(lock=String(key)===String(rTarget.value),void(0,_display.determineDisplayMap)(dependant,displayMap,lock)):"hidden"===target.type&&this.getHiddenCkbs(target)?(lock=target.checked===Boolean(key),void(0,_display.determineDisplayMap)(dependant,displayMap,lock)):"checkbox"!==target.type||target.checked?void(target.classList.contains("filepickerhidden")?(lock=!M.form_filepicker.instances[target.id].fileadded,(0,_display.determineDisplayMap)(dependant,displayMap,lock)):"select"===target.tagName.toLowerCase()&&target.multiple?window.console.error("This is a multiple select",target):(lock=target.value===key,(0,_display.determineDisplayMap)(dependant,displayMap,lock))):(lock=target.checked===Boolean(key),void(0,_display.determineDisplayMap)(dependant,displayMap,lock)))),this.form.displayMapPrune(displayMap)}neq(target){var _this$getDependantsOf,_this$getDependantsOf2,_this$getDependantsOf3,_this$getDependantsOf4,_this$getDependantsOf5,_this$getDependantsOf6;const displayMap=this.form.mapTemplate(),rTarget=this.getRadioFieldVal(target);let lock=!1;const maps=[...null!==(_this$getDependantsOf=null===(_this$getDependantsOf2=this.getDependantsOfType(target.name,"neq"))||void 0===_this$getDependantsOf2?void 0:_this$getDependantsOf2.entries())&&void 0!==_this$getDependantsOf?_this$getDependantsOf:[],...null!==(_this$getDependantsOf3=null===(_this$getDependantsOf4=this.getDependantsOfType(target.name,"ne"))||void 0===_this$getDependantsOf4?void 0:_this$getDependantsOf4.entries())&&void 0!==_this$getDependantsOf3?_this$getDependantsOf3:[],...null!==(_this$getDependantsOf5=null===(_this$getDependantsOf6=this.getDependantsOfType(target.name,"noteq"))||void 0===_this$getDependantsOf6?void 0:_this$getDependantsOf6.entries())&&void 0!==_this$getDependantsOf5?_this$getDependantsOf5:[]];return new Map(maps).forEach(((dependant,key)=>"radio"===target.type?(lock=String(key)!==String(rTarget.value),void(0,_display.determineDisplayMap)(dependant,displayMap,lock)):"hidden"===target.type&&this.getHiddenCkbs(target)?(lock=target.checked!==Boolean(key),void(0,_display.determineDisplayMap)(dependant,displayMap,lock)):"checkbox"!==target.type||target.checked?void(target.classList.contains("filepickerhidden")?(lock=!!M.form_filepicker.instances[target.id].fileadded,(0,_display.determineDisplayMap)(dependant,displayMap,lock)):"select"===target.tagName.toLowerCase()&&target.multiple?window.console.error("This is a multiple select",target):(lock=target.value!==key,(0,_display.determineDisplayMap)(dependant,displayMap,lock))):(lock=target.checked===Boolean(key),void(0,_display.determineDisplayMap)(dependant,displayMap,lock)))),this.form.displayMapPrune(displayMap)}in(target){const displayMap=this.form.mapTemplate();let lock=!1;return this.getDependantsOfType(target.name,"in").forEach(((dependant,key)=>{lock=key.split("|").includes(target.value),(0,_display.determineDisplayMap)(dependant,displayMap,lock)})),this.form.displayMapPrune(displayMap)}getDependantsOfType(element,type){return this.form.getDependantsOfType(element,type)}getRadioFieldVal(target){return"radio"===target.type?this.form.form.elements.namedItem(target.name):target}getHiddenCkbs(target){return 0!==this.form.form.querySelectorAll('input[type=checkbox][name="'+target.name+'"]').length}constructor(form){var obj,key,value;value=void 0,(key="form")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.form=form}},_exports.default})); //# sourceMappingURL=rules.min.js.map \ No newline at end of file diff --git a/lib/form/amd/build/form/rules.min.js.map b/lib/form/amd/build/form/rules.min.js.map index eafddf4c881b9..b633753d63c02 100644 --- a/lib/form/amd/build/form/rules.min.js.map +++ b/lib/form/amd/build/form/rules.min.js.map @@ -1 +1 @@ -{"version":3,"file":"rules.min.js","sources":["../../src/form/rules.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 * This file contains a set of rules that elements can be compared against to determine if they should be shown, hidden, etc...\n *\n * @See /lib/pear/HTML/QuickForm/Rule/Compare.php\n * @See https://pear.php.net/manual/en/package.html.html-quickform2.rules.list.php for a list of available rules.\n *\n * @module core_form/form/rules\n * @copyright 2024 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport * as util from './util';\n\nexport default class Rules {\n /**\n * @var {Form} form The instance of the form class that has a DOM node & references matched.\n */\n form;\n\n /**\n * Compare the value of the changed DOM node to the requested rule.\n *\n * @param {HTMLFormElement} target The changed DOM node to be compared against the requested rule.\n * @returns {Map} Actions to be taken along with elements that should be affected.\n */\n notchecked(target) {\n const displayMap = this.form.mapTemplate();\n let lock = false;\n\n this.getDependantsOfType(target.name, 'notchecked').forEach((dependant, key) => {\n lock = Boolean(key) !== target.checked;\n util.determineDisplayMap(dependant, displayMap, lock);\n });\n\n return this.form.displayMapPrune(displayMap);\n }\n\n /**\n * Compare the value of the changed DOM node to the requested rule.\n *\n * @param {HTMLFormElement} target The changed DOM node to be compared against the requested rule.\n * @returns {Map} Actions to be taken along with elements that should be affected.\n */\n checked(target) {\n const displayMap = this.form.mapTemplate();\n let lock = false;\n\n this.getDependantsOfType(target.name, 'checked').forEach((dependant, key) => {\n lock = Boolean(key) === target.checked;\n util.determineDisplayMap(dependant, displayMap, lock);\n });\n\n return this.form.displayMapPrune(displayMap);\n }\n\n /**\n * Compare the value of the changed DOM node to the requested rule.\n *\n * @param {HTMLFormElement} target The changed DOM node to be compared against the requested rule.\n * @returns {Map} Actions to be taken along with elements that should be affected.\n */\n eq(target) {\n const displayMap = this.form.mapTemplate();\n const rTarget = this.getRadioFieldVal(target);\n let lock = false;\n\n this.getDependantsOfType(target.name, 'eq').forEach((dependant, key) => {\n if (target.type === 'radio') {\n lock = String(key) === String(rTarget.value);\n util.determineDisplayMap(dependant, displayMap, lock);\n return;\n } else if (target.type === 'hidden' && this.getHiddenCkbs(target)) {\n // This is the hidden input that is part of an advcheckbox.\n lock = target.checked === Boolean(key);\n util.determineDisplayMap(dependant, displayMap, lock);\n return;\n } else if (target.type === 'checkbox' && !target.checked) {\n lock = target.checked === Boolean(key);\n util.determineDisplayMap(dependant, displayMap, lock);\n return;\n }\n if (target.classList.contains('filepickerhidden')) {\n lock = !M.form_filepicker.instances[target.id].fileadded;\n util.determineDisplayMap(dependant, displayMap, lock);\n } else if (target.tagName.toLowerCase() === 'select' && target.multiple) {\n // Multiple selects can have one or more value assigned. A pipe (|) is used as a value separator\n // when multiple values have to be selected at the same time.\n window.console.error('This is a multiple select', target);\n } else {\n lock = target.value === key;\n util.determineDisplayMap(dependant, displayMap, lock);\n }\n });\n\n return this.form.displayMapPrune(displayMap);\n }\n\n /**\n * Compare the value of the changed DOM node to the requested rule.\n * @See Moodle has some interesting aliasing ne && noteq, this is also the old \"default\" rule.\n *\n * @param {HTMLFormElement} target The changed DOM node to be compared against the requested rule.\n * @returns {Map} Actions to be taken along with elements that should be affected.\n */\n neq(target) {\n const displayMap = this.form.mapTemplate();\n const rTarget = this.getRadioFieldVal(target);\n let lock = false;\n\n // Get all the aliases of neq and check them all at once.\n const maps = [\n ...this.getDependantsOfType(target.name, 'neq')?.entries() ?? [],\n ...this.getDependantsOfType(target.name, 'ne')?.entries() ?? [],\n ...this.getDependantsOfType(target.name, 'noteq')?.entries() ?? [],\n ];\n const condensedMaps = new Map(maps);\n condensedMaps.forEach((dependant, key) => {\n if (target.type === 'radio') {\n lock = String(key) !== String(rTarget.value);\n util.determineDisplayMap(dependant, displayMap, lock);\n return;\n } else if (target.type === 'hidden' && this.getHiddenCkbs(target)) {\n // This is the hidden input that is part of an advcheckbox.\n lock = target.checked !== Boolean(key);\n util.determineDisplayMap(dependant, displayMap, lock);\n return;\n } else if (target.type === 'checkbox' && !target.checked) {\n lock = target.checked === Boolean(key);\n util.determineDisplayMap(dependant, displayMap, lock);\n return;\n }\n if (target.classList.contains('filepickerhidden')) {\n lock = !!M.form_filepicker.instances[target.id].fileadded;\n util.determineDisplayMap(dependant, displayMap, lock);\n } else if (target.tagName.toLowerCase() === 'select' && target.multiple) {\n // Multiple selects can have one or more value assigned. A pipe (|) is used as a value separator\n // when multiple values have to be selected at the same time.\n window.console.error('This is a multiple select', target);\n } else {\n lock = target.value !== key;\n util.determineDisplayMap(dependant, displayMap, lock);\n }\n });\n\n return this.form.displayMapPrune(displayMap);\n }\n\n /**\n * Compare the value of the changed DOM node to the requested rule.\n *\n * @param {HTMLFormElement} target The changed DOM node to be compared against the requested rule.\n * @returns {Map} Actions to be taken along with elements that should be affected.\n */\n in(target) {\n const displayMap = this.form.mapTemplate();\n let lock = false;\n\n this.getDependantsOfType(target.name, 'in').forEach((dependant, key) => {\n lock = key.split('|').includes(target.value);\n util.determineDisplayMap(dependant, displayMap, lock);\n });\n\n return this.form.displayMapPrune(displayMap);\n }\n\n // Getters of map values & form elements.\n\n /**\n * Call off to the Form() to grab the elements that listen to the passed rule for the given element.\n *\n * @param {String} element The name of the element to get the dependants for.\n * @param {String} type The rule type to get the dependants for.\n * @returns {Array|[]}\n */\n getDependantsOfType(element, type) {\n return this.form.getDependantsOfType(element, type);\n }\n\n /**\n * Radio fields are a bit different, they need to be handled differently.\n *\n * @param {HTMLFormElement} target The changed DOM node to find a potential radio field for.\n * @returns {RadioNodeList|HTMLFormElement}\n */\n getRadioFieldVal(target) {\n return target.type === 'radio' ? this.form.form.elements.namedItem(target.name) : target;\n }\n\n /**\n * A small helper to determine if the advcheckboxes are being used.\n *\n * @param {HTMLElement} target The target element to get the hidden checkboxes for.\n * @returns {boolean} Is this a hidden checkbox?\n */\n getHiddenCkbs(target) {\n return this.form.form.querySelectorAll('input[type=checkbox][name=\"' + target.name + '\"]').length !== 0;\n }\n\n /**\n * Constructor for the Rules class.\n * @param {Form} form The form object that the rules are being applied to.\n */\n constructor(form) {\n this.form = form;\n }\n}\n"],"names":["notchecked","target","displayMap","this","form","mapTemplate","lock","getDependantsOfType","name","forEach","dependant","key","Boolean","checked","util","determineDisplayMap","displayMapPrune","eq","rTarget","getRadioFieldVal","type","String","value","getHiddenCkbs","classList","contains","M","form_filepicker","instances","id","fileadded","tagName","toLowerCase","multiple","window","console","error","neq","maps","_this$getDependantsOf2","entries","_this$getDependantsOf4","_this$getDependantsOf6","Map","in","split","includes","element","elements","namedItem","querySelectorAll","length","constructor"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"rules.min.js","sources":["../../src/form/rules.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 * This file contains a set of rules that elements can be compared against to determine if they should be shown, hidden, etc...\n *\n * @See /lib/pear/HTML/QuickForm/Rule/Compare.php\n * @See https://pear.php.net/manual/en/package.html.html-quickform2.rules.list.php for a list of available rules.\n *\n * @module core_form/form/rules\n * @copyright 2024 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport {determineDisplayMap} from './display';\n\nexport default class Rules {\n /**\n * @var {Form} form The instance of the form class that has a DOM node & references matched.\n */\n form;\n\n /**\n * Compare the value of the changed DOM node to the requested rule.\n *\n * @param {HTMLFormElement} target The changed DOM node to be compared against the requested rule.\n * @returns {Map} Actions to be taken along with elements that should be affected.\n */\n notchecked(target) {\n const displayMap = this.form.mapTemplate();\n let lock = false;\n\n this.getDependantsOfType(target.name, 'notchecked').forEach((dependant, key) => {\n lock = Boolean(key) !== target.checked;\n determineDisplayMap(dependant, displayMap, lock);\n });\n\n return this.form.displayMapPrune(displayMap);\n }\n\n /**\n * Compare the value of the changed DOM node to the requested rule.\n *\n * @param {HTMLFormElement} target The changed DOM node to be compared against the requested rule.\n * @returns {Map} Actions to be taken along with elements that should be affected.\n */\n checked(target) {\n const displayMap = this.form.mapTemplate();\n let lock = false;\n\n this.getDependantsOfType(target.name, 'checked').forEach((dependant, key) => {\n lock = Boolean(key) === target.checked;\n determineDisplayMap(dependant, displayMap, lock);\n });\n\n return this.form.displayMapPrune(displayMap);\n }\n\n /**\n * Compare the value of the changed DOM node to the requested rule.\n *\n * @param {HTMLFormElement} target The changed DOM node to be compared against the requested rule.\n * @returns {Map} Actions to be taken along with elements that should be affected.\n */\n eq(target) {\n const displayMap = this.form.mapTemplate();\n const rTarget = this.getRadioFieldVal(target);\n let lock = false;\n\n this.getDependantsOfType(target.name, 'eq').forEach((dependant, key) => {\n if (target.type === 'radio') {\n lock = String(key) === String(rTarget.value);\n determineDisplayMap(dependant, displayMap, lock);\n return;\n } else if (target.type === 'hidden' && this.getHiddenCkbs(target)) {\n // This is the hidden input that is part of an advcheckbox.\n lock = target.checked === Boolean(key);\n determineDisplayMap(dependant, displayMap, lock);\n return;\n } else if (target.type === 'checkbox' && !target.checked) {\n lock = target.checked === Boolean(key);\n determineDisplayMap(dependant, displayMap, lock);\n return;\n }\n if (target.classList.contains('filepickerhidden')) {\n lock = !M.form_filepicker.instances[target.id].fileadded;\n determineDisplayMap(dependant, displayMap, lock);\n } else if (target.tagName.toLowerCase() === 'select' && target.multiple) {\n // Multiple selects can have one or more value assigned. A pipe (|) is used as a value separator\n // when multiple values have to be selected at the same time.\n window.console.error('This is a multiple select', target);\n } else {\n lock = target.value === key;\n determineDisplayMap(dependant, displayMap, lock);\n }\n });\n\n return this.form.displayMapPrune(displayMap);\n }\n\n /**\n * Compare the value of the changed DOM node to the requested rule.\n * @See Moodle has some interesting aliasing ne && noteq, this is also the old \"default\" rule.\n *\n * @param {HTMLFormElement} target The changed DOM node to be compared against the requested rule.\n * @returns {Map} Actions to be taken along with elements that should be affected.\n */\n neq(target) {\n const displayMap = this.form.mapTemplate();\n const rTarget = this.getRadioFieldVal(target);\n let lock = false;\n\n // Get all the aliases of neq and check them all at once.\n const maps = [\n ...this.getDependantsOfType(target.name, 'neq')?.entries() ?? [],\n ...this.getDependantsOfType(target.name, 'ne')?.entries() ?? [],\n ...this.getDependantsOfType(target.name, 'noteq')?.entries() ?? [],\n ];\n const condensedMaps = new Map(maps);\n condensedMaps.forEach((dependant, key) => {\n if (target.type === 'radio') {\n lock = String(key) !== String(rTarget.value);\n determineDisplayMap(dependant, displayMap, lock);\n return;\n } else if (target.type === 'hidden' && this.getHiddenCkbs(target)) {\n // This is the hidden input that is part of an advcheckbox.\n lock = target.checked !== Boolean(key);\n determineDisplayMap(dependant, displayMap, lock);\n return;\n } else if (target.type === 'checkbox' && !target.checked) {\n lock = target.checked === Boolean(key);\n determineDisplayMap(dependant, displayMap, lock);\n return;\n }\n if (target.classList.contains('filepickerhidden')) {\n lock = !!M.form_filepicker.instances[target.id].fileadded;\n determineDisplayMap(dependant, displayMap, lock);\n } else if (target.tagName.toLowerCase() === 'select' && target.multiple) {\n // Multiple selects can have one or more value assigned. A pipe (|) is used as a value separator\n // when multiple values have to be selected at the same time.\n window.console.error('This is a multiple select', target);\n } else {\n lock = target.value !== key;\n determineDisplayMap(dependant, displayMap, lock);\n }\n });\n\n return this.form.displayMapPrune(displayMap);\n }\n\n /**\n * Compare the value of the changed DOM node to the requested rule.\n *\n * @param {HTMLFormElement} target The changed DOM node to be compared against the requested rule.\n * @returns {Map} Actions to be taken along with elements that should be affected.\n */\n in(target) {\n const displayMap = this.form.mapTemplate();\n let lock = false;\n\n this.getDependantsOfType(target.name, 'in').forEach((dependant, key) => {\n lock = key.split('|').includes(target.value);\n determineDisplayMap(dependant, displayMap, lock);\n });\n\n return this.form.displayMapPrune(displayMap);\n }\n\n // Getters of map values & form elements.\n\n /**\n * TODO: Maybe remove as this is only an alias kind of thing...\n * Call off to the Form() to grab the elements that listen to the passed rule for the given element.\n *\n * @param {String} element The name of the element to get the dependants for.\n * @param {String} type The rule type to get the dependants for.\n * @returns {Array|[]}\n */\n getDependantsOfType(element, type) {\n return this.form.getDependantsOfType(element, type);\n }\n\n /**\n * Radio fields are a bit different, they need to be handled differently.\n *\n * @param {HTMLFormElement} target The changed DOM node to find a potential radio field for.\n * @returns {RadioNodeList|HTMLFormElement}\n */\n getRadioFieldVal(target) {\n return target.type === 'radio' ? this.form.form.elements.namedItem(target.name) : target;\n }\n\n /**\n * A small helper to determine if the advcheckboxes are being used.\n *\n * @param {HTMLElement} target The target element to get the hidden checkboxes for.\n * @returns {boolean} Is this a hidden checkbox?\n */\n getHiddenCkbs(target) {\n return this.form.form.querySelectorAll('input[type=checkbox][name=\"' + target.name + '\"]').length !== 0;\n }\n\n /**\n * Constructor for the Rules class.\n * @param {Form} form The form object that the rules are being applied to.\n */\n constructor(form) {\n this.form = form;\n }\n}\n"],"names":["notchecked","target","displayMap","this","form","mapTemplate","lock","getDependantsOfType","name","forEach","dependant","key","Boolean","checked","displayMapPrune","eq","rTarget","getRadioFieldVal","type","String","value","getHiddenCkbs","classList","contains","M","form_filepicker","instances","id","fileadded","tagName","toLowerCase","multiple","window","console","error","neq","maps","_this$getDependantsOf2","entries","_this$getDependantsOf4","_this$getDependantsOf6","Map","in","split","includes","element","elements","namedItem","querySelectorAll","length","constructor"],"mappings":"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"} \ No newline at end of file diff --git a/lib/form/amd/build/form/util.min.js b/lib/form/amd/build/form/util.min.js deleted file mode 100644 index e7a00bf7fe92e..0000000000000 --- a/lib/form/amd/build/form/util.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("core_form/form/util",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.determineDisplayMap=void 0;const dependencyBehaviour_disable=0,dependencyBehaviour_hide=1;_exports.determineDisplayMap=(dependant,displayMap,lock)=>{const hide=!!dependant.hasOwnProperty(dependencyBehaviour_hide)&&lock;dependant.hasOwnProperty(dependencyBehaviour_disable)?lock?displayMap.get("lock").push(dependant[dependencyBehaviour_disable]):displayMap.get("unlock").push(dependant[dependencyBehaviour_disable]):dependant.hasOwnProperty(dependencyBehaviour_hide)&&(hide||displayMap.get("hide").toString().includes(dependant[dependencyBehaviour_hide].toString())?displayMap.get("hide").push(dependant[dependencyBehaviour_hide]):displayMap.get("show").push(dependant[dependencyBehaviour_hide]))}})); - -//# sourceMappingURL=util.min.js.map \ No newline at end of file diff --git a/lib/form/amd/build/form/util.min.js.map b/lib/form/amd/build/form/util.min.js.map deleted file mode 100644 index 5864c5740bb7b..0000000000000 --- a/lib/form/amd/build/form/util.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"util.min.js","sources":["../../src/form/util.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 * This file contains small helper functions used within the form.\n *\n * @module core_form/form/util\n * @copyright 2024 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\n/**\n * A small object that defines the behaviour of the dependency rules for readability.\n *\n * @type {{hide: number, disable: number}}\n */\nconst dependencyBehaviour = {\n disable: 0,\n hide: 1,\n};\n\n/**\n * Considering the dependant object and if we need to lock it, assign the elements to the correct displayMap key.\n *\n * @param {Object} dependant The dependant object that contains the rules for hiding and locking.\n * @param {Map} displayMap The aggregation of elements that should be shown, hidden, locked, or unlocked.\n * @param {Boolean} lock According to the rules, should the element be locked or unlocked.\n */\nexport const determineDisplayMap = (dependant, displayMap, lock) => {\n const hide = dependant.hasOwnProperty(dependencyBehaviour.hide) ? lock : false;\n if (dependant.hasOwnProperty(dependencyBehaviour.disable)) {\n if (lock) {\n displayMap.get('lock').push(dependant[dependencyBehaviour.disable]);\n } else {\n displayMap.get('unlock').push(dependant[dependencyBehaviour.disable]);\n }\n } else if (dependant.hasOwnProperty(dependencyBehaviour.hide)) {\n // Prevent showing an element if it has already been defined hidden.\n if (!hide && !displayMap.get('hide').toString().includes(dependant[dependencyBehaviour.hide].toString())) {\n displayMap.get('show').push(dependant[dependencyBehaviour.hide]);\n } else {\n displayMap.get('hide').push(dependant[dependencyBehaviour.hide]);\n }\n }\n};\n"],"names":["dependencyBehaviour","dependant","displayMap","lock","hide","hasOwnProperty","get","push","toString","includes"],"mappings":"+JA8BMA,4BACO,EADPA,yBAEI,+BAUyB,CAACC,UAAWC,WAAYC,cACjDC,OAAOH,UAAUI,eAAeL,2BAA4BG,KAC9DF,UAAUI,eAAeL,6BACrBG,KACAD,WAAWI,IAAI,QAAQC,KAAKN,UAAUD,8BAEtCE,WAAWI,IAAI,UAAUC,KAAKN,UAAUD,8BAErCC,UAAUI,eAAeL,4BAE3BI,MAASF,WAAWI,IAAI,QAAQE,WAAWC,SAASR,UAAUD,0BAA0BQ,YAGzFN,WAAWI,IAAI,QAAQC,KAAKN,UAAUD,2BAFtCE,WAAWI,IAAI,QAAQC,KAAKN,UAAUD"} \ No newline at end of file diff --git a/lib/form/amd/src/form.js b/lib/form/amd/src/form.js index 684099db70f90..d46e4f6225e61 100644 --- a/lib/form/amd/src/form.js +++ b/lib/form/amd/src/form.js @@ -30,7 +30,7 @@ import * as FormChangeChecker from './changechecker'; import * as Submit from './submit'; import Rules from './form/rules'; -import * as MutateDom from './form/dom'; +import * as MutateDom from './form/display'; import Pending from 'core/pending'; export default class Form { diff --git a/lib/form/amd/src/form/dom.js b/lib/form/amd/src/form/display.js similarity index 67% rename from lib/form/amd/src/form/dom.js rename to lib/form/amd/src/form/display.js index cb98c22707711..d271bdcc72303 100644 --- a/lib/form/amd/src/form/dom.js +++ b/lib/form/amd/src/form/display.js @@ -98,3 +98,38 @@ export const show = (element) => { } } }; + +/** + * A small object that defines the behaviour of the dependency rules for readability. + * + * @type {{hide: number, disable: number}} + */ +const dependencyBehaviour = { + disable: 0, + hide: 1, +}; + +/** + * Considering the dependant object and if we need to lock it, assign the elements to the correct displayMap key. + * + * @param {Object} dependant The dependant object that contains the rules for hiding and locking. + * @param {Map} displayMap The aggregation of elements that should be shown, hidden, locked, or unlocked. + * @param {Boolean} lock According to the rules, should the element be locked or unlocked. + */ +export const determineDisplayMap = (dependant, displayMap, lock) => { + const hide = dependant.hasOwnProperty(dependencyBehaviour.hide) ? lock : false; + if (dependant.hasOwnProperty(dependencyBehaviour.disable)) { + if (lock) { + displayMap.get('lock').push(dependant[dependencyBehaviour.disable]); + } else { + displayMap.get('unlock').push(dependant[dependencyBehaviour.disable]); + } + } else if (dependant.hasOwnProperty(dependencyBehaviour.hide)) { + // Prevent showing an element if it has already been defined hidden. + if (!hide && !displayMap.get('hide').toString().includes(dependant[dependencyBehaviour.hide].toString())) { + displayMap.get('show').push(dependant[dependencyBehaviour.hide]); + } else { + displayMap.get('hide').push(dependant[dependencyBehaviour.hide]); + } + } +}; diff --git a/lib/form/amd/src/form/rules.js b/lib/form/amd/src/form/rules.js index 2274b4e1b4470..43b6aae16f46a 100644 --- a/lib/form/amd/src/form/rules.js +++ b/lib/form/amd/src/form/rules.js @@ -26,7 +26,7 @@ "use strict"; -import * as util from './util'; +import {determineDisplayMap} from './display'; export default class Rules { /** @@ -46,7 +46,7 @@ export default class Rules { this.getDependantsOfType(target.name, 'notchecked').forEach((dependant, key) => { lock = Boolean(key) !== target.checked; - util.determineDisplayMap(dependant, displayMap, lock); + determineDisplayMap(dependant, displayMap, lock); }); return this.form.displayMapPrune(displayMap); @@ -64,7 +64,7 @@ export default class Rules { this.getDependantsOfType(target.name, 'checked').forEach((dependant, key) => { lock = Boolean(key) === target.checked; - util.determineDisplayMap(dependant, displayMap, lock); + determineDisplayMap(dependant, displayMap, lock); }); return this.form.displayMapPrune(displayMap); @@ -84,28 +84,28 @@ export default class Rules { this.getDependantsOfType(target.name, 'eq').forEach((dependant, key) => { if (target.type === 'radio') { lock = String(key) === String(rTarget.value); - util.determineDisplayMap(dependant, displayMap, lock); + determineDisplayMap(dependant, displayMap, lock); return; } else if (target.type === 'hidden' && this.getHiddenCkbs(target)) { // This is the hidden input that is part of an advcheckbox. lock = target.checked === Boolean(key); - util.determineDisplayMap(dependant, displayMap, lock); + determineDisplayMap(dependant, displayMap, lock); return; } else if (target.type === 'checkbox' && !target.checked) { lock = target.checked === Boolean(key); - util.determineDisplayMap(dependant, displayMap, lock); + determineDisplayMap(dependant, displayMap, lock); return; } if (target.classList.contains('filepickerhidden')) { lock = !M.form_filepicker.instances[target.id].fileadded; - util.determineDisplayMap(dependant, displayMap, lock); + determineDisplayMap(dependant, displayMap, lock); } else if (target.tagName.toLowerCase() === 'select' && target.multiple) { // Multiple selects can have one or more value assigned. A pipe (|) is used as a value separator // when multiple values have to be selected at the same time. window.console.error('This is a multiple select', target); } else { lock = target.value === key; - util.determineDisplayMap(dependant, displayMap, lock); + determineDisplayMap(dependant, displayMap, lock); } }); @@ -134,28 +134,28 @@ export default class Rules { condensedMaps.forEach((dependant, key) => { if (target.type === 'radio') { lock = String(key) !== String(rTarget.value); - util.determineDisplayMap(dependant, displayMap, lock); + determineDisplayMap(dependant, displayMap, lock); return; } else if (target.type === 'hidden' && this.getHiddenCkbs(target)) { // This is the hidden input that is part of an advcheckbox. lock = target.checked !== Boolean(key); - util.determineDisplayMap(dependant, displayMap, lock); + determineDisplayMap(dependant, displayMap, lock); return; } else if (target.type === 'checkbox' && !target.checked) { lock = target.checked === Boolean(key); - util.determineDisplayMap(dependant, displayMap, lock); + determineDisplayMap(dependant, displayMap, lock); return; } if (target.classList.contains('filepickerhidden')) { lock = !!M.form_filepicker.instances[target.id].fileadded; - util.determineDisplayMap(dependant, displayMap, lock); + determineDisplayMap(dependant, displayMap, lock); } else if (target.tagName.toLowerCase() === 'select' && target.multiple) { // Multiple selects can have one or more value assigned. A pipe (|) is used as a value separator // when multiple values have to be selected at the same time. window.console.error('This is a multiple select', target); } else { lock = target.value !== key; - util.determineDisplayMap(dependant, displayMap, lock); + determineDisplayMap(dependant, displayMap, lock); } }); @@ -174,7 +174,7 @@ export default class Rules { this.getDependantsOfType(target.name, 'in').forEach((dependant, key) => { lock = key.split('|').includes(target.value); - util.determineDisplayMap(dependant, displayMap, lock); + determineDisplayMap(dependant, displayMap, lock); }); return this.form.displayMapPrune(displayMap); @@ -183,6 +183,7 @@ export default class Rules { // Getters of map values & form elements. /** + * TODO: Maybe remove as this is only an alias kind of thing... * Call off to the Form() to grab the elements that listen to the passed rule for the given element. * * @param {String} element The name of the element to get the dependants for. diff --git a/lib/form/amd/src/form/util.js b/lib/form/amd/src/form/util.js deleted file mode 100644 index 5eb244fc98b62..0000000000000 --- a/lib/form/amd/src/form/util.js +++ /dev/null @@ -1,59 +0,0 @@ -// 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 . - -/** - * This file contains small helper functions used within the form. - * - * @module core_form/form/util - * @copyright 2024 Mathew May - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -"use strict"; - -/** - * A small object that defines the behaviour of the dependency rules for readability. - * - * @type {{hide: number, disable: number}} - */ -const dependencyBehaviour = { - disable: 0, - hide: 1, -}; - -/** - * Considering the dependant object and if we need to lock it, assign the elements to the correct displayMap key. - * - * @param {Object} dependant The dependant object that contains the rules for hiding and locking. - * @param {Map} displayMap The aggregation of elements that should be shown, hidden, locked, or unlocked. - * @param {Boolean} lock According to the rules, should the element be locked or unlocked. - */ -export const determineDisplayMap = (dependant, displayMap, lock) => { - const hide = dependant.hasOwnProperty(dependencyBehaviour.hide) ? lock : false; - if (dependant.hasOwnProperty(dependencyBehaviour.disable)) { - if (lock) { - displayMap.get('lock').push(dependant[dependencyBehaviour.disable]); - } else { - displayMap.get('unlock').push(dependant[dependencyBehaviour.disable]); - } - } else if (dependant.hasOwnProperty(dependencyBehaviour.hide)) { - // Prevent showing an element if it has already been defined hidden. - if (!hide && !displayMap.get('hide').toString().includes(dependant[dependencyBehaviour.hide].toString())) { - displayMap.get('show').push(dependant[dependencyBehaviour.hide]); - } else { - displayMap.get('hide').push(dependant[dependencyBehaviour.hide]); - } - } -}; diff --git a/lib/form/tests/behat/fixtures/form_rules.php b/lib/form/tests/behat/fixtures/form_rules.php index 0558eeef1cf52..3580d53fb0eb8 100644 --- a/lib/form/tests/behat/fixtures/form_rules.php +++ b/lib/form/tests/behat/fixtures/form_rules.php @@ -150,23 +150,24 @@ public function definition() { $mform->disabledIf('tii_in_btn', 'tii_btn', 'in', [9, 10]); $mform->hideIf('tii_in_btn', 'tii_btn', 'in', [11, 12]); - /** - * TODO: Confirm if hide/disable rules work for the following input types. - * May be of use to reference: $mform->addRule('edt', get_string('required'), 'required'); - */ - // Date selector rule test. $mform->addElement('header', 'dateselectorheader', 'Date selector: ~'); $mform->setExpanded('dateselectorheader'); $mform->addElement('checkbox', 'ds_enb', get_string('enable')); - $mform->addElement('date_selector', 'ds', 'Date selector'); - $mform->hideIf("ds", 'ds_enb'); + $mform->setDefault('ds_enb', 1); + $mform->addElement('checkbox', 'ds_dis', get_string('show')); + $mform->setDefault('ds_dis', 1); + $mform->addElement('date_selector', 'ds', 'Date selector for testing'); + $mform->disabledIf("ds", 'ds_enb'); + $mform->hideIf("ds", 'ds_dis'); // Editor rule test. $mform->addElement('header', 'editorheader', 'Editor: ~'); $mform->setExpanded('editorheader'); $mform->addElement('checkbox', 'edt_enb', get_string('enable')); - $mform->addElement('checkbox', 'edt_dis', get_string('hide')); + $mform->setDefault('edt_enb', 1); + $mform->addElement('checkbox', 'edt_dis', get_string('show')); + $mform->setDefault('edt_dis', 1); $editoroptions = [ 'subdirs' => 0, 'maxbytes' => 0, @@ -183,8 +184,11 @@ public function definition() { // Filepicker rule test. $mform->addElement('header', 'filepickerheader', 'Filepicker: ~'); - //$mform->setExpanded('filepickerheader'); - $mform->addElement('filepicker', 'modelfile', get_string('file'), null, ['accepted_types' => '*']); + $mform->setExpanded('filepickerheader'); + $mform->addElement('checkbox', 'fp_dis', get_string('show')); + $mform->setDefault('fp_dis', 1); + $mform->addElement('filepicker', 'fp', 'Filepicker for testing', null, ['accepted_types' => '*']); + $mform->hideIf("fp", 'fp_dis'); $this->add_action_buttons(false, 'Send form'); } diff --git a/lib/form/tests/behat/form_rules.feature b/lib/form/tests/behat/form_rules.feature index 5b55c66d12492..3e0f9975d4398 100644 --- a/lib/form/tests/behat/form_rules.feature +++ b/lib/form/tests/behat/form_rules.feature @@ -1,4 +1,4 @@ -@core @core_form @MDL-XXXXX +@core @core_form @test Feature: Test the form rules For forms that make use of different rules So that users can use form elements @@ -10,18 +10,26 @@ Feature: Test the form rules And I wait until the page is ready @javascript - Scenario Outline: Checkbox checked & notchecked rules + Scenario Outline: Checkbox, Date selector, Editor, Filepicker rules rules Given I should "