From f5339787094dfb15d4a91a2a9a0faeed1f0ca690 Mon Sep 17 00:00:00 2001 From: Marina Glancy Date: Tue, 25 May 2021 12:13:01 +0200 Subject: [PATCH] MDL-71686 forms: allow arrays in dynamic forms args --- lib/form/amd/build/dynamicform.min.js | 2 +- lib/form/amd/build/dynamicform.min.js.map | 2 +- lib/form/amd/build/modalform.min.js | 2 +- lib/form/amd/build/modalform.min.js.map | 2 +- lib/form/amd/src/dynamicform.js | 10 ++++++++-- lib/form/amd/src/modalform.js | 11 +++++++++-- 6 files changed, 21 insertions(+), 8 deletions(-) diff --git a/lib/form/amd/build/dynamicform.min.js b/lib/form/amd/build/dynamicform.min.js index 5d35bf42309..bcf1a38c993 100644 --- a/lib/form/amd/build/dynamicform.min.js +++ b/lib/form/amd/build/dynamicform.min.js @@ -1,3 +1,3 @@ -define("core_form/dynamicform",["exports","core/ajax","core/notification","core/templates","core/event","core/str","core/yui","core/fragment","core/pending"],(function(_exports,_ajax,_notification,_templates,_event2,_str,_yui,_fragment,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}function _toConsumableArray(arr){return function(arr){if(Array.isArray(arr))return _arrayLikeToArray(arr)}(arr)||function(iter){if("undefined"!=typeof Symbol&&null!=iter[Symbol.iterator]||null!=iter["@@iterator"])return Array.from(iter)}(arr)||function(o,minLen){if(!o)return;if("string"==typeof o)return _arrayLikeToArray(o,minLen);var n=Object.prototype.toString.call(o).slice(8,-1);"Object"===n&&o.constructor&&(n=o.constructor.name);if("Map"===n||"Set"===n)return Array.from(o);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return _arrayLikeToArray(o,minLen)}(arr)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function _arrayLikeToArray(arr,len){(null==len||len>arr.length)&&(len=arr.length);for(var i=0,arr2=new Array(len);i0&&void 0!==arguments[0]?arguments[0]:null,formData=new URLSearchParams(Object.entries(args||{})),pendingPromise=new _pending.default("core_form/dynamicform:load");return this.getBody(formData.toString()).then((function(resp){return _this2.updateForm(resp)})).then(pendingPromise.resolve)}},{key:"trigger",value:function(eventName){var detail=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,cancelable=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],e=new CustomEvent(eventName,{detail:detail,cancelable:cancelable});return this.container.dispatchEvent(e),e}},{key:"addEventListener",value:function(){var _this$container;(_this$container=this.container).addEventListener.apply(_this$container,arguments)}},{key:"getBody",value:function(formDataString){return _ajax.default.call([{methodname:"core_form_dynamic_form",args:{formdata:formDataString,form:this.formClass}}])[0].then((function(response){return{html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)}}))}},{key:"onSubmitSuccess",value:function(response){this.trigger(this.events.FORM_SUBMITTED,response).defaultPrevented||(this.container.innerHTML="")}},{key:"onSubmitError",value:function(exception){this.trigger(this.events.ERROR,exception).defaultPrevented||_notification.default.exception(exception)}},{key:"processNoSubmitButton",value:function(button){var _this3=this,pendingPromise=new _pending.default("core_form/dynamicform:nosubmit"),form=this.container.querySelector("form"),formData=new URLSearchParams(_toConsumableArray(new FormData(form).entries()));formData.append(button.getAttribute("name"),button.getAttribute("value")),this.notifyFormSubmitAjax(!0).then((function(){return _this3.disableButtons(),_this3.getBody(formData.toString())})).then((function(resp){return _this3.updateForm(resp)})).finally(pendingPromise.resolve).catch((function(exception){return _this3.onSubmitError(exception)}))}},{key:"notifyFormSubmitAjax",value:function(){var skipValidation=arguments.length>0&&void 0!==arguments[0]&&arguments[0],form=this.container.querySelector("form");return new Promise((function(resolve){_yui.default.use("event","moodle-core-event","moodle-core-formchangechecker",(function(){_event2.default.notifyFormSubmitAjax(form,skipValidation),resolve()}))}))}},{key:"notifyResetFormChanges",value:function(){var form=this.container.querySelector("form");return new Promise((function(resolve){_yui.default.use("event","moodle-core-event","moodle-core-formchangechecker",(function(){_event2.default.notifyFormSubmitAjax(form,!0),M.core_formchangechecker.reset_form_dirty_state(),resolve()}))}))}},{key:"processCancelButton",value:function(){var _this4=this;this.notifyResetFormChanges().then((function(){return _this4.trigger(_this4.events.FORM_CANCELLED).defaultPrevented||(_this4.container.innerHTML=""),null})).catch(null)}},{key:"updateForm",value:function(_ref){var html=_ref.html,js=_ref.js;_templates.default.replaceNodeContents(this.container,html,js)}},{key:"validateElements",value:function(){var _this5=this;return this.notifyFormSubmitAjax().then((function(){var invalid=_toConsumableArray(_this5.container.querySelectorAll('[aria-invalid="true"], .error'));return!invalid.length||(invalid[0].focus(),!1)}))}},{key:"disableButtons",value:function(){this.container.querySelectorAll('form input[type="submit"]').forEach((function(el){return el.setAttribute("disabled",!0)}))}},{key:"enableButtons",value:function(){this.container.querySelectorAll('form input[type="submit"]').forEach((function(el){return el.removeAttribute("disabled")}))}},{key:"submitFormAjax",value:(fn=regeneratorRuntime.mark((function _callee(){var form,formData,_this6=this;return regeneratorRuntime.wrap((function(_context){for(;;)switch(_context.prev=_context.next){case 0:return _context.next=2,this.validateElements();case 2:if(_context.sent){_context.next=5;break}return this.trigger(this.events.CLIENT_VALIDATION_ERROR,null,!1),_context.abrupt("return");case 5:this.disableButtons(),form=this.container.querySelector("form"),formData=new URLSearchParams(_toConsumableArray(new FormData(form).entries())),_ajax.default.call([{methodname:"core_form_dynamic_form",args:{formdata:formData.toString(),form:this.formClass}}])[0].then((function(response){if(response.submitted){var data=JSON.parse(response.data);_this6.enableButtons(),_this6.notifyResetFormChanges().then((function(){return _this6.onSubmitSuccess(data)})).catch()}else _this6.updateForm({html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)}),_this6.enableButtons(),_this6.trigger(_this6.events.SERVER_VALIDATION_ERROR,null,!1);return null})).catch((function(exception){return _this6.onSubmitError(exception)}));case 9:case"end":return _context.stop()}}),_callee,this)})),_submitFormAjax=function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))},function(){return _submitFormAjax.apply(this,arguments)})}],protoProps&&_defineProperties(Constructor.prototype,protoProps),staticProps&&_defineProperties(Constructor,staticProps),Object.defineProperty(Constructor,"prototype",{writable:!1}),DynamicForm}();return _exports.default=DynamicForm,_exports.default})); +define("core_form/dynamicform",["exports","core/ajax","core/notification","core/templates","core/event","core/str","core/yui","core/fragment","core/pending"],(function(_exports,_ajax,_notification,_templates,_event2,_str,_yui,_fragment,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}function _typeof(obj){return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(obj){return typeof obj}:function(obj){return obj&&"function"==typeof Symbol&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj},_typeof(obj)}function _slicedToArray(arr,i){return function(arr){if(Array.isArray(arr))return arr}(arr)||function(arr,i){var _i=null==arr?null:"undefined"!=typeof Symbol&&arr[Symbol.iterator]||arr["@@iterator"];if(null==_i)return;var _s,_e,_arr=[],_n=!0,_d=!1;try{for(_i=_i.call(arr);!(_n=(_s=_i.next()).done)&&(_arr.push(_s.value),!i||_arr.length!==i);_n=!0);}catch(err){_d=!0,_e=err}finally{try{_n||null==_i.return||_i.return()}finally{if(_d)throw _e}}return _arr}(arr,i)||_unsupportedIterableToArray(arr,i)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function _toConsumableArray(arr){return function(arr){if(Array.isArray(arr))return _arrayLikeToArray(arr)}(arr)||function(iter){if("undefined"!=typeof Symbol&&null!=iter[Symbol.iterator]||null!=iter["@@iterator"])return Array.from(iter)}(arr)||_unsupportedIterableToArray(arr)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function _unsupportedIterableToArray(o,minLen){if(o){if("string"==typeof o)return _arrayLikeToArray(o,minLen);var n=Object.prototype.toString.call(o).slice(8,-1);return"Object"===n&&o.constructor&&(n=o.constructor.name),"Map"===n||"Set"===n?Array.from(o):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?_arrayLikeToArray(o,minLen):void 0}}function _arrayLikeToArray(arr,len){(null==len||len>arr.length)&&(len=arr.length);for(var i=0,arr2=new Array(len);i0&&void 0!==arguments[0]?arguments[0]:null,serialize=function serialize(obj){var _ref,prefix=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return(_ref=[]).concat.apply(_ref,_toConsumableArray(Object.entries(obj).map((function(_ref2){var _ref3=_slicedToArray(_ref2,2),idx=_ref3[0],v=_ref3[1],k=prefix?prefix+"["+idx+"]":idx;return"object"===_typeof(v)?serialize(v,k):"".concat(k,"=").concat(encodeURIComponent(v))})))).join("&")},formData=serialize(args||{}),pendingPromise=new _pending.default("core_form/dynamicform:load");return this.getBody(formData).then((function(resp){return _this2.updateForm(resp)})).then(pendingPromise.resolve)}},{key:"trigger",value:function(eventName){var detail=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,cancelable=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],e=new CustomEvent(eventName,{detail:detail,cancelable:cancelable});return this.container.dispatchEvent(e),e}},{key:"addEventListener",value:function(){var _this$container;(_this$container=this.container).addEventListener.apply(_this$container,arguments)}},{key:"getBody",value:function(formDataString){return _ajax.default.call([{methodname:"core_form_dynamic_form",args:{formdata:formDataString,form:this.formClass}}])[0].then((function(response){return{html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)}}))}},{key:"onSubmitSuccess",value:function(response){this.trigger(this.events.FORM_SUBMITTED,response).defaultPrevented||(this.container.innerHTML="")}},{key:"onSubmitError",value:function(exception){this.trigger(this.events.ERROR,exception).defaultPrevented||_notification.default.exception(exception)}},{key:"processNoSubmitButton",value:function(button){var _this3=this,pendingPromise=new _pending.default("core_form/dynamicform:nosubmit"),form=this.container.querySelector("form"),formData=new URLSearchParams(_toConsumableArray(new FormData(form).entries()));formData.append(button.getAttribute("name"),button.getAttribute("value")),this.notifyFormSubmitAjax(!0).then((function(){return _this3.disableButtons(),_this3.getBody(formData.toString())})).then((function(resp){return _this3.updateForm(resp)})).finally(pendingPromise.resolve).catch((function(exception){return _this3.onSubmitError(exception)}))}},{key:"notifyFormSubmitAjax",value:function(){var skipValidation=arguments.length>0&&void 0!==arguments[0]&&arguments[0],form=this.container.querySelector("form");return new Promise((function(resolve){_yui.default.use("event","moodle-core-event","moodle-core-formchangechecker",(function(){_event2.default.notifyFormSubmitAjax(form,skipValidation),resolve()}))}))}},{key:"notifyResetFormChanges",value:function(){var form=this.container.querySelector("form");return new Promise((function(resolve){_yui.default.use("event","moodle-core-event","moodle-core-formchangechecker",(function(){_event2.default.notifyFormSubmitAjax(form,!0),M.core_formchangechecker.reset_form_dirty_state(),resolve()}))}))}},{key:"processCancelButton",value:function(){var _this4=this;this.notifyResetFormChanges().then((function(){return _this4.trigger(_this4.events.FORM_CANCELLED).defaultPrevented||(_this4.container.innerHTML=""),null})).catch(null)}},{key:"updateForm",value:function(_ref4){var html=_ref4.html,js=_ref4.js;_templates.default.replaceNodeContents(this.container,html,js)}},{key:"validateElements",value:function(){var _this5=this;return this.notifyFormSubmitAjax().then((function(){var invalid=_toConsumableArray(_this5.container.querySelectorAll('[aria-invalid="true"], .error'));return!invalid.length||(invalid[0].focus(),!1)}))}},{key:"disableButtons",value:function(){this.container.querySelectorAll('form input[type="submit"]').forEach((function(el){return el.setAttribute("disabled",!0)}))}},{key:"enableButtons",value:function(){this.container.querySelectorAll('form input[type="submit"]').forEach((function(el){return el.removeAttribute("disabled")}))}},{key:"submitFormAjax",value:(fn=regeneratorRuntime.mark((function _callee(){var form,formData,_this6=this;return regeneratorRuntime.wrap((function(_context){for(;;)switch(_context.prev=_context.next){case 0:return _context.next=2,this.validateElements();case 2:if(_context.sent){_context.next=5;break}return this.trigger(this.events.CLIENT_VALIDATION_ERROR,null,!1),_context.abrupt("return");case 5:this.disableButtons(),form=this.container.querySelector("form"),formData=new URLSearchParams(_toConsumableArray(new FormData(form).entries())),_ajax.default.call([{methodname:"core_form_dynamic_form",args:{formdata:formData.toString(),form:this.formClass}}])[0].then((function(response){if(response.submitted){var data=JSON.parse(response.data);_this6.enableButtons(),_this6.notifyResetFormChanges().then((function(){return _this6.onSubmitSuccess(data)})).catch()}else _this6.updateForm({html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)}),_this6.enableButtons(),_this6.trigger(_this6.events.SERVER_VALIDATION_ERROR,null,!1);return null})).catch((function(exception){return _this6.onSubmitError(exception)}));case 9:case"end":return _context.stop()}}),_callee,this)})),_submitFormAjax=function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))},function(){return _submitFormAjax.apply(this,arguments)})}],protoProps&&_defineProperties(Constructor.prototype,protoProps),staticProps&&_defineProperties(Constructor,staticProps),Object.defineProperty(Constructor,"prototype",{writable:!1}),DynamicForm}();return _exports.default=DynamicForm,_exports.default})); //# sourceMappingURL=dynamicform.min.js.map \ No newline at end of file diff --git a/lib/form/amd/build/dynamicform.min.js.map b/lib/form/amd/build/dynamicform.min.js.map index 8e2053149db..1a4d42518c4 100644 --- a/lib/form/amd/build/dynamicform.min.js.map +++ b/lib/form/amd/build/dynamicform.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dynamicform.min.js","sources":["../src/dynamicform.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 * Display an embedded form, it is only loaded and reloaded inside its container\n *\n *\n * @module core_form/dynamicform\n * @copyright 2019 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * See also https://docs.moodle.org/dev/Modal_and_AJAX_forms\n *\n * @example\n * import DynamicForm from 'core_form/dynamicform';\n *\n * const dynamicForm = new DynamicForm(document.querySelector('#mycontainer', 'pluginname\\\\form\\\\formname');\n * dynamicForm.addEventListener(dynamicForm.events.FORM_SUBMITTED, e => {\n * e.preventDefault();\n * window.console.log(e.detail);\n * dynamicForm.container.innerHTML = 'Thank you, your form is submitted!';\n * });\n * dynamicForm.load();\n *\n */\n\nimport Ajax from 'core/ajax';\nimport Notification from 'core/notification';\nimport Templates from 'core/templates';\nimport Event from 'core/event';\nimport {get_strings as getStrings} from 'core/str';\nimport Y from 'core/yui';\nimport Fragment from 'core/fragment';\nimport Pending from 'core/pending';\n\n/**\n * @class core_form/dynamicform\n */\nexport default class DynamicForm {\n\n /**\n * Various events that can be observed.\n *\n * @type {Object}\n */\n events = {\n // Form was successfully submitted - the response is passed to the event listener.\n // Cancellable (in order to prevent default behavior to clear the container).\n FORM_SUBMITTED: 'core_form_dynamicform_formsubmitted',\n // Cancel button was pressed.\n // Cancellable (in order to prevent default behavior to clear the container).\n FORM_CANCELLED: 'core_form_dynamicform_formcancelled',\n // User attempted to submit the form but there was client-side validation error.\n CLIENT_VALIDATION_ERROR: 'core_form_dynamicform_clientvalidationerror',\n // User attempted to submit the form but server returned validation error.\n SERVER_VALIDATION_ERROR: 'core_form_dynamicform_validationerror',\n // Error occurred while performing request to the server.\n // Cancellable (by default calls Notification.exception).\n ERROR: 'core_form_dynamicform_error',\n // Right after user pressed no-submit button,\n // listen to this event if you want to add JS validation or processing for no-submit button.\n // Cancellable.\n NOSUBMIT_BUTTON_PRESSED: 'core_form_dynamicform_nosubmitbutton',\n // Right after user pressed submit button,\n // listen to this event if you want to add additional JS validation or confirmation dialog.\n // Cancellable.\n SUBMIT_BUTTON_PRESSED: 'core_form_dynamicform_submitbutton',\n // Right after user pressed cancel button,\n // listen to this event if you want to add confirmation dialog.\n // Cancellable.\n CANCEL_BUTTON_PRESSED: 'core_form_dynamicform_cancelbutton',\n };\n\n /**\n * Constructor\n *\n * Creates an instance\n *\n * @param {Element} container - the parent element for the form\n * @param {string} formClass full name of the php class that extends \\core_form\\modal , must be in autoloaded location\n */\n constructor(container, formClass) {\n this.formClass = formClass;\n this.container = container;\n\n // Ensure strings required for shortforms are always available.\n getStrings([\n {key: 'collapseall', component: 'moodle'},\n {key: 'expandall', component: 'moodle'}\n ]).catch(Notification.exception);\n\n // Register delegated events handlers in vanilla JS.\n this.container.addEventListener('click', e => {\n if (e.target.matches('form input[type=submit][data-cancel]')) {\n e.preventDefault();\n const event = this.trigger(this.events.CANCEL_BUTTON_PRESSED, e.target);\n if (!event.defaultPrevented) {\n this.processCancelButton();\n }\n } else if (e.target.matches('form input[type=submit][data-no-submit=\"1\"]')) {\n e.preventDefault();\n const event = this.trigger(this.events.NOSUBMIT_BUTTON_PRESSED, e.target);\n if (!event.defaultPrevented) {\n this.processNoSubmitButton(e.target);\n }\n }\n });\n\n this.container.addEventListener('submit', e => {\n if (e.target.matches('form')) {\n e.preventDefault();\n const event = this.trigger(this.events.SUBMIT_BUTTON_PRESSED);\n if (!event.defaultPrevented) {\n this.submitFormAjax();\n }\n }\n });\n }\n\n /**\n * Loads the form via AJAX and shows it inside a given container\n *\n * @param {Object} args\n * @return {Promise}\n * @public\n */\n load(args = null) {\n const formData = new URLSearchParams(Object.entries(args || {}));\n const pendingPromise = new Pending('core_form/dynamicform:load');\n return this.getBody(formData.toString())\n .then((resp) => this.updateForm(resp))\n .then(pendingPromise.resolve);\n }\n\n /**\n * Triggers a custom event\n *\n * @private\n * @param {String} eventName\n * @param {*} detail\n * @param {Boolean} cancelable\n * @return {CustomEvent}\n */\n trigger(eventName, detail = null, cancelable = true) {\n const e = new CustomEvent(eventName, {detail, cancelable});\n this.container.dispatchEvent(e);\n return e;\n }\n\n /**\n * Add listener for an event\n *\n * @param {array} args\n * @example:\n * const dynamicForm = new DynamicForm(...);\n * dynamicForm.addEventListener(dynamicForm.events.FORM_SUBMITTED, e => {\n * e.preventDefault();\n * window.console.log(e.detail);\n * dynamicForm.container.innerHTML = 'Thank you, your form is submitted!';\n * });\n */\n addEventListener(...args) {\n this.container.addEventListener(...args);\n }\n\n /**\n * Get form body\n *\n * @param {String} formDataString form data in format of a query string\n * @private\n * @return {Promise}\n */\n getBody(formDataString) {\n return Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: {\n formdata: formDataString,\n form: this.formClass,\n }\n }])[0]\n .then(response => {\n return {html: response.html, js: Fragment.processCollectedJavascript(response.javascript)};\n });\n }\n\n /**\n * On form submit\n *\n * @param {*} response Response received from the form's \"process\" method\n */\n onSubmitSuccess(response) {\n const event = this.trigger(this.events.FORM_SUBMITTED, response);\n if (event.defaultPrevented) {\n return;\n }\n\n // Default implementation is to remove the form. Event listener should either remove or reload the form\n // since its contents is no longer correct. For example, if an element was created as a result of\n // form submission, the \"id\" in the form would be still zero. Also the server-side validation\n // errors from the previous submission may still be present.\n this.container.innerHTML = '';\n }\n\n /**\n * On exception during form processing\n *\n * @private\n * @param {Object} exception\n */\n onSubmitError(exception) {\n const event = this.trigger(this.events.ERROR, exception);\n if (event.defaultPrevented) {\n return;\n }\n\n Notification.exception(exception);\n }\n\n /**\n * Click on a \"submit\" button that is marked in the form as registerNoSubmitButton()\n *\n * @method submitButtonPressed\n * @param {Element} button that was pressed\n */\n processNoSubmitButton(button) {\n const pendingPromise = new Pending('core_form/dynamicform:nosubmit');\n const form = this.container.querySelector('form');\n const formData = new URLSearchParams([...(new FormData(form)).entries()]);\n formData.append(button.getAttribute('name'), button.getAttribute('value'));\n\n this.notifyFormSubmitAjax(true)\n .then(() => {\n // Add the button name to the form data and submit it.\n this.disableButtons();\n return this.getBody(formData.toString());\n })\n .then((resp) => this.updateForm(resp))\n .finally(pendingPromise.resolve)\n .catch(exception => this.onSubmitError(exception));\n }\n\n /**\n * Wrapper for Event.notifyFormSubmitAjax that waits for the module to load\n *\n * We often destroy the form right after calling this function and we need to make sure that it actually\n * completes before it, or otherwise it will try to work with a form that does not exist.\n *\n * @param {Boolean} skipValidation\n * @return {Promise}\n */\n notifyFormSubmitAjax(skipValidation = false) {\n const form = this.container.querySelector('form');\n return new Promise(resolve => {\n Y.use('event', 'moodle-core-event', 'moodle-core-formchangechecker', function() {\n Event.notifyFormSubmitAjax(form, skipValidation);\n resolve();\n });\n });\n }\n\n /**\n * Notifies listeners that form dirty state should be reset.\n *\n * @return {Promise}\n */\n notifyResetFormChanges() {\n const form = this.container.querySelector('form');\n return new Promise(resolve => {\n Y.use('event', 'moodle-core-event', 'moodle-core-formchangechecker', () => {\n Event.notifyFormSubmitAjax(form, true);\n M.core_formchangechecker.reset_form_dirty_state();\n resolve();\n });\n });\n }\n\n /**\n * Click on a \"cancel\" button\n */\n processCancelButton() {\n // Notify listeners that the form is about to be submitted (this will reset atto autosave).\n this.notifyResetFormChanges()\n .then(() => {\n const event = this.trigger(this.events.FORM_CANCELLED);\n if (!event.defaultPrevented) {\n // By default removes the form from the DOM.\n this.container.innerHTML = '';\n }\n return null;\n })\n .catch(null);\n }\n\n /**\n * Update form contents\n *\n * @param {object} param\n * @param {string} param.html\n * @param {string} param.js\n */\n updateForm({html, js}) {\n Templates.replaceNodeContents(this.container, html, js);\n }\n\n /**\n * Validate form elements\n * @return {Promise} promise that returns true if client-side validation has passed, false if there are errors\n */\n validateElements() {\n // Notify listeners that the form is about to be submitted (this will reset atto autosave).\n return this.notifyFormSubmitAjax()\n .then(() => {\n // Now the change events have run, see if there are any \"invalid\" form fields.\n const invalid = [...this.container.querySelectorAll('[aria-invalid=\"true\"], .error')];\n\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (invalid.length) {\n invalid[0].focus();\n return false;\n }\n\n return true;\n });\n }\n\n /**\n * Disable buttons during form submission\n */\n disableButtons() {\n this.container.querySelectorAll('form input[type=\"submit\"]')\n .forEach(el => el.setAttribute('disabled', true));\n }\n\n /**\n * Enable buttons after form submission (on validation error)\n */\n enableButtons() {\n this.container.querySelectorAll('form input[type=\"submit\"]')\n .forEach(el => el.removeAttribute('disabled'));\n }\n\n /**\n * Submit the form via AJAX call to the core_form_dynamic_form WS\n */\n async submitFormAjax() {\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (!(await this.validateElements())) {\n this.trigger(this.events.CLIENT_VALIDATION_ERROR, null, false);\n return;\n }\n this.disableButtons();\n\n // Convert all the form elements values to a serialised string.\n const form = this.container.querySelector('form');\n const formData = new URLSearchParams([...(new FormData(form)).entries()]);\n\n // Now we can continue...\n Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: {\n formdata: formData.toString(),\n form: this.formClass\n }\n }])[0]\n .then((response) => {\n if (!response.submitted) {\n // Form was not submitted, it could be either because validation failed or because no-submit button was pressed.\n this.updateForm({html: response.html, js: Fragment.processCollectedJavascript(response.javascript)});\n this.enableButtons();\n this.trigger(this.events.SERVER_VALIDATION_ERROR, null, false);\n } else {\n // Form was submitted properly.\n const data = JSON.parse(response.data);\n this.enableButtons();\n this.notifyResetFormChanges()\n .then(() => this.onSubmitSuccess(data))\n .catch();\n }\n return null;\n })\n .catch(exception => this.onSubmitError(exception));\n }\n}\n"],"names":["DynamicForm","container","formClass","FORM_SUBMITTED","FORM_CANCELLED","CLIENT_VALIDATION_ERROR","SERVER_VALIDATION_ERROR","ERROR","NOSUBMIT_BUTTON_PRESSED","SUBMIT_BUTTON_PRESSED","CANCEL_BUTTON_PRESSED","key","component","catch","Notification","exception","addEventListener","e","target","matches","preventDefault","_this","trigger","events","defaultPrevented","processCancelButton","processNoSubmitButton","submitFormAjax","args","formData","URLSearchParams","Object","entries","pendingPromise","Pending","this","getBody","toString","then","resp","_this2","updateForm","resolve","eventName","detail","cancelable","CustomEvent","dispatchEvent","formDataString","Ajax","call","methodname","formdata","form","response","html","js","Fragment","processCollectedJavascript","javascript","innerHTML","button","querySelector","FormData","append","getAttribute","notifyFormSubmitAjax","_this3","disableButtons","finally","onSubmitError","skipValidation","Promise","use","M","core_formchangechecker","reset_form_dirty_state","notifyResetFormChanges","_this4","replaceNodeContents","invalid","_this5","querySelectorAll","length","focus","forEach","el","setAttribute","removeAttribute","validateElements","submitted","data","JSON","parse","_this6","enableButtons","onSubmitSuccess"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"dynamicform.min.js","sources":["../src/dynamicform.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 * Display an embedded form, it is only loaded and reloaded inside its container\n *\n *\n * @module core_form/dynamicform\n * @copyright 2019 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * See also https://docs.moodle.org/dev/Modal_and_AJAX_forms\n *\n * @example\n * import DynamicForm from 'core_form/dynamicform';\n *\n * const dynamicForm = new DynamicForm(document.querySelector('#mycontainer', 'pluginname\\\\form\\\\formname');\n * dynamicForm.addEventListener(dynamicForm.events.FORM_SUBMITTED, e => {\n * e.preventDefault();\n * window.console.log(e.detail);\n * dynamicForm.container.innerHTML = 'Thank you, your form is submitted!';\n * });\n * dynamicForm.load();\n *\n */\n\nimport Ajax from 'core/ajax';\nimport Notification from 'core/notification';\nimport Templates from 'core/templates';\nimport Event from 'core/event';\nimport {get_strings as getStrings} from 'core/str';\nimport Y from 'core/yui';\nimport Fragment from 'core/fragment';\nimport Pending from 'core/pending';\n\n/**\n * @class core_form/dynamicform\n */\nexport default class DynamicForm {\n\n /**\n * Various events that can be observed.\n *\n * @type {Object}\n */\n events = {\n // Form was successfully submitted - the response is passed to the event listener.\n // Cancellable (in order to prevent default behavior to clear the container).\n FORM_SUBMITTED: 'core_form_dynamicform_formsubmitted',\n // Cancel button was pressed.\n // Cancellable (in order to prevent default behavior to clear the container).\n FORM_CANCELLED: 'core_form_dynamicform_formcancelled',\n // User attempted to submit the form but there was client-side validation error.\n CLIENT_VALIDATION_ERROR: 'core_form_dynamicform_clientvalidationerror',\n // User attempted to submit the form but server returned validation error.\n SERVER_VALIDATION_ERROR: 'core_form_dynamicform_validationerror',\n // Error occurred while performing request to the server.\n // Cancellable (by default calls Notification.exception).\n ERROR: 'core_form_dynamicform_error',\n // Right after user pressed no-submit button,\n // listen to this event if you want to add JS validation or processing for no-submit button.\n // Cancellable.\n NOSUBMIT_BUTTON_PRESSED: 'core_form_dynamicform_nosubmitbutton',\n // Right after user pressed submit button,\n // listen to this event if you want to add additional JS validation or confirmation dialog.\n // Cancellable.\n SUBMIT_BUTTON_PRESSED: 'core_form_dynamicform_submitbutton',\n // Right after user pressed cancel button,\n // listen to this event if you want to add confirmation dialog.\n // Cancellable.\n CANCEL_BUTTON_PRESSED: 'core_form_dynamicform_cancelbutton',\n };\n\n /**\n * Constructor\n *\n * Creates an instance\n *\n * @param {Element} container - the parent element for the form\n * @param {string} formClass full name of the php class that extends \\core_form\\modal , must be in autoloaded location\n */\n constructor(container, formClass) {\n this.formClass = formClass;\n this.container = container;\n\n // Ensure strings required for shortforms are always available.\n getStrings([\n {key: 'collapseall', component: 'moodle'},\n {key: 'expandall', component: 'moodle'}\n ]).catch(Notification.exception);\n\n // Register delegated events handlers in vanilla JS.\n this.container.addEventListener('click', e => {\n if (e.target.matches('form input[type=submit][data-cancel]')) {\n e.preventDefault();\n const event = this.trigger(this.events.CANCEL_BUTTON_PRESSED, e.target);\n if (!event.defaultPrevented) {\n this.processCancelButton();\n }\n } else if (e.target.matches('form input[type=submit][data-no-submit=\"1\"]')) {\n e.preventDefault();\n const event = this.trigger(this.events.NOSUBMIT_BUTTON_PRESSED, e.target);\n if (!event.defaultPrevented) {\n this.processNoSubmitButton(e.target);\n }\n }\n });\n\n this.container.addEventListener('submit', e => {\n if (e.target.matches('form')) {\n e.preventDefault();\n const event = this.trigger(this.events.SUBMIT_BUTTON_PRESSED);\n if (!event.defaultPrevented) {\n this.submitFormAjax();\n }\n }\n });\n }\n\n /**\n * Loads the form via AJAX and shows it inside a given container\n *\n * @param {Object} args\n * @return {Promise}\n * @public\n */\n load(args = null) {\n const serialize = function(obj, prefix = '') {\n return [].concat(...Object.entries(obj).map(([idx, v]) => {\n const k = prefix ? prefix + \"[\" + idx + \"]\" : idx;\n return (typeof v === \"object\") ? serialize(v, k) : `${k}=${encodeURIComponent(v)}`;\n })).join(\"&\");\n };\n const formData = serialize(args || {});\n const pendingPromise = new Pending('core_form/dynamicform:load');\n return this.getBody(formData)\n .then((resp) => this.updateForm(resp))\n .then(pendingPromise.resolve);\n }\n\n /**\n * Triggers a custom event\n *\n * @private\n * @param {String} eventName\n * @param {*} detail\n * @param {Boolean} cancelable\n * @return {CustomEvent}\n */\n trigger(eventName, detail = null, cancelable = true) {\n const e = new CustomEvent(eventName, {detail, cancelable});\n this.container.dispatchEvent(e);\n return e;\n }\n\n /**\n * Add listener for an event\n *\n * @param {array} args\n * @example:\n * const dynamicForm = new DynamicForm(...);\n * dynamicForm.addEventListener(dynamicForm.events.FORM_SUBMITTED, e => {\n * e.preventDefault();\n * window.console.log(e.detail);\n * dynamicForm.container.innerHTML = 'Thank you, your form is submitted!';\n * });\n */\n addEventListener(...args) {\n this.container.addEventListener(...args);\n }\n\n /**\n * Get form body\n *\n * @param {String} formDataString form data in format of a query string\n * @private\n * @return {Promise}\n */\n getBody(formDataString) {\n return Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: {\n formdata: formDataString,\n form: this.formClass,\n }\n }])[0]\n .then(response => {\n return {html: response.html, js: Fragment.processCollectedJavascript(response.javascript)};\n });\n }\n\n /**\n * On form submit\n *\n * @param {*} response Response received from the form's \"process\" method\n */\n onSubmitSuccess(response) {\n const event = this.trigger(this.events.FORM_SUBMITTED, response);\n if (event.defaultPrevented) {\n return;\n }\n\n // Default implementation is to remove the form. Event listener should either remove or reload the form\n // since its contents is no longer correct. For example, if an element was created as a result of\n // form submission, the \"id\" in the form would be still zero. Also the server-side validation\n // errors from the previous submission may still be present.\n this.container.innerHTML = '';\n }\n\n /**\n * On exception during form processing\n *\n * @private\n * @param {Object} exception\n */\n onSubmitError(exception) {\n const event = this.trigger(this.events.ERROR, exception);\n if (event.defaultPrevented) {\n return;\n }\n\n Notification.exception(exception);\n }\n\n /**\n * Click on a \"submit\" button that is marked in the form as registerNoSubmitButton()\n *\n * @method submitButtonPressed\n * @param {Element} button that was pressed\n */\n processNoSubmitButton(button) {\n const pendingPromise = new Pending('core_form/dynamicform:nosubmit');\n const form = this.container.querySelector('form');\n const formData = new URLSearchParams([...(new FormData(form)).entries()]);\n formData.append(button.getAttribute('name'), button.getAttribute('value'));\n\n this.notifyFormSubmitAjax(true)\n .then(() => {\n // Add the button name to the form data and submit it.\n this.disableButtons();\n return this.getBody(formData.toString());\n })\n .then((resp) => this.updateForm(resp))\n .finally(pendingPromise.resolve)\n .catch(exception => this.onSubmitError(exception));\n }\n\n /**\n * Wrapper for Event.notifyFormSubmitAjax that waits for the module to load\n *\n * We often destroy the form right after calling this function and we need to make sure that it actually\n * completes before it, or otherwise it will try to work with a form that does not exist.\n *\n * @param {Boolean} skipValidation\n * @return {Promise}\n */\n notifyFormSubmitAjax(skipValidation = false) {\n const form = this.container.querySelector('form');\n return new Promise(resolve => {\n Y.use('event', 'moodle-core-event', 'moodle-core-formchangechecker', function() {\n Event.notifyFormSubmitAjax(form, skipValidation);\n resolve();\n });\n });\n }\n\n /**\n * Notifies listeners that form dirty state should be reset.\n *\n * @return {Promise}\n */\n notifyResetFormChanges() {\n const form = this.container.querySelector('form');\n return new Promise(resolve => {\n Y.use('event', 'moodle-core-event', 'moodle-core-formchangechecker', () => {\n Event.notifyFormSubmitAjax(form, true);\n M.core_formchangechecker.reset_form_dirty_state();\n resolve();\n });\n });\n }\n\n /**\n * Click on a \"cancel\" button\n */\n processCancelButton() {\n // Notify listeners that the form is about to be submitted (this will reset atto autosave).\n this.notifyResetFormChanges()\n .then(() => {\n const event = this.trigger(this.events.FORM_CANCELLED);\n if (!event.defaultPrevented) {\n // By default removes the form from the DOM.\n this.container.innerHTML = '';\n }\n return null;\n })\n .catch(null);\n }\n\n /**\n * Update form contents\n *\n * @param {object} param\n * @param {string} param.html\n * @param {string} param.js\n */\n updateForm({html, js}) {\n Templates.replaceNodeContents(this.container, html, js);\n }\n\n /**\n * Validate form elements\n * @return {Promise} promise that returns true if client-side validation has passed, false if there are errors\n */\n validateElements() {\n // Notify listeners that the form is about to be submitted (this will reset atto autosave).\n return this.notifyFormSubmitAjax()\n .then(() => {\n // Now the change events have run, see if there are any \"invalid\" form fields.\n const invalid = [...this.container.querySelectorAll('[aria-invalid=\"true\"], .error')];\n\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (invalid.length) {\n invalid[0].focus();\n return false;\n }\n\n return true;\n });\n }\n\n /**\n * Disable buttons during form submission\n */\n disableButtons() {\n this.container.querySelectorAll('form input[type=\"submit\"]')\n .forEach(el => el.setAttribute('disabled', true));\n }\n\n /**\n * Enable buttons after form submission (on validation error)\n */\n enableButtons() {\n this.container.querySelectorAll('form input[type=\"submit\"]')\n .forEach(el => el.removeAttribute('disabled'));\n }\n\n /**\n * Submit the form via AJAX call to the core_form_dynamic_form WS\n */\n async submitFormAjax() {\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (!(await this.validateElements())) {\n this.trigger(this.events.CLIENT_VALIDATION_ERROR, null, false);\n return;\n }\n this.disableButtons();\n\n // Convert all the form elements values to a serialised string.\n const form = this.container.querySelector('form');\n const formData = new URLSearchParams([...(new FormData(form)).entries()]);\n\n // Now we can continue...\n Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: {\n formdata: formData.toString(),\n form: this.formClass\n }\n }])[0]\n .then((response) => {\n if (!response.submitted) {\n // Form was not submitted, it could be either because validation failed or because no-submit button was pressed.\n this.updateForm({html: response.html, js: Fragment.processCollectedJavascript(response.javascript)});\n this.enableButtons();\n this.trigger(this.events.SERVER_VALIDATION_ERROR, null, false);\n } else {\n // Form was submitted properly.\n const data = JSON.parse(response.data);\n this.enableButtons();\n this.notifyResetFormChanges()\n .then(() => this.onSubmitSuccess(data))\n .catch();\n }\n return null;\n })\n .catch(exception => this.onSubmitError(exception));\n }\n}\n"],"names":["DynamicForm","container","formClass","FORM_SUBMITTED","FORM_CANCELLED","CLIENT_VALIDATION_ERROR","SERVER_VALIDATION_ERROR","ERROR","NOSUBMIT_BUTTON_PRESSED","SUBMIT_BUTTON_PRESSED","CANCEL_BUTTON_PRESSED","key","component","catch","Notification","exception","addEventListener","e","target","matches","preventDefault","_this","trigger","events","defaultPrevented","processCancelButton","processNoSubmitButton","submitFormAjax","args","serialize","obj","prefix","concat","Object","entries","map","idx","v","k","_typeof","encodeURIComponent","join","formData","pendingPromise","Pending","this","getBody","then","resp","_this2","updateForm","resolve","eventName","detail","cancelable","CustomEvent","dispatchEvent","formDataString","Ajax","call","methodname","formdata","form","response","html","js","Fragment","processCollectedJavascript","javascript","innerHTML","button","querySelector","URLSearchParams","FormData","append","getAttribute","notifyFormSubmitAjax","_this3","disableButtons","toString","finally","onSubmitError","skipValidation","Promise","use","M","core_formchangechecker","reset_form_dirty_state","notifyResetFormChanges","_this4","replaceNodeContents","invalid","_this5","querySelectorAll","length","focus","forEach","el","setAttribute","removeAttribute","validateElements","submitted","data","JSON","parse","_this6","enableButtons","onSubmitSuccess"],"mappings":"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"} \ No newline at end of file diff --git a/lib/form/amd/build/modalform.min.js b/lib/form/amd/build/modalform.min.js index 936db29aee2..5e9d49ec093 100644 --- a/lib/form/amd/build/modalform.min.js +++ b/lib/form/amd/build/modalform.min.js @@ -1,3 +1,3 @@ -define("core_form/modalform",["exports","core/modal_factory","core/modal_events","core/ajax","core/notification","core/yui","core/event","core/fragment","core/pending"],(function(_exports,_modal_factory,_modal_events,_ajax,_notification,_yui,_event,_fragment,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}function _toConsumableArray(arr){return function(arr){if(Array.isArray(arr))return _arrayLikeToArray(arr)}(arr)||function(iter){if("undefined"!=typeof Symbol&&null!=iter[Symbol.iterator]||null!=iter["@@iterator"])return Array.from(iter)}(arr)||function(o,minLen){if(!o)return;if("string"==typeof o)return _arrayLikeToArray(o,minLen);var n=Object.prototype.toString.call(o).slice(8,-1);"Object"===n&&o.constructor&&(n=o.constructor.name);if("Map"===n||"Set"===n)return Array.from(o);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return _arrayLikeToArray(o,minLen)}(arr)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function _arrayLikeToArray(arr,len){(null==len||len>arr.length)&&(len=arr.length);for(var i=0,arr2=new Array(len);i1&&void 0!==arguments[1]?arguments[1]:null,cancelable=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],e=new CustomEvent(eventName,{detail:detail,cancelable:cancelable});return this.modal.getRoot()[0].dispatchEvent(e),e}},{key:"addEventListener",value:function(){for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];var _this$modal$getRoot$2;this.modal?(_this$modal$getRoot$2=this.modal.getRoot()[0]).addEventListener.apply(_this$modal$getRoot$2,args):this.futureListeners.push(args)}},{key:"getBody",value:function(formDataString){var params={formdata:formDataString,form:this.config.formClass},pendingPromise=new _pending.default("core_form/modalform:form_body");return _ajax.default.call([{methodname:"core_form_dynamic_form",args:params}])[0].then((function(response){return pendingPromise.resolve(),{html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)}}))}},{key:"onSubmitError",value:function(exception){this.trigger(this.events.ERROR,exception).defaultPrevented||_notification.default.exception(exception)}},{key:"notifyResetFormChanges",value:function(){var _this2=this;return new Promise((function(resolve){_yui.default.use("event","moodle-core-event","moodle-core-formchangechecker",(function(){var form=_this2.modal.getRoot().find("form")[0];form&&(_event.default.notifyFormSubmitAjax(form,!0),M.core_formchangechecker.reset_form_dirty_state()),resolve()}))}))}},{key:"notifyFormSubmitAjax",value:function(){var _this3=this,skipValidation=arguments.length>0&&void 0!==arguments[0]&&arguments[0];return new Promise((function(resolve){_yui.default.use("event","moodle-core-event","moodle-core-formchangechecker",(function(){_event.default.notifyFormSubmitAjax(_this3.modal.getRoot().find("form")[0],skipValidation),resolve()}))}))}},{key:"processNoSubmitButton",value:function(button){var _this4=this;this.notifyFormSubmitAjax(!0).then((function(){var formData=_this4.modal.getRoot().find("form").serialize();formData=formData+"&"+encodeURIComponent(button.getAttribute("name"))+"="+encodeURIComponent(button.getAttribute("value"));var bodyContent=_this4.getBody(formData);return _this4.modal.setBodyContent(bodyContent),bodyContent.catch(_notification.default.exception),null})).catch(null)}},{key:"validateElements",value:function(){var _this5=this;return this.notifyFormSubmitAjax().then((function(){var invalid=_this5.modal.getRoot().find('[aria-invalid="true"], .error');return!invalid.length||(invalid.first().focus(),!1)}))}},{key:"disableButtons",value:function(){this.modal.getFooter().find("[data-action]").attr("disabled",!0)}},{key:"enableButtons",value:function(){this.modal.getFooter().find("[data-action]").removeAttr("disabled")}},{key:"submitFormAjax",value:(fn=regeneratorRuntime.mark((function _callee(){var formData,_this6=this;return regeneratorRuntime.wrap((function(_context){for(;;)switch(_context.prev=_context.next){case 0:return _context.next=2,this.validateElements();case 2:if(_context.sent){_context.next=5;break}return this.trigger(this.events.CLIENT_VALIDATION_ERROR,null,!1),_context.abrupt("return");case 5:this.disableButtons(),formData=this.modal.getRoot().find("form").serialize(),_ajax.default.call([{methodname:"core_form_dynamic_form",args:{formdata:formData,form:this.config.formClass}}])[0].then((function(response){if(response.submitted){var data=JSON.parse(response.data);return _this6.notifyResetFormChanges().then((function(){return _this6.trigger(_this6.events.FORM_SUBMITTED,data).defaultPrevented||_this6.modal.hide(),null}))}var promise=new Promise((function(resolve){return resolve({html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)})}));return _this6.modal.setBodyContent(promise),_this6.enableButtons(),_this6.trigger(_this6.events.SERVER_VALIDATION_ERROR),null})).catch((function(exception){return _this6.onSubmitError(exception)}));case 8:case"end":return _context.stop()}}),_callee,this)})),_submitFormAjax=function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))},function(){return _submitFormAjax.apply(this,arguments)})},{key:"setSaveButtonClasses",value:function(value){var button=this.modal.getFooter().find("[data-action='save']");if(!button)throw new Error("Unable to find the 'save' button");button.removeClass().addClass(value)}}],protoProps&&_defineProperties(Constructor.prototype,protoProps),staticProps&&_defineProperties(Constructor,staticProps),Object.defineProperty(Constructor,"prototype",{writable:!1}),ModalForm}();return _exports.default=ModalForm,_exports.default})); +define("core_form/modalform",["exports","core/modal_factory","core/modal_events","core/ajax","core/notification","core/yui","core/event","core/fragment","core/pending"],(function(_exports,_modal_factory,_modal_events,_ajax,_notification,_yui,_event,_fragment,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}function _typeof(obj){return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(obj){return typeof obj}:function(obj){return obj&&"function"==typeof Symbol&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj},_typeof(obj)}function _slicedToArray(arr,i){return function(arr){if(Array.isArray(arr))return arr}(arr)||function(arr,i){var _i=null==arr?null:"undefined"!=typeof Symbol&&arr[Symbol.iterator]||arr["@@iterator"];if(null==_i)return;var _s,_e,_arr=[],_n=!0,_d=!1;try{for(_i=_i.call(arr);!(_n=(_s=_i.next()).done)&&(_arr.push(_s.value),!i||_arr.length!==i);_n=!0);}catch(err){_d=!0,_e=err}finally{try{_n||null==_i.return||_i.return()}finally{if(_d)throw _e}}return _arr}(arr,i)||_unsupportedIterableToArray(arr,i)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function _toConsumableArray(arr){return function(arr){if(Array.isArray(arr))return _arrayLikeToArray(arr)}(arr)||function(iter){if("undefined"!=typeof Symbol&&null!=iter[Symbol.iterator]||null!=iter["@@iterator"])return Array.from(iter)}(arr)||_unsupportedIterableToArray(arr)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function _unsupportedIterableToArray(o,minLen){if(o){if("string"==typeof o)return _arrayLikeToArray(o,minLen);var n=Object.prototype.toString.call(o).slice(8,-1);return"Object"===n&&o.constructor&&(n=o.constructor.name),"Map"===n||"Set"===n?Array.from(o):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?_arrayLikeToArray(o,minLen):void 0}}function _arrayLikeToArray(arr,len){(null==len||len>arr.length)&&(len=arr.length);for(var i=0,arr2=new Array(len);i1&&void 0!==arguments[1]?arguments[1]:"";return(_ref=[]).concat.apply(_ref,_toConsumableArray(Object.entries(obj).map((function(_ref2){var _ref3=_slicedToArray(_ref2,2),idx=_ref3[0],v=_ref3[1],k=prefix?prefix+"["+idx+"]":idx;return"object"===_typeof(v)?serialize(v,k):"".concat(k,"=").concat(encodeURIComponent(v))})))).join("&")};return _modal_factory.default.create(this.config.modalConfig).then((function(modal){_this.modal=modal;var formParams=serialize(_this.config.args||{}),bodyContent=_this.getBody(formParams);return _this.modal.setBodyContent(bodyContent),bodyContent.catch(_notification.default.exception),_this.modal.getRoot().on(_modal_events.default.hidden,(function(){_this.notifyResetFormChanges().then((function(){return _this.modal.destroy(),_this.config.returnFocus&&_this.config.returnFocus.focus(),null})).catch((function(){return null}))})),_this.modal.getModal().addClass("modal-form-dialogue"),_this.modal.getRoot().on("click","form input[type=submit][data-no-submit]",(function(e){e.preventDefault(),_this.trigger(_this.events.NOSUBMIT_BUTTON_PRESSED,e.target).defaultPrevented||_this.processNoSubmitButton(e.target)})),_this.modal.getRoot().on("submit","form",(function(e){e.preventDefault(),_this.trigger(_this.events.SUBMIT_BUTTON_PRESSED).defaultPrevented||_this.submitFormAjax()})),void 0!==_this.config.saveButtonText&&void 0!==_this.modal.setSaveButtonText&&_this.modal.setSaveButtonText(_this.config.saveButtonText),void 0!==_this.config.saveButtonClasses&&_this.setSaveButtonClasses(_this.config.saveButtonClasses),_this.modal.getRoot().on(_modal_events.default.save,(function(e){e.preventDefault(),_this.modal.getRoot().find("form").submit()})),_this.modal.getRoot().on(_modal_events.default.cancel,(function(e){_this.trigger(_this.events.CANCEL_BUTTON_PRESSED).defaultPrevented&&e.preventDefault()})),_this.futureListeners.forEach((function(args){var _this$modal$getRoot$;return(_this$modal$getRoot$=_this.modal.getRoot()[0]).addEventListener.apply(_this$modal$getRoot$,_toConsumableArray(args))})),_this.futureListeners=[],_this.trigger(_this.events.LOADED,null,!1),_this.modal.show()})).then(pendingPromise.resolve)}},{key:"trigger",value:function(eventName){var detail=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,cancelable=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],e=new CustomEvent(eventName,{detail:detail,cancelable:cancelable});return this.modal.getRoot()[0].dispatchEvent(e),e}},{key:"addEventListener",value:function(){for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];var _this$modal$getRoot$2;this.modal?(_this$modal$getRoot$2=this.modal.getRoot()[0]).addEventListener.apply(_this$modal$getRoot$2,args):this.futureListeners.push(args)}},{key:"getBody",value:function(formDataString){var params={formdata:formDataString,form:this.config.formClass},pendingPromise=new _pending.default("core_form/modalform:form_body");return _ajax.default.call([{methodname:"core_form_dynamic_form",args:params}])[0].then((function(response){return pendingPromise.resolve(),{html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)}}))}},{key:"onSubmitError",value:function(exception){this.trigger(this.events.ERROR,exception).defaultPrevented||_notification.default.exception(exception)}},{key:"notifyResetFormChanges",value:function(){var _this2=this;return new Promise((function(resolve){_yui.default.use("event","moodle-core-event","moodle-core-formchangechecker",(function(){var form=_this2.modal.getRoot().find("form")[0];form&&(_event.default.notifyFormSubmitAjax(form,!0),M.core_formchangechecker.reset_form_dirty_state()),resolve()}))}))}},{key:"notifyFormSubmitAjax",value:function(){var _this3=this,skipValidation=arguments.length>0&&void 0!==arguments[0]&&arguments[0];return new Promise((function(resolve){_yui.default.use("event","moodle-core-event","moodle-core-formchangechecker",(function(){_event.default.notifyFormSubmitAjax(_this3.modal.getRoot().find("form")[0],skipValidation),resolve()}))}))}},{key:"processNoSubmitButton",value:function(button){var _this4=this;this.notifyFormSubmitAjax(!0).then((function(){var formData=_this4.modal.getRoot().find("form").serialize();formData=formData+"&"+encodeURIComponent(button.getAttribute("name"))+"="+encodeURIComponent(button.getAttribute("value"));var bodyContent=_this4.getBody(formData);return _this4.modal.setBodyContent(bodyContent),bodyContent.catch(_notification.default.exception),null})).catch(null)}},{key:"validateElements",value:function(){var _this5=this;return this.notifyFormSubmitAjax().then((function(){var invalid=_this5.modal.getRoot().find('[aria-invalid="true"], .error');return!invalid.length||(invalid.first().focus(),!1)}))}},{key:"disableButtons",value:function(){this.modal.getFooter().find("[data-action]").attr("disabled",!0)}},{key:"enableButtons",value:function(){this.modal.getFooter().find("[data-action]").removeAttr("disabled")}},{key:"submitFormAjax",value:(fn=regeneratorRuntime.mark((function _callee(){var formData,_this6=this;return regeneratorRuntime.wrap((function(_context){for(;;)switch(_context.prev=_context.next){case 0:return _context.next=2,this.validateElements();case 2:if(_context.sent){_context.next=5;break}return this.trigger(this.events.CLIENT_VALIDATION_ERROR,null,!1),_context.abrupt("return");case 5:this.disableButtons(),formData=this.modal.getRoot().find("form").serialize(),_ajax.default.call([{methodname:"core_form_dynamic_form",args:{formdata:formData,form:this.config.formClass}}])[0].then((function(response){if(response.submitted){var data=JSON.parse(response.data);return _this6.notifyResetFormChanges().then((function(){return _this6.trigger(_this6.events.FORM_SUBMITTED,data).defaultPrevented||_this6.modal.hide(),null}))}var promise=new Promise((function(resolve){return resolve({html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)})}));return _this6.modal.setBodyContent(promise),_this6.enableButtons(),_this6.trigger(_this6.events.SERVER_VALIDATION_ERROR),null})).catch((function(exception){return _this6.onSubmitError(exception)}));case 8:case"end":return _context.stop()}}),_callee,this)})),_submitFormAjax=function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))},function(){return _submitFormAjax.apply(this,arguments)})},{key:"setSaveButtonClasses",value:function(value){var button=this.modal.getFooter().find("[data-action='save']");if(!button)throw new Error("Unable to find the 'save' button");button.removeClass().addClass(value)}}],protoProps&&_defineProperties(Constructor.prototype,protoProps),staticProps&&_defineProperties(Constructor,staticProps),Object.defineProperty(Constructor,"prototype",{writable:!1}),ModalForm}();return _exports.default=ModalForm,_exports.default})); //# sourceMappingURL=modalform.min.js.map \ No newline at end of file diff --git a/lib/form/amd/build/modalform.min.js.map b/lib/form/amd/build/modalform.min.js.map index 070befd3231..1d68b51778a 100644 --- a/lib/form/amd/build/modalform.min.js.map +++ b/lib/form/amd/build/modalform.min.js.map @@ -1 +1 @@ -{"version":3,"file":"modalform.min.js","sources":["../src/modalform.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 * Display a form in a modal dialogue\n *\n * Example:\n * import ModalForm from 'core_form/modalform';\n *\n * const modalForm = new ModalForm({\n * formClass: 'pluginname\\\\form\\\\formname',\n * modalConfig: {title: 'Here comes the title'},\n * args: {categoryid: 123},\n * returnFocus: e.target,\n * });\n * modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, (c) => window.console.log(c.detail));\n * modalForm.show();\n *\n * See also https://docs.moodle.org/dev/Modal_and_AJAX_forms\n *\n * @module core_form/modalform\n * @copyright 2018 Mitxel Moriana \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Ajax from 'core/ajax';\nimport Notification from 'core/notification';\nimport Y from 'core/yui';\nimport Event from 'core/event';\nimport Fragment from 'core/fragment';\nimport Pending from 'core/pending';\n\nexport default class ModalForm {\n\n /**\n * Various events that can be observed.\n *\n * @type {Object}\n */\n events = {\n // Form was successfully submitted - the response is passed to the event listener.\n // Cancellable (but it's hardly ever needed to cancel this event).\n FORM_SUBMITTED: 'core_form_modalform_formsubmitted',\n // Cancel button was pressed.\n // Cancellable (but it's hardly ever needed to cancel this event).\n FORM_CANCELLED: 'core_form_modalform_formcancelled',\n // User attempted to submit the form but there was client-side validation error.\n CLIENT_VALIDATION_ERROR: 'core_form_modalform_clientvalidationerror',\n // User attempted to submit the form but server returned validation error.\n SERVER_VALIDATION_ERROR: 'core_form_modalform_validationerror',\n // Error occurred while performing request to the server.\n // Cancellable (by default calls Notification.exception).\n ERROR: 'core_form_modalform_error',\n // Right after user pressed no-submit button,\n // listen to this event if you want to add JS validation or processing for no-submit button.\n // Cancellable.\n NOSUBMIT_BUTTON_PRESSED: 'core_form_modalform_nosubmitbutton',\n // Right after user pressed submit button,\n // listen to this event if you want to add additional JS validation or confirmation dialog.\n // Cancellable.\n SUBMIT_BUTTON_PRESSED: 'core_form_modalform_submitbutton',\n // Right after user pressed cancel button,\n // listen to this event if you want to add confirmation dialog.\n // Cancellable.\n CANCEL_BUTTON_PRESSED: 'core_form_modalform_cancelbutton',\n // Modal was loaded and this.modal is available (but the form content may not be loaded yet).\n LOADED: 'core_form_modalform_loaded',\n };\n\n /**\n * Constructor\n *\n * Shows the required form inside a modal dialogue\n *\n * @param {Object} config parameters for the form and modal dialogue:\n * @paramy {String} config.formClass PHP class name that handles the form (should extend \\core_form\\modal )\n * @paramy {Object} config.modalConfig modal config - title, type, etc.\n * Default: {removeOnClose: true, type: ModalFactory.types.SAVE_CANCEL}\n * @paramy {Object} config.args Arguments for the initial form rendering (for example, id of the edited entity)\n * @paramy {String} config.saveButtonText the text to display on the Modal \"Save\" button (optional)\n * @paramy {String} config.saveButtonClasses additional CSS classes for the Modal \"Save\" button\n * @paramy {HTMLElement} config.returnFocus element to return focus to after the dialogue is closed\n */\n constructor(config) {\n this.modal = null;\n this.config = config;\n this.config.modalConfig = {\n removeOnClose: true,\n type: ModalFactory.types.SAVE_CANCEL,\n large: true,\n ...(this.config.modalConfig || {}),\n };\n this.config.args = this.config.args || {};\n this.futureListeners = [];\n }\n\n /**\n * Initialise the modal and shows it\n *\n * @return {Promise}\n */\n show() {\n const pendingPromise = new Pending('core_form/modalform:init');\n return ModalFactory.create(this.config.modalConfig)\n .then((modal) => {\n this.modal = modal;\n\n // Retrieve the form and set the modal body. We can not set the body in the modalConfig,\n // we need to make sure that the modal already exists when we render the form. Some form elements\n // such as date_selector inspect the existing elements on the page to find the highest z-index.\n const formParams = new URLSearchParams(Object.entries(this.config.args || {}));\n const bodyContent = this.getBody(formParams.toString());\n this.modal.setBodyContent(bodyContent);\n bodyContent.catch(Notification.exception);\n\n // After successfull submit, when we press \"Cancel\" or close the dialogue by clicking on X in the top right corner.\n this.modal.getRoot().on(ModalEvents.hidden, () => {\n this.notifyResetFormChanges()\n .then(() => {\n this.modal.destroy();\n // Focus on the element that actually launched the modal.\n if (this.config.returnFocus) {\n this.config.returnFocus.focus();\n }\n return null;\n })\n .catch(() => null);\n });\n\n // Add the class to the modal dialogue.\n this.modal.getModal().addClass('modal-form-dialogue');\n\n // We catch the press on submit buttons in the forms.\n this.modal.getRoot().on('click', 'form input[type=submit][data-no-submit]',\n (e) => {\n e.preventDefault();\n const event = this.trigger(this.events.NOSUBMIT_BUTTON_PRESSED, e.target);\n if (!event.defaultPrevented) {\n this.processNoSubmitButton(e.target);\n }\n });\n\n // We catch the form submit event and use it to submit the form with ajax.\n this.modal.getRoot().on('submit', 'form', (e) => {\n e.preventDefault();\n const event = this.trigger(this.events.SUBMIT_BUTTON_PRESSED);\n if (!event.defaultPrevented) {\n this.submitFormAjax();\n }\n });\n\n // Change the text for the save button.\n if (typeof this.config.saveButtonText !== 'undefined' &&\n typeof this.modal.setSaveButtonText !== 'undefined') {\n this.modal.setSaveButtonText(this.config.saveButtonText);\n }\n // Set classes for the save button.\n if (typeof this.config.saveButtonClasses !== 'undefined') {\n this.setSaveButtonClasses(this.config.saveButtonClasses);\n }\n // When Save button is pressed - submit the form.\n this.modal.getRoot().on(ModalEvents.save, (e) => {\n e.preventDefault();\n this.modal.getRoot().find('form').submit();\n });\n\n // When Cancel button is pressed - allow to intercept.\n this.modal.getRoot().on(ModalEvents.cancel, (e) => {\n const event = this.trigger(this.events.CANCEL_BUTTON_PRESSED);\n if (event.defaultPrevented) {\n e.preventDefault();\n }\n });\n this.futureListeners.forEach(args => this.modal.getRoot()[0].addEventListener(...args));\n this.futureListeners = [];\n this.trigger(this.events.LOADED, null, false);\n return this.modal.show();\n })\n .then(pendingPromise.resolve);\n }\n\n /**\n * Triggers a custom event\n *\n * @private\n * @param {String} eventName\n * @param {*} detail\n * @param {Boolean} cancelable\n * @return {CustomEvent}\n */\n trigger(eventName, detail = null, cancelable = true) {\n const e = new CustomEvent(eventName, {detail, cancelable});\n this.modal.getRoot()[0].dispatchEvent(e);\n return e;\n }\n\n /**\n * Add listener for an event\n *\n * @param {array} args\n * @example:\n * const modalForm = new ModalForm(...);\n * dynamicForm.addEventListener(modalForm.events.FORM_SUBMITTED, e => {\n * window.console.log(e.detail);\n * });\n */\n addEventListener(...args) {\n if (!this.modal) {\n this.futureListeners.push(args);\n } else {\n this.modal.getRoot()[0].addEventListener(...args);\n }\n }\n\n /**\n * Get form contents (to be used in ModalForm.setBodyContent())\n *\n * @param {String} formDataString form data in format of a query string\n * @method getBody\n * @private\n * @return {Promise}\n */\n getBody(formDataString) {\n const params = {\n formdata: formDataString,\n form: this.config.formClass\n };\n const pendingPromise = new Pending('core_form/modalform:form_body');\n return Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: params\n }])[0]\n .then(response => {\n pendingPromise.resolve();\n return {html: response.html, js: Fragment.processCollectedJavascript(response.javascript)};\n });\n }\n\n /**\n * On exception during form processing. Caller may override\n *\n * @param {Object} exception\n */\n onSubmitError(exception) {\n const event = this.trigger(this.events.ERROR, exception);\n if (event.defaultPrevented) {\n return;\n }\n\n Notification.exception(exception);\n }\n\n /**\n * Notifies listeners that form dirty state should be reset.\n *\n * @return {Promise}\n */\n notifyResetFormChanges() {\n return new Promise(resolve => {\n Y.use('event', 'moodle-core-event', 'moodle-core-formchangechecker', () => {\n // Ensure that modal contains a form element (it may not if the form class threw an early exception).\n const form = this.modal.getRoot().find('form')[0];\n if (form) {\n Event.notifyFormSubmitAjax(form, true);\n M.core_formchangechecker.reset_form_dirty_state();\n }\n resolve();\n });\n });\n }\n\n /**\n * Wrapper for Event.notifyFormSubmitAjax that waits for the module to load\n *\n * We often destroy the form right after calling this function and we need to make sure that it actually\n * completes before it, or otherwise it will try to work with a form that does not exist.\n *\n * @param {Boolean} skipValidation\n * @return {Promise}\n */\n notifyFormSubmitAjax(skipValidation = false) {\n return new Promise(resolve => {\n Y.use('event', 'moodle-core-event', 'moodle-core-formchangechecker', () => {\n Event.notifyFormSubmitAjax(this.modal.getRoot().find('form')[0], skipValidation);\n resolve();\n });\n });\n }\n\n /**\n * Click on a \"submit\" button that is marked in the form as registerNoSubmitButton()\n *\n * @param {Element} button button that was pressed\n */\n processNoSubmitButton(button) {\n this.notifyFormSubmitAjax(true)\n .then(() => {\n // Add the button name to the form data and submit it.\n let formData = this.modal.getRoot().find('form').serialize();\n formData = formData + '&' + encodeURIComponent(button.getAttribute('name')) + '=' +\n encodeURIComponent(button.getAttribute('value'));\n\n const bodyContent = this.getBody(formData);\n this.modal.setBodyContent(bodyContent);\n bodyContent.catch(Notification.exception);\n\n return null;\n })\n .catch(null);\n }\n\n /**\n * Validate form elements\n * @return {Promise} promise that returns true if client-side validation has passed, false if there are errors\n */\n validateElements() {\n return this.notifyFormSubmitAjax()\n .then(() => {\n // Now the change events have run, see if there are any \"invalid\" form fields.\n /** @var {jQuery} list of elements with errors */\n const invalid = this.modal.getRoot().find('[aria-invalid=\"true\"], .error');\n\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (invalid.length) {\n invalid.first().focus();\n return false;\n }\n\n return true;\n });\n }\n\n /**\n * Disable buttons during form submission\n */\n disableButtons() {\n this.modal.getFooter().find('[data-action]').attr('disabled', true);\n }\n\n /**\n * Enable buttons after form submission (on validation error)\n */\n enableButtons() {\n this.modal.getFooter().find('[data-action]').removeAttr('disabled');\n }\n\n /**\n * Submit the form via AJAX call to the core_form_dynamic_form WS\n */\n async submitFormAjax() {\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (!await this.validateElements()) {\n this.trigger(this.events.CLIENT_VALIDATION_ERROR, null, false);\n return;\n }\n this.disableButtons();\n\n // Convert all the form elements values to a serialised string.\n const formData = this.modal.getRoot().find('form').serialize();\n\n // Now we can continue...\n Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: {\n formdata: formData,\n form: this.config.formClass\n }\n }])[0]\n .then((response) => {\n if (!response.submitted) {\n // Form was not submitted because validation failed.\n const promise = new Promise(\n resolve => resolve({html: response.html, js: Fragment.processCollectedJavascript(response.javascript)}));\n this.modal.setBodyContent(promise);\n this.enableButtons();\n this.trigger(this.events.SERVER_VALIDATION_ERROR);\n } else {\n // Form was submitted properly. Hide the modal and execute callback.\n const data = JSON.parse(response.data);\n return this.notifyResetFormChanges()\n .then(() => {\n const event = this.trigger(this.events.FORM_SUBMITTED, data);\n if (!event.defaultPrevented) {\n this.modal.hide();\n }\n return null;\n });\n }\n return null;\n })\n .catch(exception => this.onSubmitError(exception));\n }\n\n /**\n * Set the classes for the 'save' button.\n *\n * @method setSaveButtonClasses\n * @param {(String)} value The 'save' button classes.\n */\n setSaveButtonClasses(value) {\n const button = this.modal.getFooter().find(\"[data-action='save']\");\n if (!button) {\n throw new Error(\"Unable to find the 'save' button\");\n }\n button.removeClass().addClass(value);\n }\n}\n"],"names":["ModalForm","config","FORM_SUBMITTED","FORM_CANCELLED","CLIENT_VALIDATION_ERROR","SERVER_VALIDATION_ERROR","ERROR","NOSUBMIT_BUTTON_PRESSED","SUBMIT_BUTTON_PRESSED","CANCEL_BUTTON_PRESSED","LOADED","modal","modalConfig","removeOnClose","type","ModalFactory","types","SAVE_CANCEL","large","this","args","futureListeners","pendingPromise","Pending","create","then","_this","formParams","URLSearchParams","Object","entries","bodyContent","getBody","toString","setBodyContent","catch","Notification","exception","getRoot","on","ModalEvents","hidden","notifyResetFormChanges","destroy","returnFocus","focus","getModal","addClass","e","preventDefault","trigger","events","target","defaultPrevented","processNoSubmitButton","submitFormAjax","saveButtonText","setSaveButtonText","saveButtonClasses","setSaveButtonClasses","save","find","submit","cancel","forEach","addEventListener","show","resolve","eventName","detail","cancelable","CustomEvent","dispatchEvent","push","formDataString","params","formdata","form","formClass","Ajax","call","methodname","response","html","js","Fragment","processCollectedJavascript","javascript","Promise","use","_this2","notifyFormSubmitAjax","M","core_formchangechecker","reset_form_dirty_state","skipValidation","_this3","button","formData","_this4","serialize","encodeURIComponent","getAttribute","invalid","_this5","length","first","getFooter","attr","removeAttr","validateElements","disableButtons","submitted","data","JSON","parse","_this6","hide","promise","enableButtons","onSubmitError","value","Error","removeClass"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"modalform.min.js","sources":["../src/modalform.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 * Display a form in a modal dialogue\n *\n * Example:\n * import ModalForm from 'core_form/modalform';\n *\n * const modalForm = new ModalForm({\n * formClass: 'pluginname\\\\form\\\\formname',\n * modalConfig: {title: 'Here comes the title'},\n * args: {categoryid: 123},\n * returnFocus: e.target,\n * });\n * modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, (c) => window.console.log(c.detail));\n * modalForm.show();\n *\n * See also https://docs.moodle.org/dev/Modal_and_AJAX_forms\n *\n * @module core_form/modalform\n * @copyright 2018 Mitxel Moriana \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Ajax from 'core/ajax';\nimport Notification from 'core/notification';\nimport Y from 'core/yui';\nimport Event from 'core/event';\nimport Fragment from 'core/fragment';\nimport Pending from 'core/pending';\n\nexport default class ModalForm {\n\n /**\n * Various events that can be observed.\n *\n * @type {Object}\n */\n events = {\n // Form was successfully submitted - the response is passed to the event listener.\n // Cancellable (but it's hardly ever needed to cancel this event).\n FORM_SUBMITTED: 'core_form_modalform_formsubmitted',\n // Cancel button was pressed.\n // Cancellable (but it's hardly ever needed to cancel this event).\n FORM_CANCELLED: 'core_form_modalform_formcancelled',\n // User attempted to submit the form but there was client-side validation error.\n CLIENT_VALIDATION_ERROR: 'core_form_modalform_clientvalidationerror',\n // User attempted to submit the form but server returned validation error.\n SERVER_VALIDATION_ERROR: 'core_form_modalform_validationerror',\n // Error occurred while performing request to the server.\n // Cancellable (by default calls Notification.exception).\n ERROR: 'core_form_modalform_error',\n // Right after user pressed no-submit button,\n // listen to this event if you want to add JS validation or processing for no-submit button.\n // Cancellable.\n NOSUBMIT_BUTTON_PRESSED: 'core_form_modalform_nosubmitbutton',\n // Right after user pressed submit button,\n // listen to this event if you want to add additional JS validation or confirmation dialog.\n // Cancellable.\n SUBMIT_BUTTON_PRESSED: 'core_form_modalform_submitbutton',\n // Right after user pressed cancel button,\n // listen to this event if you want to add confirmation dialog.\n // Cancellable.\n CANCEL_BUTTON_PRESSED: 'core_form_modalform_cancelbutton',\n // Modal was loaded and this.modal is available (but the form content may not be loaded yet).\n LOADED: 'core_form_modalform_loaded',\n };\n\n /**\n * Constructor\n *\n * Shows the required form inside a modal dialogue\n *\n * @param {Object} config parameters for the form and modal dialogue:\n * @paramy {String} config.formClass PHP class name that handles the form (should extend \\core_form\\modal )\n * @paramy {Object} config.modalConfig modal config - title, type, etc.\n * Default: {removeOnClose: true, type: ModalFactory.types.SAVE_CANCEL}\n * @paramy {Object} config.args Arguments for the initial form rendering (for example, id of the edited entity)\n * @paramy {String} config.saveButtonText the text to display on the Modal \"Save\" button (optional)\n * @paramy {String} config.saveButtonClasses additional CSS classes for the Modal \"Save\" button\n * @paramy {HTMLElement} config.returnFocus element to return focus to after the dialogue is closed\n */\n constructor(config) {\n this.modal = null;\n this.config = config;\n this.config.modalConfig = {\n removeOnClose: true,\n type: ModalFactory.types.SAVE_CANCEL,\n large: true,\n ...(this.config.modalConfig || {}),\n };\n this.config.args = this.config.args || {};\n this.futureListeners = [];\n }\n\n /**\n * Initialise the modal and shows it\n *\n * @return {Promise}\n */\n show() {\n const pendingPromise = new Pending('core_form/modalform:init');\n const serialize = function(obj, prefix = '') {\n return [].concat(...Object.entries(obj).map(([idx, v]) => {\n const k = prefix ? prefix + \"[\" + idx + \"]\" : idx;\n return (typeof v === \"object\") ? serialize(v, k) : `${k}=${encodeURIComponent(v)}`;\n })).join(\"&\");\n };\n\n return ModalFactory.create(this.config.modalConfig)\n .then((modal) => {\n this.modal = modal;\n\n // Retrieve the form and set the modal body. We can not set the body in the modalConfig,\n // we need to make sure that the modal already exists when we render the form. Some form elements\n // such as date_selector inspect the existing elements on the page to find the highest z-index.\n const formParams = serialize(this.config.args || {});\n const bodyContent = this.getBody(formParams);\n this.modal.setBodyContent(bodyContent);\n bodyContent.catch(Notification.exception);\n\n // After successfull submit, when we press \"Cancel\" or close the dialogue by clicking on X in the top right corner.\n this.modal.getRoot().on(ModalEvents.hidden, () => {\n this.notifyResetFormChanges()\n .then(() => {\n this.modal.destroy();\n // Focus on the element that actually launched the modal.\n if (this.config.returnFocus) {\n this.config.returnFocus.focus();\n }\n return null;\n })\n .catch(() => null);\n });\n\n // Add the class to the modal dialogue.\n this.modal.getModal().addClass('modal-form-dialogue');\n\n // We catch the press on submit buttons in the forms.\n this.modal.getRoot().on('click', 'form input[type=submit][data-no-submit]',\n (e) => {\n e.preventDefault();\n const event = this.trigger(this.events.NOSUBMIT_BUTTON_PRESSED, e.target);\n if (!event.defaultPrevented) {\n this.processNoSubmitButton(e.target);\n }\n });\n\n // We catch the form submit event and use it to submit the form with ajax.\n this.modal.getRoot().on('submit', 'form', (e) => {\n e.preventDefault();\n const event = this.trigger(this.events.SUBMIT_BUTTON_PRESSED);\n if (!event.defaultPrevented) {\n this.submitFormAjax();\n }\n });\n\n // Change the text for the save button.\n if (typeof this.config.saveButtonText !== 'undefined' &&\n typeof this.modal.setSaveButtonText !== 'undefined') {\n this.modal.setSaveButtonText(this.config.saveButtonText);\n }\n // Set classes for the save button.\n if (typeof this.config.saveButtonClasses !== 'undefined') {\n this.setSaveButtonClasses(this.config.saveButtonClasses);\n }\n // When Save button is pressed - submit the form.\n this.modal.getRoot().on(ModalEvents.save, (e) => {\n e.preventDefault();\n this.modal.getRoot().find('form').submit();\n });\n\n // When Cancel button is pressed - allow to intercept.\n this.modal.getRoot().on(ModalEvents.cancel, (e) => {\n const event = this.trigger(this.events.CANCEL_BUTTON_PRESSED);\n if (event.defaultPrevented) {\n e.preventDefault();\n }\n });\n this.futureListeners.forEach(args => this.modal.getRoot()[0].addEventListener(...args));\n this.futureListeners = [];\n this.trigger(this.events.LOADED, null, false);\n return this.modal.show();\n })\n .then(pendingPromise.resolve);\n }\n\n /**\n * Triggers a custom event\n *\n * @private\n * @param {String} eventName\n * @param {*} detail\n * @param {Boolean} cancelable\n * @return {CustomEvent}\n */\n trigger(eventName, detail = null, cancelable = true) {\n const e = new CustomEvent(eventName, {detail, cancelable});\n this.modal.getRoot()[0].dispatchEvent(e);\n return e;\n }\n\n /**\n * Add listener for an event\n *\n * @param {array} args\n * @example:\n * const modalForm = new ModalForm(...);\n * dynamicForm.addEventListener(modalForm.events.FORM_SUBMITTED, e => {\n * window.console.log(e.detail);\n * });\n */\n addEventListener(...args) {\n if (!this.modal) {\n this.futureListeners.push(args);\n } else {\n this.modal.getRoot()[0].addEventListener(...args);\n }\n }\n\n /**\n * Get form contents (to be used in ModalForm.setBodyContent())\n *\n * @param {String} formDataString form data in format of a query string\n * @method getBody\n * @private\n * @return {Promise}\n */\n getBody(formDataString) {\n const params = {\n formdata: formDataString,\n form: this.config.formClass\n };\n const pendingPromise = new Pending('core_form/modalform:form_body');\n return Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: params\n }])[0]\n .then(response => {\n pendingPromise.resolve();\n return {html: response.html, js: Fragment.processCollectedJavascript(response.javascript)};\n });\n }\n\n /**\n * On exception during form processing. Caller may override\n *\n * @param {Object} exception\n */\n onSubmitError(exception) {\n const event = this.trigger(this.events.ERROR, exception);\n if (event.defaultPrevented) {\n return;\n }\n\n Notification.exception(exception);\n }\n\n /**\n * Notifies listeners that form dirty state should be reset.\n *\n * @return {Promise}\n */\n notifyResetFormChanges() {\n return new Promise(resolve => {\n Y.use('event', 'moodle-core-event', 'moodle-core-formchangechecker', () => {\n // Ensure that modal contains a form element (it may not if the form class threw an early exception).\n const form = this.modal.getRoot().find('form')[0];\n if (form) {\n Event.notifyFormSubmitAjax(form, true);\n M.core_formchangechecker.reset_form_dirty_state();\n }\n resolve();\n });\n });\n }\n\n /**\n * Wrapper for Event.notifyFormSubmitAjax that waits for the module to load\n *\n * We often destroy the form right after calling this function and we need to make sure that it actually\n * completes before it, or otherwise it will try to work with a form that does not exist.\n *\n * @param {Boolean} skipValidation\n * @return {Promise}\n */\n notifyFormSubmitAjax(skipValidation = false) {\n return new Promise(resolve => {\n Y.use('event', 'moodle-core-event', 'moodle-core-formchangechecker', () => {\n Event.notifyFormSubmitAjax(this.modal.getRoot().find('form')[0], skipValidation);\n resolve();\n });\n });\n }\n\n /**\n * Click on a \"submit\" button that is marked in the form as registerNoSubmitButton()\n *\n * @param {Element} button button that was pressed\n */\n processNoSubmitButton(button) {\n this.notifyFormSubmitAjax(true)\n .then(() => {\n // Add the button name to the form data and submit it.\n let formData = this.modal.getRoot().find('form').serialize();\n formData = formData + '&' + encodeURIComponent(button.getAttribute('name')) + '=' +\n encodeURIComponent(button.getAttribute('value'));\n\n const bodyContent = this.getBody(formData);\n this.modal.setBodyContent(bodyContent);\n bodyContent.catch(Notification.exception);\n\n return null;\n })\n .catch(null);\n }\n\n /**\n * Validate form elements\n * @return {Promise} promise that returns true if client-side validation has passed, false if there are errors\n */\n validateElements() {\n return this.notifyFormSubmitAjax()\n .then(() => {\n // Now the change events have run, see if there are any \"invalid\" form fields.\n /** @var {jQuery} list of elements with errors */\n const invalid = this.modal.getRoot().find('[aria-invalid=\"true\"], .error');\n\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (invalid.length) {\n invalid.first().focus();\n return false;\n }\n\n return true;\n });\n }\n\n /**\n * Disable buttons during form submission\n */\n disableButtons() {\n this.modal.getFooter().find('[data-action]').attr('disabled', true);\n }\n\n /**\n * Enable buttons after form submission (on validation error)\n */\n enableButtons() {\n this.modal.getFooter().find('[data-action]').removeAttr('disabled');\n }\n\n /**\n * Submit the form via AJAX call to the core_form_dynamic_form WS\n */\n async submitFormAjax() {\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (!await this.validateElements()) {\n this.trigger(this.events.CLIENT_VALIDATION_ERROR, null, false);\n return;\n }\n this.disableButtons();\n\n // Convert all the form elements values to a serialised string.\n const formData = this.modal.getRoot().find('form').serialize();\n\n // Now we can continue...\n Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: {\n formdata: formData,\n form: this.config.formClass\n }\n }])[0]\n .then((response) => {\n if (!response.submitted) {\n // Form was not submitted because validation failed.\n const promise = new Promise(\n resolve => resolve({html: response.html, js: Fragment.processCollectedJavascript(response.javascript)}));\n this.modal.setBodyContent(promise);\n this.enableButtons();\n this.trigger(this.events.SERVER_VALIDATION_ERROR);\n } else {\n // Form was submitted properly. Hide the modal and execute callback.\n const data = JSON.parse(response.data);\n return this.notifyResetFormChanges()\n .then(() => {\n const event = this.trigger(this.events.FORM_SUBMITTED, data);\n if (!event.defaultPrevented) {\n this.modal.hide();\n }\n return null;\n });\n }\n return null;\n })\n .catch(exception => this.onSubmitError(exception));\n }\n\n /**\n * Set the classes for the 'save' button.\n *\n * @method setSaveButtonClasses\n * @param {(String)} value The 'save' button classes.\n */\n setSaveButtonClasses(value) {\n const button = this.modal.getFooter().find(\"[data-action='save']\");\n if (!button) {\n throw new Error(\"Unable to find the 'save' button\");\n }\n button.removeClass().addClass(value);\n }\n}\n"],"names":["ModalForm","config","FORM_SUBMITTED","FORM_CANCELLED","CLIENT_VALIDATION_ERROR","SERVER_VALIDATION_ERROR","ERROR","NOSUBMIT_BUTTON_PRESSED","SUBMIT_BUTTON_PRESSED","CANCEL_BUTTON_PRESSED","LOADED","modal","modalConfig","removeOnClose","type","ModalFactory","types","SAVE_CANCEL","large","this","args","futureListeners","pendingPromise","Pending","serialize","obj","prefix","concat","Object","entries","map","idx","v","k","_typeof","encodeURIComponent","join","create","then","_this","formParams","bodyContent","getBody","setBodyContent","catch","Notification","exception","getRoot","on","ModalEvents","hidden","notifyResetFormChanges","destroy","returnFocus","focus","getModal","addClass","e","preventDefault","trigger","events","target","defaultPrevented","processNoSubmitButton","submitFormAjax","saveButtonText","setSaveButtonText","saveButtonClasses","setSaveButtonClasses","save","find","submit","cancel","forEach","addEventListener","show","resolve","eventName","detail","cancelable","CustomEvent","dispatchEvent","push","formDataString","params","formdata","form","formClass","Ajax","call","methodname","response","html","js","Fragment","processCollectedJavascript","javascript","Promise","use","_this2","notifyFormSubmitAjax","M","core_formchangechecker","reset_form_dirty_state","skipValidation","_this3","button","formData","_this4","getAttribute","invalid","_this5","length","first","getFooter","attr","removeAttr","validateElements","disableButtons","submitted","data","JSON","parse","_this6","hide","promise","enableButtons","onSubmitError","value","Error","removeClass"],"mappings":"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"} \ No newline at end of file diff --git a/lib/form/amd/src/dynamicform.js b/lib/form/amd/src/dynamicform.js index 2db956e4fd8..1e62132a19b 100644 --- a/lib/form/amd/src/dynamicform.js +++ b/lib/form/amd/src/dynamicform.js @@ -136,9 +136,15 @@ export default class DynamicForm { * @public */ load(args = null) { - const formData = new URLSearchParams(Object.entries(args || {})); + const serialize = function(obj, prefix = '') { + return [].concat(...Object.entries(obj).map(([idx, v]) => { + const k = prefix ? prefix + "[" + idx + "]" : idx; + return (typeof v === "object") ? serialize(v, k) : `${k}=${encodeURIComponent(v)}`; + })).join("&"); + }; + const formData = serialize(args || {}); const pendingPromise = new Pending('core_form/dynamicform:load'); - return this.getBody(formData.toString()) + return this.getBody(formData) .then((resp) => this.updateForm(resp)) .then(pendingPromise.resolve); } diff --git a/lib/form/amd/src/modalform.js b/lib/form/amd/src/modalform.js index 22036d04708..0038e9691e6 100644 --- a/lib/form/amd/src/modalform.js +++ b/lib/form/amd/src/modalform.js @@ -115,6 +115,13 @@ export default class ModalForm { */ show() { const pendingPromise = new Pending('core_form/modalform:init'); + const serialize = function(obj, prefix = '') { + return [].concat(...Object.entries(obj).map(([idx, v]) => { + const k = prefix ? prefix + "[" + idx + "]" : idx; + return (typeof v === "object") ? serialize(v, k) : `${k}=${encodeURIComponent(v)}`; + })).join("&"); + }; + return ModalFactory.create(this.config.modalConfig) .then((modal) => { this.modal = modal; @@ -122,8 +129,8 @@ export default class ModalForm { // Retrieve the form and set the modal body. We can not set the body in the modalConfig, // we need to make sure that the modal already exists when we render the form. Some form elements // such as date_selector inspect the existing elements on the page to find the highest z-index. - const formParams = new URLSearchParams(Object.entries(this.config.args || {})); - const bodyContent = this.getBody(formParams.toString()); + const formParams = serialize(this.config.args || {}); + const bodyContent = this.getBody(formParams); this.modal.setBodyContent(bodyContent); bodyContent.catch(Notification.exception);