diff --git a/lib/classes/output/requirements/page_requirements_manager.php b/lib/classes/output/requirements/page_requirements_manager.php
index a417d4e9ac6c2..e9dd2025baa9b 100644
--- a/lib/classes/output/requirements/page_requirements_manager.php
+++ b/lib/classes/output/requirements/page_requirements_manager.php
@@ -1099,7 +1099,7 @@ public function js_call_amd($fullmodule, $func = null, $params = []) {
$jsonparams[] = json_encode($param);
}
$strparams = implode(', ', $jsonparams);
- if ($CFG->debugdeveloper) {
+ if ($CFG->debugdeveloper && $fullmodule !== 'core_form/form') {
$toomanyparamslimit = 1024;
if (strlen($strparams) > $toomanyparamslimit) {
debugging('Too much data passed as arguments to js_call_amd("' . $fullmodule . '", "' . $func .
diff --git a/lib/form/amd/build/form.min.js b/lib/form/amd/build/form.min.js
index b4270eaae1f52..82a21746e8547 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","./events","./submit","./form/rules","./form/dom"],(function(_exports,FormChangeChecker,FormEvents,Submit,_rules,MutateDom){var 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),FormEvents=_interopRequireWildcard(FormEvents),Submit=_interopRequireWildcard(Submit),_rules=(obj=_rules)&&obj.__esModule?obj:{default:obj},MutateDom=_interopRequireWildcard(MutateDom);class Form{constructor(formID,dependencies){_defineProperty(this,"form",void 0),_defineProperty(this,"dependencies",void 0),this.form=document.querySelector("#".concat(formID)),this.dependencies=this.getDependencyMapper(dependencies),this.rules=new _rules.default(this),this.registerEventListeners(),FormChangeChecker.watchForm(this.form)}registerEventListeners(){this.form.addEventListener("change",(e=>{if("submit"===e.target.type&&(FormEvents.notifyFormSubmittedByJavascript(this.form),FormChangeChecker.resetFormDirtyState(this.form),Submit.init(e.target.id)),"reset"===e.target.type&&this.form.reset(),this.dependencies.has(e.target.name)){const displayMap=this.dispatchDependencyRules(e.target),reducedDisplayMap=this.displayMapPrune(displayMap);this.domDispatch(reducedDisplayMap)}}))}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)=>{displayMap.get(displayOption).push(...nodeNames)}))})),displayMap}displayMapPrune(displayMap){window.console.log("preprune:",displayMap);const removelockifhidden=displayMap.get("lock").filter((x=>displayMap.get("hide").includes(x)));window.console.log("removelockifhidden:",removelockifhidden),displayMap.set("lock",removelockifhidden);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(elNamesMap){window.console.log("elNamesMap",elNamesMap),elNamesMap.forEach(((elements,domUpdateOpt)=>{if(!MutateDom[domUpdateOpt])return;this.elementNamesToDomNodes(elements).forEach((node=>{null!==node&&MutateDom[domUpdateOpt](node)}))}))}elementNamesToDomNodes(elementNames){return elementNames.map((element=>this.form.elements.namedItem(element)))}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)=>{ruleMap.set(key,new Map(Object.entries(ruleComparisons)))})),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/dom"],(function(_exports,FormChangeChecker,Submit,_rules,MutateDom){var 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=(obj=_rules)&&obj.__esModule?obj:{default:obj},MutateDom=_interopRequireWildcard(MutateDom);class Form{constructor(formID,dependencies){_defineProperty(this,"form",void 0),_defineProperty(this,"dependencies",void 0),this.form=document.querySelector("#".concat(formID)),this.dependencies=this.getDependencyMapper(dependencies),this.rules=new _rules.default(this),this.applyInitialState(),this.registerEventListeners(),FormChangeChecker.watchForm(this.form)}applyInitialState(){[...this.form.elements].forEach((element=>{this.dependencies.has(element.name)&&this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(element)))}))}registerEventListeners(){this.form.addEventListener("change",(e=>{"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),this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(e.target))))}))}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)=>{displayMap.get(displayOption).push(...nodeNames)}))})),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(elNamesMap){elNamesMap.forEach(((elements,domUpdateOpt)=>{if(!MutateDom[domUpdateOpt])return;this.elementNamesToDomNodes(elements).forEach((node=>{null!==node&&MutateDom[domUpdateOpt](node)}))}))}elementNamesToDomNodes(elementNames){return elementNames.map((element=>element.map((element2=>this.form.elements.namedItem(element2)))))}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 3cbd0752e884e..a0226b31f88f0 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\n// Pure form functionality.\n// import {serialize} from './util';\nimport * as FormChangeChecker from './changechecker';\nimport * as FormEvents from './events';\nimport * as Submit from './submit';\nimport Rules from './form/rules';\nimport * as MutateDom from './form/dom';\n\n// Maybe not needed but noted just in case.\n// import * as CollapseSections from './collapsesections'; // This is included via ../collapsesections.mustache\n// import * as EncryptedPassword from './encryptedpassword'; // This is included via ../setting_encryptedpassword.mustache\n// import * as FileTypes from './filetypes'; // This is included via /lib/form/filetypes.php#L152\n// import * as PasswordUnmask from './passwordunmask'; // This is included via ../element-passwordunmask.mustache\n// import * as ShowAdvanced from './showadvanced'; // This is included via /lib/formslib.php#L3349\n\n// Custom element types, Likely not needed.\n// import * as ChoiceDropdown from 'core_form/choicedropdown'; // This is included via ../choicedropdown.mustache\n// import * as ConfigText_maxlength from 'core_form/configtext_maxlength'; // This is included via lib/adminlib.php#L2635\n// import * as DefaultCustom from 'core_form/defaultcustom'; // This is included via lib/form/defaultcustom.php#L253\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 * 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 // Set class properties.\n this.form = document.querySelector(`#${formID}`);\n this.dependencies = this.getDependencyMapper(dependencies);\n this.rules = new Rules(this);\n\n // Handle mutations within the form.\n this.registerEventListeners();\n FormChangeChecker.watchForm(this.form);\n // TODO: Apply the rules to the form on load.\n }\n\n /**\n * Add event listeners to the form.\n */\n registerEventListeners() {\n this.form.addEventListener('change', (e) => {\n if (e.target.type === 'submit') {\n // TODO: Dummy calls so import list looks better...\n // Notify listeners that the form is about to be submitted.\n FormEvents.notifyFormSubmittedByJavascript(this.form);\n FormChangeChecker.resetFormDirtyState(this.form);\n Submit.init(e.target.id);\n }\n if (e.target.type === 'reset') {\n this.form.reset();\n }\n // Something changes based on this element.\n if (this.dependencies.has(e.target.name)) {\n const displayMap = this.dispatchDependencyRules(e.target);\n const reducedDisplayMap = this.displayMapPrune(displayMap);\n this.domDispatch(reducedDisplayMap);\n }\n });\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 displayMap.get(displayOption).push(...nodeNames);\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 window.console.log('preprune:', displayMap);\n // Filter any locked items that are to be hidden anyway.\n const removelockifhidden = displayMap.get('lock').filter(x => displayMap.get('hide').includes(x));\n window.console.log('removelockifhidden:', removelockifhidden);\n displayMap.set('lock', removelockifhidden);\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 => displayMap.get('hide').includes(x));\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 window.console.log('elNamesMap', elNamesMap);\n elNamesMap.forEach((elements, domUpdateOpt) => {\n if (!MutateDom[domUpdateOpt]) {\n return;\n }\n const nodes = this.elementNamesToDomNodes(elements);\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 return this.form.elements.namedItem(element);\n });\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 ruleMap.set(key, new Map(Object.entries(ruleComparisons)));\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","form","document","querySelector","this","getDependencyMapper","rules","Rules","registerEventListeners","FormChangeChecker","watchForm","addEventListener","e","target","type","FormEvents","notifyFormSubmittedByJavascript","resetFormDirtyState","Submit","init","id","reset","has","name","displayMap","dispatchDependencyRules","reducedDisplayMap","displayMapPrune","domDispatch","mapTemplate","get","forEach","dependants","ruleName","neq","nodeNames","displayOption","push","window","console","log","removelockifhidden","filter","x","includes","set","key","value","length","delete","getDependantsOfType","element","elNamesMap","elements","domUpdateOpt","MutateDom","elementNamesToDomNodes","node","elementNames","map","namedItem","elementMap","Map","Object","entries","elementrules","ruleMap","ruleComparisons"],"mappings":"gmDAiDqBA,KAiBjBC,YAAYC,OAAQC,mGAEXC,KAAOC,SAASC,yBAAkBJ,cAClCC,aAAeI,KAAKC,oBAAoBL,mBACxCM,MAAQ,IAAIC,eAAMH,WAGlBI,yBACLC,kBAAkBC,UAAUN,KAAKH,MAOrCO,8BACSP,KAAKU,iBAAiB,UAAWC,OACZ,WAAlBA,EAAEC,OAAOC,OAGTC,WAAWC,gCAAgCZ,KAAKH,MAChDQ,kBAAkBQ,oBAAoBb,KAAKH,MAC3CiB,OAAOC,KAAKP,EAAEC,OAAOO,KAEH,UAAlBR,EAAEC,OAAOC,WACJb,KAAKoB,QAGVjB,KAAKJ,aAAasB,IAAIV,EAAEC,OAAOU,MAAO,OAChCC,WAAapB,KAAKqB,wBAAwBb,EAAEC,QAC5Ca,kBAAoBtB,KAAKuB,gBAAgBH,iBAC1CI,YAAYF,uBAW7BD,wBAAwBZ,cACdW,WAAapB,KAAKyB,0BACnB7B,aAAa8B,IAAIjB,OAAOU,MAAMQ,SAAQ,CAACC,WAAYC,aAEjC7B,KAAKE,MAAM2B,UAAY7B,KAAKE,MAAM2B,UAAUpB,QAAUT,KAAKE,MAAM4B,IAAIrB,SAE7EkB,SAAQ,CAACI,UAAWC,iBAC3BZ,WAAWM,IAAIM,eAAeC,QAAQF,iBAGvCX,WAWXG,gBAAgBH,YACZc,OAAOC,QAAQC,IAAI,YAAahB,kBAE1BiB,mBAAqBjB,WAAWM,IAAI,QAAQY,QAAOC,GAAKnB,WAAWM,IAAI,QAAQc,SAASD,KAC9FL,OAAOC,QAAQC,IAAI,sBAAuBC,oBAC1CjB,WAAWqB,IAAI,OAAQJ,wBAMlB,MAAOK,IAAKC,SAAUvB,WACF,IAAjBuB,MAAMC,QACNxB,WAAWyB,OAAOH,YAGnBtB,WAUX0B,oBAAoBC,QAASrC,sCACiB,cAAnCV,KAAKJ,aAAa8B,IAAIqB,wCAA2B/C,KAAKJ,aAAa8B,IAAIqB,SAASrB,IAAIhB,6DAAc,GAQ7Gc,YAAYwB,YACRd,OAAOC,QAAQC,IAAI,aAAcY,YACjCA,WAAWrB,SAAQ,CAACsB,SAAUC,oBACrBC,UAAUD,qBAGDlD,KAAKoD,uBAAuBH,UACpCtB,SAAS0B,OACE,OAATA,MAGJF,UAAUD,cAAcG,YAWpCD,uBAAuBE,qBACZA,aAAaC,KAAKR,SACd/C,KAAKH,KAAKoD,SAASO,UAAUT,WAgD5C9C,oBAAoBL,oBAOV6D,WAAa,IAAIC,IAAIC,OAAOC,QAAQhE,sBAC1C6D,WAAW9B,SAAQ,CAACkC,aAAcnB,aAQxBoB,QAAU,IAAIJ,IAAIC,OAAOC,QAAQC,eACvCC,QAAQnC,SAAQ,CAACoC,gBAAiBrB,OAC9BoB,QAAQrB,IAAIC,IAAK,IAAIgB,IAAIC,OAAOC,QAAQG,sBAE5CN,WAAWhB,IAAIC,IAAKoB,YAEjBL,WAQXhC,qBACW,IAAIiC,IAAI,CACX,CAAC,OAAQ,IACT,CAAC,OAAQ,IACT,CAAC,OAAQ,IACT,CAAC,SAAU,kBAWP/D,OAAQC,qBACT,IAAIH,KAAKE,OAAQC"}
\ 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\n// Pure form functionality.\n// import {serialize} from './util';\nimport * as FormChangeChecker from './changechecker';\nimport * as Submit from './submit';\nimport Rules from './form/rules';\nimport * as MutateDom from './form/dom';\n\n// Maybe not needed but noted just in case.\n// import * as CollapseSections from './collapsesections'; // This is included via ../collapsesections.mustache\n// import * as EncryptedPassword from './encryptedpassword'; // This is included via ../setting_encryptedpassword.mustache\n// import * as FileTypes from './filetypes'; // This is included via /lib/form/filetypes.php#L152\n// import * as PasswordUnmask from './passwordunmask'; // This is included via ../element-passwordunmask.mustache\n// import * as ShowAdvanced from './showadvanced'; // This is included via /lib/formslib.php#L3349\n\n// Custom element types, Likely not needed.\n// import * as ChoiceDropdown from 'core_form/choicedropdown'; // This is included via ../choicedropdown.mustache\n// import * as ConfigText_maxlength from 'core_form/configtext_maxlength'; // This is included via lib/adminlib.php#L2635\n// import * as DefaultCustom from 'core_form/defaultcustom'; // This is included via lib/form/defaultcustom.php#L253\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 * 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 // Set class properties.\n this.form = document.querySelector(`#${formID}`);\n this.dependencies = this.getDependencyMapper(dependencies);\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 }\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 (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', (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 this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(e.target)));\n }\n });\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 displayMap.get(displayOption).push(...nodeNames);\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 const nodes = this.elementNamesToDomNodes(elements);\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 // TODO: Rename and document why we need to deep look if there are multiple nodes effected by the rule.\n return element.map((element2) => {\n return this.form.elements.namedItem(element2);\n });\n });\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","form","document","querySelector","this","getDependencyMapper","rules","Rules","applyInitialState","registerEventListeners","FormChangeChecker","watchForm","elements","forEach","element","has","name","domDispatch","displayMapPrune","dispatchDependencyRules","addEventListener","e","target","type","resetFormDirtyState","Submit","init","id","reset","markFormChangedFromNode","displayMap","mapTemplate","get","dependants","ruleName","neq","nodeNames","displayOption","push","removeunlockifhidden","filter","x","toString","includes","set","key","value","length","delete","getDependantsOfType","elNamesMap","domUpdateOpt","MutateDom","elementNamesToDomNodes","node","elementNames","map","element2","namedItem","elementMap","Map","Object","entries","elementrules","ruleMap","ruleComparisons","hideDefine","action","compVal","Array","isArray"],"mappings":"2hDAgDqBA,KAiBjBC,YAAYC,OAAQC,mGAEXC,KAAOC,SAASC,yBAAkBJ,cAClCC,aAAeI,KAAKC,oBAAoBL,mBACxCM,MAAQ,IAAIC,eAAMH,WAGlBI,yBAGAC,yBACLC,kBAAkBC,UAAUP,KAAKH,MAMrCO,wBACQJ,KAAKH,KAAKW,UAAUC,SAASC,UACzBV,KAAKJ,aAAae,IAAID,QAAQE,YACzBC,YAAYb,KAAKc,gBAAgBd,KAAKe,wBAAwBL,cAQ/EL,8BACSR,KAAKmB,iBAAiB,UAAWC,IACZ,WAAlBA,EAAEC,OAAOC,OACTb,kBAAkBc,oBAAoBpB,KAAKH,MAC3CwB,OAAOC,KAAKL,EAAEC,OAAOK,KAEH,UAAlBN,EAAEC,OAAOC,OACTb,kBAAkBc,oBAAoBpB,KAAKH,WACtCA,KAAK2B,SAGVxB,KAAKJ,aAAae,IAAIM,EAAEC,OAAON,QAC/BN,kBAAkBmB,wBAAwBR,EAAEC,aACvCL,YAAYb,KAAKc,gBAAgBd,KAAKe,wBAAwBE,EAAEC,cAWjFH,wBAAwBG,cACdQ,WAAa1B,KAAK2B,0BACnB/B,aAAagC,IAAIV,OAAON,MAAMH,SAAQ,CAACoB,WAAYC,aAEjC9B,KAAKE,MAAM4B,UAAY9B,KAAKE,MAAM4B,UAAUZ,QAAUlB,KAAKE,MAAM6B,IAAIb,SAE7ET,SAAQ,CAACuB,UAAWC,iBAC3BP,WAAWE,IAAIK,eAAeC,QAAQF,iBAGvCN,WAWXZ,gBAAgBY,kBAENS,qBAAuBT,WAAWE,IAAI,UAAUQ,QAAOC,IACjDX,WAAWE,IAAI,QAAQU,WAAWC,SAASF,EAAEC,cAEzDZ,WAAWc,IAAI,SAAUL,0BAGpB,MAAOM,IAAKC,SAAUhB,WACF,IAAjBgB,MAAMC,QACNjB,WAAWkB,OAAOH,YAGnBf,WAUXmB,oBAAoBnC,QAASS,sCACiB,cAAnCnB,KAAKJ,aAAagC,IAAIlB,wCAA2BV,KAAKJ,aAAagC,IAAIlB,SAASkB,IAAIT,6DAAc,GAQ7GN,YAAYiC,YACRA,WAAWrC,SAAQ,CAACD,SAAUuC,oBACrBC,UAAUD,qBAGD/C,KAAKiD,uBAAuBzC,UACpCC,SAASyC,OACE,OAATA,MAGJF,UAAUD,cAAcG,YAWpCD,uBAAuBE,qBACZA,aAAaC,KAAK1C,SAEdA,QAAQ0C,KAAKC,UACTrD,KAAKH,KAAKW,SAAS8C,UAAUD,cAiDhDpD,oBAAoBL,oBAOV2D,WAAa,IAAIC,IAAIC,OAAOC,QAAQ9D,sBAC1C2D,WAAW9C,SAAQ,CAACkD,aAAclB,aAQxBmB,QAAU,IAAIJ,IAAIC,OAAOC,QAAQC,eACvCC,QAAQnD,SAAQ,CAACoD,gBAAiBpB,aAQxBqB,WAAa,IAAIN,IAAIC,OAAOC,QAAQG,kBAC1CC,WAAWrD,SAAQ,CAACsD,OAAQC,WACpBC,MAAMC,QAAQH,UACdA,OAAS,IAAIA,SAEjBD,WAAWtB,IAAIwB,QAASD,WAE5BH,QAAQpB,IAAIC,IAAKqB,eAErBP,WAAWf,IAAIC,IAAKmB,YAEjBL,WAQX5B,qBACW,IAAI6B,IAAI,CACX,CAAC,OAAQ,IACT,CAAC,OAAQ,IACT,CAAC,OAAQ,IACT,CAAC,SAAU,kBAWP7D,OAAQC,qBACT,IAAIH,KAAKE,OAAQC"}
\ 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
index 431f307508586..7c22a1db52025 100644
--- a/lib/form/amd/build/form/dom.min.js
+++ b/lib/form/amd/build/form/dom.min.js
@@ -1,3 +1,3 @@
-define("core_form/form/dom",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.unlock=_exports.show=_exports.lock=_exports.hide=void 0;const lock=element=>{};_exports.lock=lock;_exports.unlock=element=>{show(element)};_exports.hide=element=>{};const show=element=>{};_exports.show=show}));
+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=elements=>{elements.forEach((element=>{null!==element&&element.setAttribute("disabled","disabled")}))};_exports.unlock=elements=>{elements.forEach((element=>{null!==element&&element.removeAttribute("disabled")}))};_exports.hide=elements=>{elements.forEach((element=>{if(null!==element){const parent=element.closest("[data-groupname]")?element.closest("[data-groupname]"):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=elements=>{elements.forEach((element=>{if(null!==element){const parent=element.closest("[data-groupname]")?element.closest("[data-groupname]"):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
index 65fa46cd79c61..a1e3793d292c1 100644
--- a/lib/form/amd/build/form/dom.min.js.map
+++ b/lib/form/amd/build/form/dom.min.js.map
@@ -1 +1 @@
-{"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 * TODO: Considerations requiring action:\n *\n * Things old YUI looked for after grabbing the element DOM ancestor (Label management):\n * Classes: fitem fitem_fgroup\n * Data attributes: label[for=\"' + id + '\"]\n * Selectors from old YUI:\n * .fitem [data-fieldtype=\"filepicker\"] input,\n * .fitem [data-fieldtype=\"filemanager\"] input,\n * .fitem [data-fieldtype=\"group\"] input[id*=\"filemanager\"]\n *\n * Guessed selectors:\n * [data-groupname=\"${element}\"]\n *\n * Other things to consider:\n * Special handling for editors & filepickers i.e. Name suffix on editors -> See old YUI?\n * element.setAttribute('aria-disabled', >);\n * element.setAttribute('readonly', 'readonly');\n */\n\n/**\n * Disable an element.\n *\n * @param {HTMLElement} element The element to be disabled.\n */\n// eslint-disable-next-line no-unused-vars\nexport const lock = (element) => {\n // window.console.log('Lock node:', element);\n // element.disabled = true;\n // element.classList.add('disabled');\n};\n\n/**\n * Enable an element.\n *\n * @param {HTMLElement} element The element to be enabled.\n */\nexport const unlock = (element) => {\n show(element);\n // window.console.log('Unlock node:', element);\n // element.disabled = false;\n // element.classList.remove('disabled');\n};\n\n/**\n * Hide an element.\n *\n * @param {HTMLElement} element The element to be hidden.\n */\nexport const hide = (element) => {\n // Lock the element to prevent interaction.\n lock(element);\n // window.console.log('Hide node:', element);\n // element.hidden = true;\n // element.classList.add('hidden');\n};\n\n/**\n * Show an element.\n *\n * @param {HTMLElement} element The element to be shown.\n */\n// eslint-disable-next-line no-unused-vars\nexport const show = (element) => {\n //window.console.log('Show node:', element);\n // element.hidden = false;\n // element.classList.remove('hidden');\n};\n"],"names":["lock","element","show"],"mappings":"2LAmDaA,KAAQC,+CAWEA,UACnBC,KAAKD,wBAWYA,kBAcRC,KAAQD"}
\ No newline at end of file
+{"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 {Array} elements The element to be disabled.\n */\nexport const lock = (elements) => {\n elements.forEach(element => {\n if (element !== null) {\n element.setAttribute('disabled', 'disabled');\n }\n });\n};\n\n/**\n * Enable an element.\n *\n * @param {Array} elements The element to be enabled.\n */\nexport const unlock = (elements) => {\n elements.forEach(element => {\n if (element !== null) {\n element.removeAttribute('disabled');\n }\n });\n};\n\n/**\n * Hide an element.\n *\n * @param {Array} elements The elements to be hidden.\n */\nexport const hide = (elements) => {\n elements.forEach(element => {\n if (element !== null) {\n // Check if the element is part of a group otherwise, just grab the nearest wrapping element.\n const parent = element.closest('[data-groupname]') ? element.closest('[data-groupname]') : 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/**\n * Show an element.\n *\n * @param {Array} elements The elements to be shown.\n */\nexport const show = (elements) => {\n elements.forEach(element => {\n if (element !== null) {\n // Check if the element is part of a group otherwise, just grab the nearest wrapping element.\n const parent = element.closest('[data-groupname]') ? element.closest('[data-groupname]') : 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"],"names":["elements","forEach","element","setAttribute","removeAttribute","parent","closest","classList","add","label","document","querySelector","id","remove"],"mappings":"mMA8BqBA,WACjBA,SAASC,SAAQC,UACG,OAAZA,SACAA,QAAQC,aAAa,WAAY,gCAUtBH,WACnBA,SAASC,SAAQC,UACG,OAAZA,SACAA,QAAQE,gBAAgB,8BAUfJ,WACjBA,SAASC,SAAQC,aACG,OAAZA,QAAkB,OAEZG,OAASH,QAAQI,QAAQ,oBAAsBJ,QAAQI,QAAQ,oBAAsBJ,QAAQI,QAAQ,aACvGD,OAAQ,CACRA,OAAOF,aAAa,SAAU,UAC9BE,OAAOE,UAAUC,IAAI,gBAGfC,MAAQC,SAASC,cAAc,cAAgBT,QAAQU,GAAK,MAC9DH,QACAA,MAAMN,aAAa,SAAU,UAC7BM,MAAMF,UAAUC,IAAI,+BAYnBR,WACjBA,SAASC,SAAQC,aACG,OAAZA,QAAkB,OAEZG,OAASH,QAAQI,QAAQ,oBAAsBJ,QAAQI,QAAQ,oBAAsBJ,QAAQI,QAAQ,aACvGD,OAAQ,CACRA,OAAOD,gBAAgB,UACvBC,OAAOE,UAAUM,OAAO,gBAGlBJ,MAAQC,SAASC,cAAc,cAAgBT,QAAQU,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 8d87674d5d8a4..209b928930205 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"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;_exports.default=class{notchecked(target){const displayMap=this.form.mapTemplate();return this.getDependantsOfType(target.name,"notchecked").forEach(((dependant,key)=>{Boolean(key)===target.checked?showUnlock(dependant,displayMap):hideLock(dependant,displayMap)})),this.form.displayMapPrune(displayMap)}checked(target){const displayMap=this.form.mapTemplate();return this.getDependantsOfType(target.name,"checked").forEach(((dependant,key)=>{Boolean(key)!==target.checked?showUnlock(dependant,displayMap):hideLock(dependant,displayMap)})),this.form.displayMapPrune(displayMap)}eq(target){const displayMap=this.form.mapTemplate(),radioFieldVal=this.getRadioFieldVal(target);return this.getDependantsOfType(target.name,"eq").forEach(((dependant,key)=>{key===target.value||String(key)===String(null==radioFieldVal?void 0:radioFieldVal.value)?hideLock(dependant,displayMap):showUnlock(dependant,displayMap)})),this.form.displayMapPrune(displayMap)}neq(target){const displayMap=this.form.mapTemplate(),radioFieldVal=this.getRadioFieldVal(target);return this.getDependantsOfType(target.name,"neq").forEach(((dependant,key)=>{key!==target.value||String(key)!==String(null==radioFieldVal?void 0:radioFieldVal.value)?hideLock(dependant,displayMap):showUnlock(dependant,displayMap)})),this.form.displayMapPrune(displayMap)}in(target){const displayMap=this.form.mapTemplate();return this.getDependantsOfType(target.name,"in").forEach(((dependant,key)=>{key.split("|").includes(target.value)?hideLock(dependant,displayMap):showUnlock(dependant,displayMap)})),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):null}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}};const dependencyBehaviour_disable=0,dependencyBehaviour_hide=1,showUnlock=(dependant,displayMap)=>{dependant.hasOwnProperty(dependencyBehaviour_hide)&&displayMap.get("show").push(dependant[dependencyBehaviour_hide]),dependant.hasOwnProperty(dependencyBehaviour_disable)&&displayMap.get("unlock").push(dependant[dependencyBehaviour_disable])},hideLock=(dependant,displayMap)=>{dependant.hasOwnProperty(dependencyBehaviour_hide)&&displayMap.get("hide").push(dependant[dependencyBehaviour_hide]),dependant.hasOwnProperty(dependencyBehaviour_disable)&&displayMap.get("lock").push(dependant[dependencyBehaviour_disable])};return _exports.default}));
+define("core_form/form/rules",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;_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,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,determineDisplayMap(dependant,displayMap,lock)})),this.form.displayMapPrune(displayMap)}eq(target){const displayMap=this.form.mapTemplate(),ctarget=this.getRadioFieldVal(target);let lock=!1;return this.getDependantsOfType(target.name,"eq").forEach(((dependant,key)=>{if(("radio"!==target.type||String(key)===String(ctarget.value))&&"hidden"!==target.type)return"checkbox"!==target.type||target.checksed?void(target.classList.contains("filepickerhidden")?(lock=!M.form_filepicker.instances[target.id].fileadded,determineDisplayMap(dependant,displayMap,lock)):"select"===target.tagName.toLowerCase()&&target.multiple||(lock=target.value===key,determineDisplayMap(dependant,displayMap,lock))):(lock=target.checked===Boolean(key),void 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(),ctarget=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)=>{if(("radio"!==target.type||String(key)===String(ctarget.value))&&"hidden"!==target.type)return"checkbox"!==target.type||target.checked?void(target.classList.contains("filepickerhidden")?(lock=!!M.form_filepicker.instances[target.id].fileadded,determineDisplayMap(dependant,displayMap,lock)):"select"===target.tagName.toLowerCase()&&target.multiple||(lock=target.value!==key,determineDisplayMap(dependant,displayMap,lock))):(lock=target.checked===Boolean(key),void 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),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}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}};const dependencyBehaviour_disable=0,dependencyBehaviour_hide=1,determineDisplayMap=(dependant,displayMap,lock)=>{const hide=!!dependant.hasOwnProperty(dependencyBehaviour_hide)&&lock;dependant.hasOwnProperty(dependencyBehaviour_disable)?(displayMap.get("show").push(dependant[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").push(dependant[dependencyBehaviour_hide]):displayMap.get("show").push(dependant[dependencyBehaviour_hide]))};return _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 30bd4c50314e5..613bc3aae21fc 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\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 // Shorthand helpers rather than requiring devs to export the value and use an eq or neq check.\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\n this.getDependantsOfType(target.name, 'notchecked').forEach((dependant, key) => {\n if (Boolean(key) === target.checked) {\n showUnlock(dependant, displayMap);\n } else {\n hideLock(dependant, displayMap);\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 checked(target) {\n const displayMap = this.form.mapTemplate();\n\n this.getDependantsOfType(target.name, 'checked').forEach((dependant, key) => {\n if (Boolean(key) !== target.checked) {\n showUnlock(dependant, displayMap);\n } else {\n hideLock(dependant, displayMap);\n }\n });\n\n return this.form.displayMapPrune(displayMap);\n }\n\n // Handlers for default rules defined in the links in the JSDoc for this class.\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 radioFieldVal = this.getRadioFieldVal(target);\n\n this.getDependantsOfType(target.name, 'eq').forEach((dependant, key) => {\n if (key === target.value || String(key) === String(radioFieldVal?.value)) {\n hideLock(dependant, displayMap);\n } else {\n showUnlock(dependant, displayMap);\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 radioFieldVal = this.getRadioFieldVal(target);\n\n // TODO: get ne & noteq as well.\n this.getDependantsOfType(target.name, 'neq').forEach((dependant, key) => {\n if (key !== target.value || String(key) !== String(radioFieldVal?.value)) {\n hideLock(dependant, displayMap);\n } else {\n showUnlock(dependant, displayMap);\n }\n });\n\n return this.form.displayMapPrune(displayMap);\n }\n\n // Moodle addons to the rules list.\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\n this.getDependantsOfType(target.name, 'in').forEach((dependant, key) => {\n if (key.split('|').includes(target.value)) {\n hideLock(dependant, displayMap);\n } else {\n showUnlock(dependant, displayMap);\n }\n });\n\n return this.form.displayMapPrune(displayMap);\n }\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|null}\n */\n getRadioFieldVal(target) {\n return target.type === 'radio' ? this.form.form.elements.namedItem(target.name) : null;\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\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, show and unlock the elements that are required.\n *\n * @param {Object} dependant The dependant object that contains the rules for showing and unlocking.\n * @param {Map} displayMap The aggregation of elements that should be shown, hidden, locked, or unlocked.\n */\nconst showUnlock = (dependant, displayMap) => {\n if (dependant.hasOwnProperty(dependencyBehaviour.hide)) {\n displayMap.get('show').push(dependant[dependencyBehaviour.hide]);\n }\n if (dependant.hasOwnProperty(dependencyBehaviour.disable)) {\n displayMap.get('unlock').push(dependant[dependencyBehaviour.disable]);\n }\n};\n\n/**\n * Considering the dependant object, hide and lock the elements that are required.\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 */\nconst hideLock = (dependant, displayMap) => {\n if (dependant.hasOwnProperty(dependencyBehaviour.hide)) {\n displayMap.get('hide').push(dependant[dependencyBehaviour.hide]);\n }\n if (dependant.hasOwnProperty(dependencyBehaviour.disable)) {\n displayMap.get('lock').push(dependant[dependencyBehaviour.disable]);\n }\n};\n"],"names":["notchecked","target","displayMap","this","form","mapTemplate","getDependantsOfType","name","forEach","dependant","key","Boolean","checked","showUnlock","hideLock","displayMapPrune","eq","radioFieldVal","getRadioFieldVal","value","String","neq","in","split","includes","element","type","elements","namedItem","constructor","dependencyBehaviour","hasOwnProperty","get","push"],"mappings":"qKA0CIA,WAAWC,cACDC,WAAaC,KAAKC,KAAKC,0BAExBC,oBAAoBL,OAAOM,KAAM,cAAcC,SAAQ,CAACC,UAAWC,OAChEC,QAAQD,OAAST,OAAOW,QACxBC,WAAWJ,UAAWP,YAEtBY,SAASL,UAAWP,eAIrBC,KAAKC,KAAKW,gBAAgBb,YASrCU,QAAQX,cACEC,WAAaC,KAAKC,KAAKC,0BAExBC,oBAAoBL,OAAOM,KAAM,WAAWC,SAAQ,CAACC,UAAWC,OAC7DC,QAAQD,OAAST,OAAOW,QACxBC,WAAWJ,UAAWP,YAEtBY,SAASL,UAAWP,eAIrBC,KAAKC,KAAKW,gBAAgBb,YAWrCc,GAAGf,cACOC,WAAaC,KAAKC,KAAKC,cACvBY,cAAgBd,KAAKe,iBAAiBjB,oBAEvCK,oBAAoBL,OAAOM,KAAM,MAAMC,SAAQ,CAACC,UAAWC,OACxDA,MAAQT,OAAOkB,OAASC,OAAOV,OAASU,OAAOH,MAAAA,qBAAAA,cAAeE,OAC9DL,SAASL,UAAWP,YAEpBW,WAAWJ,UAAWP,eAIvBC,KAAKC,KAAKW,gBAAgBb,YAUrCmB,IAAIpB,cACMC,WAAaC,KAAKC,KAAKC,cACvBY,cAAgBd,KAAKe,iBAAiBjB,oBAGvCK,oBAAoBL,OAAOM,KAAM,OAAOC,SAAQ,CAACC,UAAWC,OACzDA,MAAQT,OAAOkB,OAASC,OAAOV,OAASU,OAAOH,MAAAA,qBAAAA,cAAeE,OAC9DL,SAASL,UAAWP,YAEpBW,WAAWJ,UAAWP,eAIvBC,KAAKC,KAAKW,gBAAgBb,YAWrCoB,GAAGrB,cACOC,WAAaC,KAAKC,KAAKC,0BAExBC,oBAAoBL,OAAOM,KAAM,MAAMC,SAAQ,CAACC,UAAWC,OACxDA,IAAIa,MAAM,KAAKC,SAASvB,OAAOkB,OAC/BL,SAASL,UAAWP,YAEpBW,WAAWJ,UAAWP,eAIvBC,KAAKC,KAAKW,gBAAgBb,YAUrCI,oBAAoBmB,QAASC,aAClBvB,KAAKC,KAAKE,oBAAoBmB,QAASC,MASlDR,iBAAiBjB,cACU,UAAhBA,OAAOyB,KAAmBvB,KAAKC,KAAKA,KAAKuB,SAASC,UAAU3B,OAAOM,MAAQ,KAOtFsB,YAAYzB,yKACHA,KAAOA,aASd0B,4BACO,EADPA,yBAEI,EASJjB,WAAa,CAACJ,UAAWP,cACvBO,UAAUsB,eAAeD,2BACzB5B,WAAW8B,IAAI,QAAQC,KAAKxB,UAAUqB,2BAEtCrB,UAAUsB,eAAeD,8BACzB5B,WAAW8B,IAAI,UAAUC,KAAKxB,UAAUqB,+BAU1ChB,SAAW,CAACL,UAAWP,cACrBO,UAAUsB,eAAeD,2BACzB5B,WAAW8B,IAAI,QAAQC,KAAKxB,UAAUqB,2BAEtCrB,UAAUsB,eAAeD,8BACzB5B,WAAW8B,IAAI,QAAQC,KAAKxB,UAAUqB"}
\ 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\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 // Shorthand helpers rather than requiring devs to export the value and use an eq or neq check.\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 // Handlers for default rules defined in the links in the JSDoc for this class.\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 ctarget = this.getRadioFieldVal(target);\n let lock = false;\n\n this.getDependantsOfType(target.name, 'eq').forEach((dependant, key) => {\n if (target.type === 'radio' && String(key) !== String(ctarget.value)) {\n return;\n } else if (target.type === 'hidden') {\n // This is the hidden input that is part of an advcheckbox.\n //hiddenVal = target.value === key;\n return;\n } else if (target.type === 'checkbox' && !target.checksed) {\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 } 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 ctarget = 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' && String(key) !== String(ctarget.value)) {\n return;\n } else if (target.type === 'hidden') {\n // This is the hidden input that is part of an advcheckbox.\n //hiddenVal = target.value === key;\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 } else {\n lock = target.value !== key;\n determineDisplayMap(dependant, displayMap, lock);\n }\n });\n\n return this.form.displayMapPrune(displayMap);\n }\n\n // Moodle addons to the rules list.\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 /**\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 * 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\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 */\nconst determineDisplayMap = (dependant, displayMap, lock) => {\n const hide = dependant.hasOwnProperty(dependencyBehaviour.hide) ? lock : false;\n if (dependant.hasOwnProperty(dependencyBehaviour.disable)) {\n displayMap.get('show').push(dependant[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 if (hide) {\n displayMap.get('hide').push(dependant[dependencyBehaviour.hide]);\n } else {\n displayMap.get('show').push(dependant[dependencyBehaviour.hide]);\n }\n }\n};\n"],"names":["notchecked","target","displayMap","this","form","mapTemplate","lock","getDependantsOfType","name","forEach","dependant","key","Boolean","checked","determineDisplayMap","displayMapPrune","eq","ctarget","getRadioFieldVal","type","String","value","checksed","classList","contains","M","form_filepicker","instances","id","fileadded","tagName","toLowerCase","multiple","neq","maps","_this$getDependantsOf2","entries","_this$getDependantsOf4","_this$getDependantsOf6","Map","in","split","includes","element","elements","namedItem","constructor","dependencyBehaviour","hide","hasOwnProperty","get","push"],"mappings":"qKA0CIA,WAAWC,cACDC,WAAaC,KAAKC,KAAKC,kBACzBC,MAAO,cAENC,oBAAoBN,OAAOO,KAAM,cAAcC,SAAQ,CAACC,UAAWC,OACpEL,KAAOM,QAAQD,OAASV,OAAOY,QAC/BC,oBAAoBJ,UAAWR,WAAYI,SAGxCH,KAAKC,KAAKW,gBAAgBb,YASrCW,QAAQZ,cACEC,WAAaC,KAAKC,KAAKC,kBACzBC,MAAO,cAENC,oBAAoBN,OAAOO,KAAM,WAAWC,SAAQ,CAACC,UAAWC,OACjEL,KAAOM,QAAQD,OAASV,OAAOY,QAC/BC,oBAAoBJ,UAAWR,WAAYI,SAGxCH,KAAKC,KAAKW,gBAAgBb,YAWrCc,GAAGf,cACOC,WAAaC,KAAKC,KAAKC,cACvBY,QAAUd,KAAKe,iBAAiBjB,YAClCK,MAAO,cAENC,oBAAoBN,OAAOO,KAAM,MAAMC,SAAQ,CAACC,UAAWC,WACxC,UAAhBV,OAAOkB,MAAoBC,OAAOT,OAASS,OAAOH,QAAQI,SAEnC,WAAhBpB,OAAOkB,KAIX,MAAoB,aAAhBlB,OAAOkB,MAAwBlB,OAAOqB,cAK7CrB,OAAOsB,UAAUC,SAAS,qBAC1BlB,MAAQmB,EAAEC,gBAAgBC,UAAU1B,OAAO2B,IAAIC,UAC/Cf,oBAAoBJ,UAAWR,WAAYI,OACH,WAAjCL,OAAO6B,QAAQC,eAA8B9B,OAAO+B,WAI3D1B,KAAOL,OAAOoB,QAAUV,IACxBG,oBAAoBJ,UAAWR,WAAYI,SAZ3CA,KAAOL,OAAOY,UAAYD,QAAQD,UAClCG,oBAAoBJ,UAAWR,WAAYI,UAe5CH,KAAKC,KAAKW,gBAAgBb,YAUrC+B,IAAIhC,2JACMC,WAAaC,KAAKC,KAAKC,cACvBY,QAAUd,KAAKe,iBAAiBjB,YAClCK,MAAO,QAGL4B,KAAO,iEACN/B,KAAKI,oBAAoBN,OAAOO,KAAM,gDAAtC2B,uBAA8CC,iEAAa,oEAC3DjC,KAAKI,oBAAoBN,OAAOO,KAAM,+CAAtC6B,uBAA6CD,mEAAa,oEAC1DjC,KAAKI,oBAAoBN,OAAOO,KAAM,kDAAtC8B,uBAAgDF,mEAAa,WAE9C,IAAIG,IAAIL,MAChBzB,SAAQ,CAACC,UAAWC,WACV,UAAhBV,OAAOkB,MAAoBC,OAAOT,OAASS,OAAOH,QAAQI,SAEnC,WAAhBpB,OAAOkB,KAIX,MAAoB,aAAhBlB,OAAOkB,MAAwBlB,OAAOY,aAK7CZ,OAAOsB,UAAUC,SAAS,qBAC1BlB,OAASmB,EAAEC,gBAAgBC,UAAU1B,OAAO2B,IAAIC,UAChDf,oBAAoBJ,UAAWR,WAAYI,OACH,WAAjCL,OAAO6B,QAAQC,eAA8B9B,OAAO+B,WAI3D1B,KAAOL,OAAOoB,QAAUV,IACxBG,oBAAoBJ,UAAWR,WAAYI,SAZ3CA,KAAOL,OAAOY,UAAYD,QAAQD,UAClCG,oBAAoBJ,UAAWR,WAAYI,UAe5CH,KAAKC,KAAKW,gBAAgBb,YAWrCsC,GAAGvC,cACOC,WAAaC,KAAKC,KAAKC,kBACzBC,MAAO,cAENC,oBAAoBN,OAAOO,KAAM,MAAMC,SAAQ,CAACC,UAAWC,OAC5DL,KAAOK,IAAI8B,MAAM,KAAKC,SAASzC,OAAOoB,OACtCP,oBAAoBJ,UAAWR,WAAYI,SAGxCH,KAAKC,KAAKW,gBAAgBb,YAUrCK,oBAAoBoC,QAASxB,aAClBhB,KAAKC,KAAKG,oBAAoBoC,QAASxB,MASlDD,iBAAiBjB,cACU,UAAhBA,OAAOkB,KAAmBhB,KAAKC,KAAKA,KAAKwC,SAASC,UAAU5C,OAAOO,MAAQP,OAOtF6C,YAAY1C,yKACHA,KAAOA,aASd2C,4BACO,EADPA,yBAEI,EAUJjC,oBAAsB,CAACJ,UAAWR,WAAYI,cAC1C0C,OAAOtC,UAAUuC,eAAeF,2BAA4BzC,KAC9DI,UAAUuC,eAAeF,8BACzB7C,WAAWgD,IAAI,QAAQC,KAAKzC,UAAUqC,8BAClCzC,KACAJ,WAAWgD,IAAI,QAAQC,KAAKzC,UAAUqC,8BAEtC7C,WAAWgD,IAAI,UAAUC,KAAKzC,UAAUqC,+BAErCrC,UAAUuC,eAAeF,4BAC5BC,KACA9C,WAAWgD,IAAI,QAAQC,KAAKzC,UAAUqC,2BAEtC7C,WAAWgD,IAAI,QAAQC,KAAKzC,UAAUqC"}
\ No newline at end of file
diff --git a/lib/form/amd/src/form.js b/lib/form/amd/src/form.js
index 74d716ac265a8..5d6d7f04344ce 100644
--- a/lib/form/amd/src/form.js
+++ b/lib/form/amd/src/form.js
@@ -30,7 +30,6 @@
// Pure form functionality.
// import {serialize} from './util';
import * as FormChangeChecker from './changechecker';
-import * as FormEvents from './events';
import * as Submit from './submit';
import Rules from './form/rules';
import * as MutateDom from './form/dom';
@@ -70,10 +69,23 @@ export default class Form {
this.dependencies = this.getDependencyMapper(dependencies);
this.rules = new Rules(this);
+ // Apply the initial state of the form.
+ this.applyInitialState();
+
// Handle mutations within the form.
this.registerEventListeners();
FormChangeChecker.watchForm(this.form);
- // TODO: Apply the rules to the form on load.
+ }
+
+ /**
+ * Given the page has loaded, apply the initial state of the form.
+ */
+ applyInitialState() {
+ [...this.form.elements].forEach((element) => {
+ if (this.dependencies.has(element.name)) {
+ this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(element)));
+ }
+ });
}
/**
@@ -82,20 +94,17 @@ export default class Form {
registerEventListeners() {
this.form.addEventListener('change', (e) => {
if (e.target.type === 'submit') {
- // TODO: Dummy calls so import list looks better...
- // Notify listeners that the form is about to be submitted.
- FormEvents.notifyFormSubmittedByJavascript(this.form);
FormChangeChecker.resetFormDirtyState(this.form);
Submit.init(e.target.id);
}
if (e.target.type === 'reset') {
+ FormChangeChecker.resetFormDirtyState(this.form);
this.form.reset();
}
// Something changes based on this element.
if (this.dependencies.has(e.target.name)) {
- const displayMap = this.dispatchDependencyRules(e.target);
- const reducedDisplayMap = this.displayMapPrune(displayMap);
- this.domDispatch(reducedDisplayMap);
+ FormChangeChecker.markFormChangedFromNode(e.target);
+ this.domDispatch(this.displayMapPrune(this.dispatchDependencyRules(e.target)));
}
});
}
@@ -128,14 +137,11 @@ export default class Form {
* @returns {Map|Map<>} The pruned map or map even a fully pruned map if noting has to change.
*/
displayMapPrune(displayMap) {
- window.console.log('preprune:', displayMap);
- // Filter any locked items that are to be hidden anyway.
- const removelockifhidden = displayMap.get('lock').filter(x => displayMap.get('hide').includes(x));
- window.console.log('removelockifhidden:', removelockifhidden);
- displayMap.set('lock', removelockifhidden);
// Filter any unlocked items that pegged to be hidden as they must be locked if they are hidden.
- //const removeunlockifhidden = displayMap.get('unlock').filter(x => displayMap.get('hide').includes(x));
- //displayMap.set('unlock', removeunlockifhidden);
+ const removeunlockifhidden = displayMap.get('unlock').filter(x => {
+ return !displayMap.get('hide').toString().includes(x.toString());
+ });
+ displayMap.set('unlock', removeunlockifhidden);
// Remove any empty entries.
for (const [key, value] of displayMap) {
@@ -163,7 +169,6 @@ export default class Form {
* @param {Map} elNamesMap What needs to change.
*/
domDispatch(elNamesMap) {
- window.console.log('elNamesMap', elNamesMap);
elNamesMap.forEach((elements, domUpdateOpt) => {
if (!MutateDom[domUpdateOpt]) {
return;
@@ -181,12 +186,15 @@ export default class Form {
/**
* Convert the element names into DOM nodes based on the element names.
*
- * @param {Array} elementNames The name of dependent elements to get associated DOM nodes.
+ * @param {Array} elementNames The name of dependent elements to get associated DOM nodes.
* @returns {Array}
*/
elementNamesToDomNodes(elementNames) {
return elementNames.map((element) => {
- return this.form.elements.namedItem(element);
+ // TODO: Rename and document why we need to deep look if there are multiple nodes effected by the rule.
+ return element.map((element2) => {
+ return this.form.elements.namedItem(element2);
+ });
});
}
@@ -252,7 +260,21 @@ export default class Form {
*/
const ruleMap = new Map(Object.entries(elementrules));
ruleMap.forEach((ruleComparisons, key) => {
- ruleMap.set(key, new Map(Object.entries(ruleComparisons)));
+ /**
+ * Convert any disabledIf rules into objects, so we can manage them the same as hideIf items.
+ *
+ * @type {Map} The map of comparison values t.
+ * @example "none" => "none" => Object
+ * @example "neq" => "point" => Object
+ */
+ const hideDefine = new Map(Object.entries(ruleComparisons));
+ hideDefine.forEach((action, compVal) => {
+ if (Array.isArray(action)) {
+ action = {...action};
+ }
+ hideDefine.set(compVal, action);
+ });
+ ruleMap.set(key, hideDefine);
});
elementMap.set(key, ruleMap);
});
diff --git a/lib/form/amd/src/form/dom.js b/lib/form/amd/src/form/dom.js
index 8050af73632fe..5606a635d8124 100644
--- a/lib/form/amd/src/form/dom.js
+++ b/lib/form/amd/src/form/dom.js
@@ -23,71 +23,78 @@
"use strict";
-/**
- * TODO: Considerations requiring action:
- *
- * Things old YUI looked for after grabbing the element DOM ancestor (Label management):
- * Classes: fitem fitem_fgroup
- * Data attributes: label[for="' + id + '"]
- * Selectors from old YUI:
- * .fitem [data-fieldtype="filepicker"] input,
- * .fitem [data-fieldtype="filemanager"] input,
- * .fitem [data-fieldtype="group"] input[id*="filemanager"]
- *
- * Guessed selectors:
- * [data-groupname="${element}"]
- *
- * Other things to consider:
- * Special handling for editors & filepickers i.e. Name suffix on editors -> See old YUI?
- * element.setAttribute('aria-disabled', >);
- * element.setAttribute('readonly', 'readonly');
- */
-
/**
* Disable an element.
*
- * @param {HTMLElement} element The element to be disabled.
+ * @param {Array} elements The element to be disabled.
*/
-// eslint-disable-next-line no-unused-vars
-export const lock = (element) => {
- // window.console.log('Lock node:', element);
- // element.disabled = true;
- // element.classList.add('disabled');
+export const lock = (elements) => {
+ elements.forEach(element => {
+ if (element !== null) {
+ element.setAttribute('disabled', 'disabled');
+ }
+ });
};
/**
* Enable an element.
*
- * @param {HTMLElement} element The element to be enabled.
+ * @param {Array} elements The element to be enabled.
*/
-export const unlock = (element) => {
- show(element);
- // window.console.log('Unlock node:', element);
- // element.disabled = false;
- // element.classList.remove('disabled');
+export const unlock = (elements) => {
+ elements.forEach(element => {
+ if (element !== null) {
+ element.removeAttribute('disabled');
+ }
+ });
};
/**
* Hide an element.
*
- * @param {HTMLElement} element The element to be hidden.
+ * @param {Array} elements The elements to be hidden.
*/
-export const hide = (element) => {
- // Lock the element to prevent interaction.
- lock(element);
- // window.console.log('Hide node:', element);
- // element.hidden = true;
- // element.classList.add('hidden');
+export const hide = (elements) => {
+ elements.forEach(element => {
+ if (element !== null) {
+ // Check if the element is part of a group otherwise, just grab the nearest wrapping element.
+ const parent = element.closest('[data-groupname]') ? element.closest('[data-groupname]') : element.closest('.fitem');
+ if (parent) {
+ parent.setAttribute('hidden', 'hidden');
+ parent.classList.add('d-none');
+
+ // Hide the label as well.
+ const label = document.querySelector('label[for="' + element.id + '"]');
+ if (label) {
+ label.setAttribute('hidden', 'hidden');
+ label.classList.add('d-none');
+ }
+ }
+ }
+ });
};
/**
* Show an element.
*
- * @param {HTMLElement} element The element to be shown.
+ * @param {Array} elements The elements to be shown.
*/
-// eslint-disable-next-line no-unused-vars
-export const show = (element) => {
- //window.console.log('Show node:', element);
- // element.hidden = false;
- // element.classList.remove('hidden');
+export const show = (elements) => {
+ elements.forEach(element => {
+ if (element !== null) {
+ // Check if the element is part of a group otherwise, just grab the nearest wrapping element.
+ const parent = element.closest('[data-groupname]') ? element.closest('[data-groupname]') : element.closest('.fitem');
+ if (parent) {
+ parent.removeAttribute('hidden');
+ parent.classList.remove('d-none');
+
+ // Show the label as well.
+ const label = document.querySelector('label[for="' + element.id + '"]');
+ if (label) {
+ label.removeAttribute('hidden');
+ label.classList.remove('d-none');
+ }
+ }
+ }
+ });
};
diff --git a/lib/form/amd/src/form/rules.js b/lib/form/amd/src/form/rules.js
index a7d746187bb7e..1317725007959 100644
--- a/lib/form/amd/src/form/rules.js
+++ b/lib/form/amd/src/form/rules.js
@@ -42,13 +42,11 @@ export default class Rules {
*/
notchecked(target) {
const displayMap = this.form.mapTemplate();
+ let lock = false;
this.getDependantsOfType(target.name, 'notchecked').forEach((dependant, key) => {
- if (Boolean(key) === target.checked) {
- showUnlock(dependant, displayMap);
- } else {
- hideLock(dependant, displayMap);
- }
+ lock = Boolean(key) !== target.checked;
+ determineDisplayMap(dependant, displayMap, lock);
});
return this.form.displayMapPrune(displayMap);
@@ -62,13 +60,11 @@ export default class Rules {
*/
checked(target) {
const displayMap = this.form.mapTemplate();
+ let lock = false;
this.getDependantsOfType(target.name, 'checked').forEach((dependant, key) => {
- if (Boolean(key) !== target.checked) {
- showUnlock(dependant, displayMap);
- } else {
- hideLock(dependant, displayMap);
- }
+ lock = Boolean(key) === target.checked;
+ determineDisplayMap(dependant, displayMap, lock);
});
return this.form.displayMapPrune(displayMap);
@@ -84,13 +80,30 @@ export default class Rules {
*/
eq(target) {
const displayMap = this.form.mapTemplate();
- const radioFieldVal = this.getRadioFieldVal(target);
+ const ctarget = this.getRadioFieldVal(target);
+ let lock = false;
this.getDependantsOfType(target.name, 'eq').forEach((dependant, key) => {
- if (key === target.value || String(key) === String(radioFieldVal?.value)) {
- hideLock(dependant, displayMap);
+ if (target.type === 'radio' && String(key) !== String(ctarget.value)) {
+ return;
+ } else if (target.type === 'hidden') {
+ // This is the hidden input that is part of an advcheckbox.
+ //hiddenVal = target.value === key;
+ return;
+ } else if (target.type === 'checkbox' && !target.checksed) {
+ lock = target.checked === Boolean(key);
+ determineDisplayMap(dependant, displayMap, lock);
+ return;
+ }
+ if (target.classList.contains('filepickerhidden')) {
+ lock = !M.form_filepicker.instances[target.id].fileadded;
+ 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.
} else {
- showUnlock(dependant, displayMap);
+ lock = target.value === key;
+ determineDisplayMap(dependant, displayMap, lock);
}
});
@@ -106,14 +119,37 @@ export default class Rules {
*/
neq(target) {
const displayMap = this.form.mapTemplate();
- const radioFieldVal = this.getRadioFieldVal(target);
-
- // TODO: get ne & noteq as well.
- this.getDependantsOfType(target.name, 'neq').forEach((dependant, key) => {
- if (key !== target.value || String(key) !== String(radioFieldVal?.value)) {
- hideLock(dependant, displayMap);
+ const ctarget = this.getRadioFieldVal(target);
+ let lock = false;
+
+ // Get all the aliases of neq and check them all at once.
+ const maps = [
+ ...this.getDependantsOfType(target.name, 'neq')?.entries() ?? [],
+ ...this.getDependantsOfType(target.name, 'ne')?.entries() ?? [],
+ ...this.getDependantsOfType(target.name, 'noteq')?.entries() ?? [],
+ ];
+ const condensedMaps = new Map(maps);
+ condensedMaps.forEach((dependant, key) => {
+ if (target.type === 'radio' && String(key) !== String(ctarget.value)) {
+ return;
+ } else if (target.type === 'hidden') {
+ // This is the hidden input that is part of an advcheckbox.
+ //hiddenVal = target.value === key;
+ return;
+ } else if (target.type === 'checkbox' && !target.checked) {
+ lock = target.checked === Boolean(key);
+ determineDisplayMap(dependant, displayMap, lock);
+ return;
+ }
+ if (target.classList.contains('filepickerhidden')) {
+ lock = !!M.form_filepicker.instances[target.id].fileadded;
+ 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.
} else {
- showUnlock(dependant, displayMap);
+ lock = target.value !== key;
+ determineDisplayMap(dependant, displayMap, lock);
}
});
@@ -130,13 +166,11 @@ export default class Rules {
*/
in(target) {
const displayMap = this.form.mapTemplate();
+ let lock = false;
this.getDependantsOfType(target.name, 'in').forEach((dependant, key) => {
- if (key.split('|').includes(target.value)) {
- hideLock(dependant, displayMap);
- } else {
- showUnlock(dependant, displayMap);
- }
+ lock = key.split('|').includes(target.value);
+ determineDisplayMap(dependant, displayMap, lock);
});
return this.form.displayMapPrune(displayMap);
@@ -157,10 +191,10 @@ export default class Rules {
* Radio fields are a bit different, they need to be handled differently.
*
* @param {HTMLFormElement} target The changed DOM node to find a potential radio field for.
- * @returns {RadioNodeList|null}
+ * @returns {RadioNodeList|HTMLFormElement}
*/
getRadioFieldVal(target) {
- return target.type === 'radio' ? this.form.form.elements.namedItem(target.name) : null;
+ return target.type === 'radio' ? this.form.form.elements.namedItem(target.name) : target;
}
/**
@@ -183,31 +217,26 @@ const dependencyBehaviour = {
};
/**
- * Considering the dependant object, show and unlock the elements that are required.
- *
- * @param {Object} dependant The dependant object that contains the rules for showing and unlocking.
- * @param {Map} displayMap The aggregation of elements that should be shown, hidden, locked, or unlocked.
- */
-const showUnlock = (dependant, displayMap) => {
- if (dependant.hasOwnProperty(dependencyBehaviour.hide)) {
- displayMap.get('show').push(dependant[dependencyBehaviour.hide]);
- }
- if (dependant.hasOwnProperty(dependencyBehaviour.disable)) {
- displayMap.get('unlock').push(dependant[dependencyBehaviour.disable]);
- }
-};
-
-/**
- * Considering the dependant object, hide and lock the elements that are required.
+ * 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.
*/
-const hideLock = (dependant, displayMap) => {
- if (dependant.hasOwnProperty(dependencyBehaviour.hide)) {
- displayMap.get('hide').push(dependant[dependencyBehaviour.hide]);
- }
+const determineDisplayMap = (dependant, displayMap, lock) => {
+ const hide = dependant.hasOwnProperty(dependencyBehaviour.hide) ? lock : false;
if (dependant.hasOwnProperty(dependencyBehaviour.disable)) {
- displayMap.get('lock').push(dependant[dependencyBehaviour.disable]);
+ displayMap.get('show').push(dependant[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)) {
+ if (hide) {
+ displayMap.get('hide').push(dependant[dependencyBehaviour.hide]);
+ } else {
+ displayMap.get('show').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 0099cc98ce62f..f672fec85df2b 100644
--- a/lib/form/tests/behat/fixtures/form_rules.php
+++ b/lib/form/tests/behat/fixtures/form_rules.php
@@ -65,18 +65,23 @@ public function definition() {
$mform->hideIf('r_neq_btn', 'rgt', 'neq', '3');
// Checkboxes checked/notchecked rule test.
- $mform->addElement('header', 'checkboxheader', 'Checkboxes: checked/notchecked');
+ $mform->addElement('header', 'checkboxheader', 'Checkboxes: checked/notchecked/eq/neq');
$mform->setExpanded('checkboxheader');
$ckb = [
$mform->createElement('advcheckbox', 'hidden_ckb', 'Checked hide', ''),
$mform->createElement('advcheckbox', 'disabled_ckb', 'Checked disable', ''),
$mform->createElement('advcheckbox', 'hidden_uncheck_ckb', 'Not checked hide', ''),
$mform->createElement('advcheckbox', 'disabled_uncheck_ckb', 'Not checked disable', ''),
+ $mform->createElement('advcheckbox', 'eq_ckb', 'EQ Checked', ''),
+ $mform->createElement('advcheckbox', 'neq_ckb', 'NEQ Checked', ''),
];
- $mform->addGroup($ckb, 'checkbox_test_group', 'Checked/Not checked', ' ', false);
+ $mform->addGroup($ckb, 'checkbox_test_group', 'Checked/Not checked/EQ/NEQ', ' ', false);
$mform->addElement('button', 'checked_hidden_btn', 'Checked hidden');
$mform->hideIf('checked_hidden_btn', 'hidden_ckb', 'checked');
+ $mform->addElement('text', 'checked_hidden_txt', 'Text input');
+ $mform->setType('checked_hidden_txt', PARAM_TEXT);
+ $mform->hideIf('checked_hidden_txt', 'hidden_ckb', 'checked');
$mform->addElement('button', 'checked_disabled_btn', 'Checked disabled');
$mform->disabledIf('checked_disabled_btn', 'disabled_ckb', 'checked');
@@ -87,6 +92,28 @@ public function definition() {
$mform->addElement('button', 'unchecked_disabled_btn', 'Not checked disabled');
$mform->disabledIf('unchecked_disabled_btn', 'disabled_uncheck_ckb', 'notchecked');
+ $mform->addElement('button', 'eq_ckb_btn', 'EQ ckb 1 disabled');
+ $mform->disabledIf('eq_ckb_btn', 'eq_ckb', 'eq' , '1');
+
+ $mform->addElement('button', 'neq_ckb_btn', 'NEQ ckb 0 hidden');
+ $mform->hideIf('neq_ckb_btn', 'neq_ckb', 'neq' , '0');
+
+ // Select test.
+ $mform->addElement('header', 'selectheader', 'Select: eq/neq');
+ $mform->setExpanded('selectheader');
+ $mform->addElement('select', 'sct_int', 'Select',
+ [0 => 'Enable', 1 => 'Disable', 2 => 'Hide'],
+ /*['multiple' => true]*/
+ );
+
+ $mform->addElement('button', 'sct_eq_btn', 'Select EQ');
+ $mform->disabledIf('sct_eq_btn', 'sct_int', 'eq', 1);
+ $mform->hideIf('sct_eq_btn', 'sct_int', 'eq', 2);
+
+ $mform->addElement('button', 'sct_neq_btn', 'Select NEQ');
+ $mform->disabledIf('sct_neq_btn', 'sct_int', 'neq', 1);
+ $mform->hideIf('sct_neq_btn', 'sct_int', 'neq', 0);
+
// Text alpha input rule test.
$mform->addElement('header', 'textalphaheader', 'Text alpha: eq/neq/in');
$mform->setExpanded('textalphaheader');
@@ -130,8 +157,10 @@ public function definition() {
// Date selector rule test.
$mform->addElement('header', 'dateselectorheader', 'Date selector: ~');
- //$mform->setExpanded('dateselectorheader');
+ $mform->setExpanded('dateselectorheader');
+ $mform->addElement('checkbox', 'ds_enb', get_string('enable'));
$mform->addElement('date_selector', 'ds', 'Date selector');
+ $mform->hideIf("ds", 'ds_enb');
// Editor rule test.
$mform->addElement('header', 'editorheader', 'Editor: ~');
@@ -151,7 +180,7 @@ public function definition() {
// Filepicker rule test.
$mform->addElement('header', 'filepickerheader', 'Filepicker: ~');
//$mform->setExpanded('filepickerheader');
- $mform->addElement('filepicker', 'modelfile', get_string('file'), null, ['accepted_types' => '.zip']);
+ $mform->addElement('filepicker', 'modelfile', get_string('file'), null, ['accepted_types' => '*']);
$this->add_action_buttons(false, 'Send form');
}
diff --git a/lib/formslib.php b/lib/formslib.php
index b5314c17c8e6f..f3ba9e2069a18 100644
--- a/lib/formslib.php
+++ b/lib/formslib.php
@@ -3526,7 +3526,7 @@ function finishForm(&$form){
if (!$form->isFrozen()) {
$args = $form->getLockOptionObject();
if (count($args[1]) > 0) {
- $PAGE->requires->js_init_call('M.form.initFormDependencies', $args, true, moodleform::get_js_module());
+ //$PAGE->requires->js_init_call('M.form.initFormDependencies', $args, true, moodleform::get_js_module());
$PAGE->requires->js_call_amd('core_form/form', 'init', $args);
}
}