diff --git a/admin/tool/dataprivacy/amd/build/contactdpo.min.js b/admin/tool/dataprivacy/amd/build/contactdpo.min.js index c13d9806449..0da8ec4b22a 100644 --- a/admin/tool/dataprivacy/amd/build/contactdpo.min.js +++ b/admin/tool/dataprivacy/amd/build/contactdpo.min.js @@ -5,6 +5,6 @@ define("tool_dataprivacy/contactdpo",["exports","core_form/modalform","core/noti * @module tool_dataprivacy/contactdpo * @copyright 2021 Paul Holden * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const SELECTORS_CONTACT_DPO='[data-action="contactdpo"]';_exports.init=()=>{const triggerElement=document.querySelector(SELECTORS_CONTACT_DPO);triggerElement.addEventListener("click",(event=>{event.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.get_string)("contactdataprotectionofficer","tool_dataprivacy")},formClass:"tool_dataprivacy\\form\\contactdpo",saveButtonText:(0,_str.get_string)("send","tool_dataprivacy"),returnFocus:triggerElement});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{if(event.detail.result)(0,_str.get_string)("requestsubmitted","tool_dataprivacy").then(_toast.add).catch();else{const warningMessages=event.detail.warnings.map((warning=>warning.message));_notification.default.addNotification({type:"error",message:warningMessages.join("
")})}})),modalForm.show()}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const SELECTORS_CONTACT_DPO='[data-action="contactdpo"]';_exports.init=()=>{const triggerElement=document.querySelector(SELECTORS_CONTACT_DPO);triggerElement.addEventListener("click",(event=>{event.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("contactdataprotectionofficer","tool_dataprivacy")},formClass:"tool_dataprivacy\\form\\contactdpo",saveButtonText:(0,_str.getString)("send","tool_dataprivacy"),returnFocus:triggerElement});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{if(event.detail.result)(0,_str.getString)("requestsubmitted","tool_dataprivacy").then(_toast.add).catch();else{const warningMessages=event.detail.warnings.map((warning=>warning.message));_notification.default.addNotification({type:"error",message:warningMessages.join("
")})}})),modalForm.show()}))}})); //# sourceMappingURL=contactdpo.min.js.map \ No newline at end of file diff --git a/admin/tool/dataprivacy/amd/build/contactdpo.min.js.map b/admin/tool/dataprivacy/amd/build/contactdpo.min.js.map index 680d7bb355d..d8652514cea 100644 --- a/admin/tool/dataprivacy/amd/build/contactdpo.min.js.map +++ b/admin/tool/dataprivacy/amd/build/contactdpo.min.js.map @@ -1 +1 @@ -{"version":3,"file":"contactdpo.min.js","sources":["../src/contactdpo.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 * Javascript module for contacting the site DPO\n *\n * @module tool_dataprivacy/contactdpo\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\n\nconst SELECTORS = {\n CONTACT_DPO: '[data-action=\"contactdpo\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n const triggerElement = document.querySelector(SELECTORS.CONTACT_DPO);\n\n triggerElement.addEventListener('click', event => {\n event.preventDefault();\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('contactdataprotectionofficer', 'tool_dataprivacy'),\n },\n formClass: 'tool_dataprivacy\\\\form\\\\contactdpo',\n saveButtonText: getString('send', 'tool_dataprivacy'),\n returnFocus: triggerElement,\n });\n\n // Show a toast notification when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n getString('requestsubmitted', 'tool_dataprivacy').then(addToast).catch();\n } else {\n const warningMessages = event.detail.warnings.map(warning => warning.message);\n Notification.addNotification({\n type: 'error',\n message: warningMessages.join('
')\n });\n }\n });\n\n modalForm.show();\n });\n};\n"],"names":["SELECTORS","triggerElement","document","querySelector","addEventListener","event","preventDefault","modalForm","ModalForm","modalConfig","title","formClass","saveButtonText","returnFocus","events","FORM_SUBMITTED","detail","result","then","addToast","catch","warningMessages","warnings","map","warning","message","addNotification","type","join","show"],"mappings":";;;;;;;0LA4BMA,sBACW,2CAMG,WACVC,eAAiBC,SAASC,cAAcH,uBAE9CC,eAAeG,iBAAiB,SAASC,QACrCA,MAAMC,uBAEAC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,mBAAU,+BAAgC,qBAErDC,UAAW,qCACXC,gBAAgB,mBAAU,OAAQ,oBAClCC,YAAaZ,iBAIjBM,UAAUH,iBAAiBG,UAAUO,OAAOC,gBAAgBV,WACpDA,MAAMW,OAAOC,2BACH,mBAAoB,oBAAoBC,KAAKC,YAAUC,YAC9D,OACGC,gBAAkBhB,MAAMW,OAAOM,SAASC,KAAIC,SAAWA,QAAQC,gCACxDC,gBAAgB,CACzBC,KAAM,QACNF,QAASJ,gBAAgBO,KAAK,cAK1CrB,UAAUsB"} \ No newline at end of file +{"version":3,"file":"contactdpo.min.js","sources":["../src/contactdpo.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 * Javascript module for contacting the site DPO\n *\n * @module tool_dataprivacy/contactdpo\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\n\nconst SELECTORS = {\n CONTACT_DPO: '[data-action=\"contactdpo\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n const triggerElement = document.querySelector(SELECTORS.CONTACT_DPO);\n\n triggerElement.addEventListener('click', event => {\n event.preventDefault();\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('contactdataprotectionofficer', 'tool_dataprivacy'),\n },\n formClass: 'tool_dataprivacy\\\\form\\\\contactdpo',\n saveButtonText: getString('send', 'tool_dataprivacy'),\n returnFocus: triggerElement,\n });\n\n // Show a toast notification when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n getString('requestsubmitted', 'tool_dataprivacy').then(addToast).catch();\n } else {\n const warningMessages = event.detail.warnings.map(warning => warning.message);\n Notification.addNotification({\n type: 'error',\n message: warningMessages.join('
')\n });\n }\n });\n\n modalForm.show();\n });\n};\n"],"names":["SELECTORS","triggerElement","document","querySelector","addEventListener","event","preventDefault","modalForm","ModalForm","modalConfig","title","formClass","saveButtonText","returnFocus","events","FORM_SUBMITTED","detail","result","then","addToast","catch","warningMessages","warnings","map","warning","message","addNotification","type","join","show"],"mappings":";;;;;;;0LA4BMA,sBACW,2CAMG,WACVC,eAAiBC,SAASC,cAAcH,uBAE9CC,eAAeG,iBAAiB,SAASC,QACrCA,MAAMC,uBAEAC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,+BAAgC,qBAErDC,UAAW,qCACXC,gBAAgB,kBAAU,OAAQ,oBAClCC,YAAaZ,iBAIjBM,UAAUH,iBAAiBG,UAAUO,OAAOC,gBAAgBV,WACpDA,MAAMW,OAAOC,0BACH,mBAAoB,oBAAoBC,KAAKC,YAAUC,YAC9D,OACGC,gBAAkBhB,MAAMW,OAAOM,SAASC,KAAIC,SAAWA,QAAQC,gCACxDC,gBAAgB,CACzBC,KAAM,QACNF,QAASJ,gBAAgBO,KAAK,cAK1CrB,UAAUsB"} \ No newline at end of file diff --git a/admin/tool/dataprivacy/amd/build/myrequestactions.min.js b/admin/tool/dataprivacy/amd/build/myrequestactions.min.js index ebad9a83240..c2f26c4eee6 100644 --- a/admin/tool/dataprivacy/amd/build/myrequestactions.min.js +++ b/admin/tool/dataprivacy/amd/build/myrequestactions.min.js @@ -5,6 +5,6 @@ define("tool_dataprivacy/myrequestactions",["exports","core/ajax","core/notifica * @module tool_dataprivacy/myrequestactions * @copyright 2018 Jun Pataleta * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_ajax=_interopRequireDefault(_ajax),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const SELECTORS_CANCEL_REQUEST='[data-action="cancel"][data-requestid]';_exports.init=()=>{document.addEventListener("click",(event=>{const triggerElement=event.target.closest(SELECTORS_CANCEL_REQUEST);if(null===triggerElement)return;event.preventDefault();(0,_str.get_strings)([{key:"cancelrequest",component:"tool_dataprivacy"},{key:"cancelrequestconfirmation",component:"tool_dataprivacy"}]).then((_ref=>{let[cancelRequest,cancelConfirm]=_ref;return _notification.default.confirm(cancelRequest,cancelConfirm,cancelRequest,null,(()=>{const pendingPromise=new _pending.default("tool/dataprivacy:cancelRequest"),request={methodname:"tool_dataprivacy_cancel_data_request",args:{requestid:triggerElement.dataset.requestid}};_ajax.default.call([request])[0].then((response=>(response.result?window.location.reload():_notification.default.addNotification({type:"error",message:response.warnings[0].message}),pendingPromise.resolve()))).catch(_notification.default.exception)}))})).catch()}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_ajax=_interopRequireDefault(_ajax),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const SELECTORS_CANCEL_REQUEST='[data-action="cancel"][data-requestid]';_exports.init=()=>{document.addEventListener("click",(event=>{const triggerElement=event.target.closest(SELECTORS_CANCEL_REQUEST);if(null===triggerElement)return;event.preventDefault();(0,_str.getStrings)([{key:"cancelrequest",component:"tool_dataprivacy"},{key:"cancelrequestconfirmation",component:"tool_dataprivacy"}]).then((_ref=>{let[cancelRequest,cancelConfirm]=_ref;return _notification.default.confirm(cancelRequest,cancelConfirm,cancelRequest,null,(()=>{const pendingPromise=new _pending.default("tool/dataprivacy:cancelRequest"),request={methodname:"tool_dataprivacy_cancel_data_request",args:{requestid:triggerElement.dataset.requestid}};_ajax.default.call([request])[0].then((response=>(response.result?window.location.reload():_notification.default.addNotification({type:"error",message:response.warnings[0].message}),pendingPromise.resolve()))).catch(_notification.default.exception)}))})).catch()}))}})); //# sourceMappingURL=myrequestactions.min.js.map \ No newline at end of file diff --git a/admin/tool/dataprivacy/amd/build/myrequestactions.min.js.map b/admin/tool/dataprivacy/amd/build/myrequestactions.min.js.map index ac1d3817f17..4d1f38e0849 100644 --- a/admin/tool/dataprivacy/amd/build/myrequestactions.min.js.map +++ b/admin/tool/dataprivacy/amd/build/myrequestactions.min.js.map @@ -1 +1 @@ -{"version":3,"file":"myrequestactions.min.js","sources":["../src/myrequestactions.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 * AMD module to enable users to manage their own data requests.\n *\n * @module tool_dataprivacy/myrequestactions\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {get_strings as getStrings} from 'core/str';\n\nconst SELECTORS = {\n CANCEL_REQUEST: '[data-action=\"cancel\"][data-requestid]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n document.addEventListener('click', event => {\n const triggerElement = event.target.closest(SELECTORS.CANCEL_REQUEST);\n if (triggerElement === null) {\n return;\n }\n\n event.preventDefault();\n\n const requiredStrings = [\n {key: 'cancelrequest', component: 'tool_dataprivacy'},\n {key: 'cancelrequestconfirmation', component: 'tool_dataprivacy'},\n ];\n\n getStrings(requiredStrings).then(([cancelRequest, cancelConfirm]) => {\n return Notification.confirm(cancelRequest, cancelConfirm, cancelRequest, null, () => {\n const pendingPromise = new Pending('tool/dataprivacy:cancelRequest');\n const request = {\n methodname: 'tool_dataprivacy_cancel_data_request',\n args: {requestid: triggerElement.dataset.requestid}\n };\n\n Ajax.call([request])[0].then(response => {\n if (response.result) {\n window.location.reload();\n } else {\n Notification.addNotification({\n type: 'error',\n message: response.warnings[0].message\n });\n }\n return pendingPromise.resolve();\n }).catch(Notification.exception);\n });\n }).catch();\n });\n};\n"],"names":["SELECTORS","document","addEventListener","event","triggerElement","target","closest","preventDefault","key","component","then","_ref","cancelRequest","cancelConfirm","Notification","confirm","pendingPromise","Pending","request","methodname","args","requestid","dataset","call","response","result","window","location","reload","addNotification","type","message","warnings","resolve","catch","exception"],"mappings":";;;;;;;0NA4BMA,yBACc,uDAMA,KAChBC,SAASC,iBAAiB,SAASC,cACzBC,eAAiBD,MAAME,OAAOC,QAAQN,6BACrB,OAAnBI,sBAIJD,MAAMI,sCAEkB,CACpB,CAACC,IAAK,gBAAiBC,UAAW,oBAClC,CAACD,IAAK,4BAA6BC,UAAW,sBAGtBC,MAAKC,WAAEC,cAAeC,2BACvCC,sBAAaC,QAAQH,cAAeC,cAAeD,cAAe,MAAM,WACrEI,eAAiB,IAAIC,iBAAQ,kCAC7BC,QAAU,CACZC,WAAY,uCACZC,KAAM,CAACC,UAAWjB,eAAekB,QAAQD,0BAGxCE,KAAK,CAACL,UAAU,GAAGR,MAAKc,WACrBA,SAASC,OACTC,OAAOC,SAASC,+BAEHC,gBAAgB,CACzBC,KAAM,QACNC,QAASP,SAASQ,SAAS,GAAGD,UAG/Bf,eAAeiB,aACvBC,MAAMpB,sBAAaqB,iBAE3BD"} \ No newline at end of file +{"version":3,"file":"myrequestactions.min.js","sources":["../src/myrequestactions.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 * AMD module to enable users to manage their own data requests.\n *\n * @module tool_dataprivacy/myrequestactions\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {getStrings} from 'core/str';\n\nconst SELECTORS = {\n CANCEL_REQUEST: '[data-action=\"cancel\"][data-requestid]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n document.addEventListener('click', event => {\n const triggerElement = event.target.closest(SELECTORS.CANCEL_REQUEST);\n if (triggerElement === null) {\n return;\n }\n\n event.preventDefault();\n\n const requiredStrings = [\n {key: 'cancelrequest', component: 'tool_dataprivacy'},\n {key: 'cancelrequestconfirmation', component: 'tool_dataprivacy'},\n ];\n\n getStrings(requiredStrings).then(([cancelRequest, cancelConfirm]) => {\n return Notification.confirm(cancelRequest, cancelConfirm, cancelRequest, null, () => {\n const pendingPromise = new Pending('tool/dataprivacy:cancelRequest');\n const request = {\n methodname: 'tool_dataprivacy_cancel_data_request',\n args: {requestid: triggerElement.dataset.requestid}\n };\n\n Ajax.call([request])[0].then(response => {\n if (response.result) {\n window.location.reload();\n } else {\n Notification.addNotification({\n type: 'error',\n message: response.warnings[0].message\n });\n }\n return pendingPromise.resolve();\n }).catch(Notification.exception);\n });\n }).catch();\n });\n};\n"],"names":["SELECTORS","document","addEventListener","event","triggerElement","target","closest","preventDefault","key","component","then","_ref","cancelRequest","cancelConfirm","Notification","confirm","pendingPromise","Pending","request","methodname","args","requestid","dataset","call","response","result","window","location","reload","addNotification","type","message","warnings","resolve","catch","exception"],"mappings":";;;;;;;0NA4BMA,yBACc,uDAMA,KAChBC,SAASC,iBAAiB,SAASC,cACzBC,eAAiBD,MAAME,OAAOC,QAAQN,6BACrB,OAAnBI,sBAIJD,MAAMI,qCAEkB,CACpB,CAACC,IAAK,gBAAiBC,UAAW,oBAClC,CAACD,IAAK,4BAA6BC,UAAW,sBAGtBC,MAAKC,WAAEC,cAAeC,2BACvCC,sBAAaC,QAAQH,cAAeC,cAAeD,cAAe,MAAM,WACrEI,eAAiB,IAAIC,iBAAQ,kCAC7BC,QAAU,CACZC,WAAY,uCACZC,KAAM,CAACC,UAAWjB,eAAekB,QAAQD,0BAGxCE,KAAK,CAACL,UAAU,GAAGR,MAAKc,WACrBA,SAASC,OACTC,OAAOC,SAASC,+BAEHC,gBAAgB,CACzBC,KAAM,QACNC,QAASP,SAASQ,SAAS,GAAGD,UAG/Bf,eAAeiB,aACvBC,MAAMpB,sBAAaqB,iBAE3BD"} \ No newline at end of file diff --git a/admin/tool/dataprivacy/amd/build/selectedcourses.min.js b/admin/tool/dataprivacy/amd/build/selectedcourses.min.js index 3e34e9d4279..bce8782921f 100644 --- a/admin/tool/dataprivacy/amd/build/selectedcourses.min.js +++ b/admin/tool/dataprivacy/amd/build/selectedcourses.min.js @@ -1,3 +1,3 @@ -define("tool_dataprivacy/selectedcourses",["exports","core/ajax","core/notification","core/modal_factory","core/modal_events","core/fragment","core/prefetch","core/str"],(function(_exports,_ajax,_notification,_modal_factory,_modal_events,_fragment,_prefetch,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_ajax=_interopRequireDefault(_ajax),_notification=_interopRequireDefault(_notification),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_fragment=_interopRequireDefault(_fragment),(0,_prefetch.prefetchStrings)("tool_dataprivacy",["selectcourses","approverequest","errornoselectedcourse"]);return _exports.default=class{constructor(contextId,requestId){_defineProperty(this,"contextId",0),_defineProperty(this,"requestId",0),this.contextId=contextId,this.requestId=requestId,_modal_factory.default.create({type:_modal_factory.default.types.SAVE_CANCEL,title:(0,_str.get_string)("selectcourses","tool_dataprivacy"),body:this.getBody({requestid:requestId}),large:!0,removeOnClose:!0,buttons:{save:(0,_str.get_string)("approverequest","tool_dataprivacy")}}).then((modal=>(this.modal=modal,modal))).then((modal=>(modal.getRoot().on(_modal_events.default.save,this.submitForm.bind(this)),modal.getRoot().on("submit","form",this.submitFormAjax.bind(this)),modal.show(),modal))).catch(_notification.default.exception)}getBody(formdata){const params=formdata?{jsonformdata:JSON.stringify(formdata)}:null;return _fragment.default.loadFragment("tool_dataprivacy","selectcourses_form",this.contextId,params)}submitForm(e){e.preventDefault(),this.modal.getRoot().find("form").submit()}submitFormAjax(e){e.preventDefault();let formData=this.modal.getRoot().find("form").serialize();if(-1===formData.indexOf("coursecontextids")){const customSelect=this.modal.getRoot().find(".custom-select"),invalidText=this.modal.getRoot().find(".invalid-feedback");return customSelect.addClass("is-invalid"),invalidText.attr("style","display: block"),void(0,_str.get_string)("errornoselectedcourse","tool_dataprivacy").then((value=>{invalidText.empty().append(value)})).catch(_notification.default.exception)}_ajax.default.call([{methodname:"tool_dataprivacy_submit_selected_courses_form",args:{requestid:this.requestId,jsonformdata:JSON.stringify(formData)}}])[0].then((data=>(data.warnings.length>0?this.modal.setBody(this.getBody(formData)):(this.modal.destroy(),document.location.reload()),data))).catch((error=>_notification.default.exception(error)))}},_exports.default})); +define("tool_dataprivacy/selectedcourses",["exports","core/ajax","core/notification","core/modal_factory","core/modal_events","core/fragment","core/prefetch","core/str"],(function(_exports,_ajax,_notification,_modal_factory,_modal_events,_fragment,_prefetch,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_ajax=_interopRequireDefault(_ajax),_notification=_interopRequireDefault(_notification),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_fragment=_interopRequireDefault(_fragment),(0,_prefetch.prefetchStrings)("tool_dataprivacy",["selectcourses","approverequest","errornoselectedcourse"]);return _exports.default=class{constructor(contextId,requestId){_defineProperty(this,"contextId",0),_defineProperty(this,"requestId",0),this.contextId=contextId,this.requestId=requestId,_modal_factory.default.create({type:_modal_factory.default.types.SAVE_CANCEL,title:(0,_str.getString)("selectcourses","tool_dataprivacy"),body:this.getBody({requestid:requestId}),large:!0,removeOnClose:!0,buttons:{save:(0,_str.getString)("approverequest","tool_dataprivacy")}}).then((modal=>(this.modal=modal,modal))).then((modal=>(modal.getRoot().on(_modal_events.default.save,this.submitForm.bind(this)),modal.getRoot().on("submit","form",this.submitFormAjax.bind(this)),modal.show(),modal))).catch(_notification.default.exception)}getBody(formdata){const params=formdata?{jsonformdata:JSON.stringify(formdata)}:null;return _fragment.default.loadFragment("tool_dataprivacy","selectcourses_form",this.contextId,params)}submitForm(e){e.preventDefault(),this.modal.getRoot().find("form").submit()}submitFormAjax(e){e.preventDefault();let formData=this.modal.getRoot().find("form").serialize();if(-1===formData.indexOf("coursecontextids")){const customSelect=this.modal.getRoot().find(".custom-select"),invalidText=this.modal.getRoot().find(".invalid-feedback");return customSelect.addClass("is-invalid"),invalidText.attr("style","display: block"),void(0,_str.getString)("errornoselectedcourse","tool_dataprivacy").then((value=>{invalidText.empty().append(value)})).catch(_notification.default.exception)}_ajax.default.call([{methodname:"tool_dataprivacy_submit_selected_courses_form",args:{requestid:this.requestId,jsonformdata:JSON.stringify(formData)}}])[0].then((data=>(data.warnings.length>0?this.modal.setBody(this.getBody(formData)):(this.modal.destroy(),document.location.reload()),data))).catch((error=>_notification.default.exception(error)))}},_exports.default})); //# sourceMappingURL=selectedcourses.min.js.map \ No newline at end of file diff --git a/admin/tool/dataprivacy/amd/build/selectedcourses.min.js.map b/admin/tool/dataprivacy/amd/build/selectedcourses.min.js.map index 27d12c6c1ef..e2833445b3d 100644 --- a/admin/tool/dataprivacy/amd/build/selectedcourses.min.js.map +++ b/admin/tool/dataprivacy/amd/build/selectedcourses.min.js.map @@ -1 +1 @@ -{"version":3,"file":"selectedcourses.min.js","sources":["../src/selectedcourses.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 * Selected courses.\n *\n * @module tool_dataprivacy/selectedcourses\n * @copyright 2021 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since Moodle 4.3\n */\n\nimport Ajax from 'core/ajax';\nimport Notification from 'core/notification';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Fragment from 'core/fragment';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nprefetchStrings('tool_dataprivacy', [\n 'selectcourses',\n 'approverequest',\n 'errornoselectedcourse',\n]);\n\n/**\n * Selected Courses popup modal.\n *\n */\nexport default class SelectedCourses {\n /**\n * @var {String} contextId Context ID to load the fragment.\n * @private\n */\n contextId = 0;\n\n /**\n * @var {String} requestId ID of data export request.\n * @private\n */\n requestId = 0;\n\n /**\n * Constructor\n *\n * @param {String} contextId Context ID to load the fragment.\n * @param {String} requestId ID of data export request.\n */\n constructor(contextId, requestId) {\n this.contextId = contextId;\n this.requestId = requestId;\n // Now create the modal.\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: getString('selectcourses', 'tool_dataprivacy'),\n body: this.getBody({requestid: requestId}),\n large: true,\n removeOnClose: true,\n buttons: {\n save: getString('approverequest', 'tool_dataprivacy'),\n },\n }).then((modal) => {\n this.modal = modal;\n\n return modal;\n }).then((modal) => {\n // We catch the modal save event, and use it to submit the form inside the modal.\n // Triggering a form submission will give JS validation scripts a chance to check for errors.\n modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));\n\n // We also catch the form submit event and use it to submit the form with ajax.\n modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));\n modal.show();\n return modal;\n }).catch(Notification.exception);\n }\n\n /**\n * Get body of modal.\n *\n * @method getBody\n * @param {Object} formdata\n * @private\n * @return {Promise}\n */\n getBody(formdata) {\n const params = formdata ? {jsonformdata: JSON.stringify(formdata)} : null;\n\n // Get the content of the modal.\n return Fragment.loadFragment('tool_dataprivacy', 'selectcourses_form', this.contextId, params);\n }\n\n /**\n * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.\n *\n * @method submitForm\n * @param {Event} e Form submission event.\n * @private\n */\n submitForm(e) {\n e.preventDefault();\n this.modal.getRoot().find('form').submit();\n }\n\n /**\n * Submit select courses form using ajax.\n *\n * @method submitFormAjax\n * @private\n * @param {Event} e Form submission event.\n */\n submitFormAjax(e) {\n e.preventDefault();\n\n // Convert all the form elements values to a serialised string.\n let formData = this.modal.getRoot().find('form').serialize();\n\n if (formData.indexOf('coursecontextids') === -1) {\n const customSelect = this.modal.getRoot().find('.custom-select');\n const invalidText = this.modal.getRoot().find('.invalid-feedback');\n customSelect.addClass('is-invalid');\n invalidText.attr('style', 'display: block');\n getString('errornoselectedcourse', 'tool_dataprivacy').then(value => {\n invalidText.empty().append(value);\n return;\n }).catch(Notification.exception);\n return;\n }\n\n Ajax.call([{\n methodname: 'tool_dataprivacy_submit_selected_courses_form',\n args: {requestid: this.requestId, jsonformdata: JSON.stringify(formData)},\n }])[0]\n .then((data) => {\n if (data.warnings.length > 0) {\n this.modal.setBody(this.getBody(formData));\n } else {\n this.modal.destroy();\n document.location.reload();\n }\n return data;\n })\n .catch((error) => Notification.exception(error));\n }\n}\n"],"names":["constructor","contextId","requestId","create","type","ModalFactory","types","SAVE_CANCEL","title","body","this","getBody","requestid","large","removeOnClose","buttons","save","then","modal","getRoot","on","ModalEvents","submitForm","bind","submitFormAjax","show","catch","Notification","exception","formdata","params","jsonformdata","JSON","stringify","Fragment","loadFragment","e","preventDefault","find","submit","formData","serialize","indexOf","customSelect","invalidText","addClass","attr","value","empty","append","call","methodname","args","data","warnings","length","setBody","destroy","document","location","reload","error"],"mappings":"01BAgCgB,mBAAoB,CAChC,gBACA,iBACA,wDA0BAA,YAAYC,UAAWC,4CAdX,oCAMA,QASHD,UAAYA,eACZC,UAAYA,iCAEJC,OAAO,CAChBC,KAAMC,uBAAaC,MAAMC,YACzBC,OAAO,mBAAU,gBAAiB,oBAClCC,KAAMC,KAAKC,QAAQ,CAACC,UAAWV,YAC/BW,OAAO,EACPC,eAAe,EACfC,QAAS,CACLC,MAAM,mBAAU,iBAAkB,uBAEvCC,MAAMC,aACAA,MAAQA,MAENA,SACRD,MAAMC,QAGLA,MAAMC,UAAUC,GAAGC,sBAAYL,KAAMN,KAAKY,WAAWC,KAAKb,OAG1DQ,MAAMC,UAAUC,GAAG,SAAU,OAAQV,KAAKc,eAAeD,KAAKb,OAC9DQ,MAAMO,OACCP,SACRQ,MAAMC,sBAAaC,WAW1BjB,QAAQkB,gBACEC,OAASD,SAAW,CAACE,aAAcC,KAAKC,UAAUJ,WAAa,YAG9DK,kBAASC,aAAa,mBAAoB,qBAAsBzB,KAAKT,UAAW6B,QAU3FR,WAAWc,GACPA,EAAEC,sBACGnB,MAAMC,UAAUmB,KAAK,QAAQC,SAUtCf,eAAeY,GACXA,EAAEC,qBAGEG,SAAW9B,KAAKQ,MAAMC,UAAUmB,KAAK,QAAQG,gBAEH,IAA1CD,SAASE,QAAQ,oBAA4B,OACvCC,aAAejC,KAAKQ,MAAMC,UAAUmB,KAAK,kBACzCM,YAAclC,KAAKQ,MAAMC,UAAUmB,KAAK,4BAC9CK,aAAaE,SAAS,cACtBD,YAAYE,KAAK,QAAS,0CAChB,wBAAyB,oBAAoB7B,MAAK8B,QACxDH,YAAYI,QAAQC,OAAOF,UAE5BrB,MAAMC,sBAAaC,yBAIrBsB,KAAK,CAAC,CACPC,WAAY,gDACZC,KAAM,CAACxC,UAAWF,KAAKR,UAAW6B,aAAcC,KAAKC,UAAUO,cAC/D,GACHvB,MAAMoC,OACCA,KAAKC,SAASC,OAAS,OAClBrC,MAAMsC,QAAQ9C,KAAKC,QAAQ6B,iBAE3BtB,MAAMuC,UACXC,SAASC,SAASC,UAEfP,QAEV3B,OAAOmC,OAAUlC,sBAAaC,UAAUiC"} \ No newline at end of file +{"version":3,"file":"selectedcourses.min.js","sources":["../src/selectedcourses.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 * Selected courses.\n *\n * @module tool_dataprivacy/selectedcourses\n * @copyright 2021 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since Moodle 4.3\n */\n\nimport Ajax from 'core/ajax';\nimport Notification from 'core/notification';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Fragment from 'core/fragment';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\n\nprefetchStrings('tool_dataprivacy', [\n 'selectcourses',\n 'approverequest',\n 'errornoselectedcourse',\n]);\n\n/**\n * Selected Courses popup modal.\n *\n */\nexport default class SelectedCourses {\n /**\n * @var {String} contextId Context ID to load the fragment.\n * @private\n */\n contextId = 0;\n\n /**\n * @var {String} requestId ID of data export request.\n * @private\n */\n requestId = 0;\n\n /**\n * Constructor\n *\n * @param {String} contextId Context ID to load the fragment.\n * @param {String} requestId ID of data export request.\n */\n constructor(contextId, requestId) {\n this.contextId = contextId;\n this.requestId = requestId;\n // Now create the modal.\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: getString('selectcourses', 'tool_dataprivacy'),\n body: this.getBody({requestid: requestId}),\n large: true,\n removeOnClose: true,\n buttons: {\n save: getString('approverequest', 'tool_dataprivacy'),\n },\n }).then((modal) => {\n this.modal = modal;\n\n return modal;\n }).then((modal) => {\n // We catch the modal save event, and use it to submit the form inside the modal.\n // Triggering a form submission will give JS validation scripts a chance to check for errors.\n modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));\n\n // We also catch the form submit event and use it to submit the form with ajax.\n modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));\n modal.show();\n return modal;\n }).catch(Notification.exception);\n }\n\n /**\n * Get body of modal.\n *\n * @method getBody\n * @param {Object} formdata\n * @private\n * @return {Promise}\n */\n getBody(formdata) {\n const params = formdata ? {jsonformdata: JSON.stringify(formdata)} : null;\n\n // Get the content of the modal.\n return Fragment.loadFragment('tool_dataprivacy', 'selectcourses_form', this.contextId, params);\n }\n\n /**\n * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.\n *\n * @method submitForm\n * @param {Event} e Form submission event.\n * @private\n */\n submitForm(e) {\n e.preventDefault();\n this.modal.getRoot().find('form').submit();\n }\n\n /**\n * Submit select courses form using ajax.\n *\n * @method submitFormAjax\n * @private\n * @param {Event} e Form submission event.\n */\n submitFormAjax(e) {\n e.preventDefault();\n\n // Convert all the form elements values to a serialised string.\n let formData = this.modal.getRoot().find('form').serialize();\n\n if (formData.indexOf('coursecontextids') === -1) {\n const customSelect = this.modal.getRoot().find('.custom-select');\n const invalidText = this.modal.getRoot().find('.invalid-feedback');\n customSelect.addClass('is-invalid');\n invalidText.attr('style', 'display: block');\n getString('errornoselectedcourse', 'tool_dataprivacy').then(value => {\n invalidText.empty().append(value);\n return;\n }).catch(Notification.exception);\n return;\n }\n\n Ajax.call([{\n methodname: 'tool_dataprivacy_submit_selected_courses_form',\n args: {requestid: this.requestId, jsonformdata: JSON.stringify(formData)},\n }])[0]\n .then((data) => {\n if (data.warnings.length > 0) {\n this.modal.setBody(this.getBody(formData));\n } else {\n this.modal.destroy();\n document.location.reload();\n }\n return data;\n })\n .catch((error) => Notification.exception(error));\n }\n}\n"],"names":["constructor","contextId","requestId","create","type","ModalFactory","types","SAVE_CANCEL","title","body","this","getBody","requestid","large","removeOnClose","buttons","save","then","modal","getRoot","on","ModalEvents","submitForm","bind","submitFormAjax","show","catch","Notification","exception","formdata","params","jsonformdata","JSON","stringify","Fragment","loadFragment","e","preventDefault","find","submit","formData","serialize","indexOf","customSelect","invalidText","addClass","attr","value","empty","append","call","methodname","args","data","warnings","length","setBody","destroy","document","location","reload","error"],"mappings":"01BAgCgB,mBAAoB,CAChC,gBACA,iBACA,wDA0BAA,YAAYC,UAAWC,4CAdX,oCAMA,QASHD,UAAYA,eACZC,UAAYA,iCAEJC,OAAO,CAChBC,KAAMC,uBAAaC,MAAMC,YACzBC,OAAO,kBAAU,gBAAiB,oBAClCC,KAAMC,KAAKC,QAAQ,CAACC,UAAWV,YAC/BW,OAAO,EACPC,eAAe,EACfC,QAAS,CACLC,MAAM,kBAAU,iBAAkB,uBAEvCC,MAAMC,aACAA,MAAQA,MAENA,SACRD,MAAMC,QAGLA,MAAMC,UAAUC,GAAGC,sBAAYL,KAAMN,KAAKY,WAAWC,KAAKb,OAG1DQ,MAAMC,UAAUC,GAAG,SAAU,OAAQV,KAAKc,eAAeD,KAAKb,OAC9DQ,MAAMO,OACCP,SACRQ,MAAMC,sBAAaC,WAW1BjB,QAAQkB,gBACEC,OAASD,SAAW,CAACE,aAAcC,KAAKC,UAAUJ,WAAa,YAG9DK,kBAASC,aAAa,mBAAoB,qBAAsBzB,KAAKT,UAAW6B,QAU3FR,WAAWc,GACPA,EAAEC,sBACGnB,MAAMC,UAAUmB,KAAK,QAAQC,SAUtCf,eAAeY,GACXA,EAAEC,qBAGEG,SAAW9B,KAAKQ,MAAMC,UAAUmB,KAAK,QAAQG,gBAEH,IAA1CD,SAASE,QAAQ,oBAA4B,OACvCC,aAAejC,KAAKQ,MAAMC,UAAUmB,KAAK,kBACzCM,YAAclC,KAAKQ,MAAMC,UAAUmB,KAAK,4BAC9CK,aAAaE,SAAS,cACtBD,YAAYE,KAAK,QAAS,yCAChB,wBAAyB,oBAAoB7B,MAAK8B,QACxDH,YAAYI,QAAQC,OAAOF,UAE5BrB,MAAMC,sBAAaC,yBAIrBsB,KAAK,CAAC,CACPC,WAAY,gDACZC,KAAM,CAACxC,UAAWF,KAAKR,UAAW6B,aAAcC,KAAKC,UAAUO,cAC/D,GACHvB,MAAMoC,OACCA,KAAKC,SAASC,OAAS,OAClBrC,MAAMsC,QAAQ9C,KAAKC,QAAQ6B,iBAE3BtB,MAAMuC,UACXC,SAASC,SAASC,UAEfP,QAEV3B,OAAOmC,OAAUlC,sBAAaC,UAAUiC"} \ No newline at end of file diff --git a/admin/tool/dataprivacy/amd/src/contactdpo.js b/admin/tool/dataprivacy/amd/src/contactdpo.js index e509c5f1197..8c50d868efc 100644 --- a/admin/tool/dataprivacy/amd/src/contactdpo.js +++ b/admin/tool/dataprivacy/amd/src/contactdpo.js @@ -23,7 +23,7 @@ import ModalForm from 'core_form/modalform'; import Notification from 'core/notification'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {add as addToast} from 'core/toast'; const SELECTORS = { diff --git a/admin/tool/dataprivacy/amd/src/myrequestactions.js b/admin/tool/dataprivacy/amd/src/myrequestactions.js index f1a67ef48f1..52f98fae5d4 100644 --- a/admin/tool/dataprivacy/amd/src/myrequestactions.js +++ b/admin/tool/dataprivacy/amd/src/myrequestactions.js @@ -24,7 +24,7 @@ import Ajax from 'core/ajax'; import Notification from 'core/notification'; import Pending from 'core/pending'; -import {get_strings as getStrings} from 'core/str'; +import {getStrings} from 'core/str'; const SELECTORS = { CANCEL_REQUEST: '[data-action="cancel"][data-requestid]', diff --git a/admin/tool/dataprivacy/amd/src/selectedcourses.js b/admin/tool/dataprivacy/amd/src/selectedcourses.js index 4a61b68da11..5449f4e9a9e 100644 --- a/admin/tool/dataprivacy/amd/src/selectedcourses.js +++ b/admin/tool/dataprivacy/amd/src/selectedcourses.js @@ -28,7 +28,7 @@ import ModalFactory from 'core/modal_factory'; import ModalEvents from 'core/modal_events'; import Fragment from 'core/fragment'; import {prefetchStrings} from 'core/prefetch'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; prefetchStrings('tool_dataprivacy', [ 'selectcourses', diff --git a/admin/tool/langimport/amd/build/uninstall.min.js b/admin/tool/langimport/amd/build/uninstall.min.js index ba931cdf4f6..aa07524f573 100644 --- a/admin/tool/langimport/amd/build/uninstall.min.js +++ b/admin/tool/langimport/amd/build/uninstall.min.js @@ -1,3 +1,3 @@ -define("tool_langimport/uninstall",["exports","core/str","core/notification","core/config"],(function(_exports,_str,_notification,_config){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_config=(obj=_config)&&obj.__esModule?obj:{default:obj};_exports.init=form=>{null==form||form.addEventListener("submit",(async e=>{var _form$querySelector;e.preventDefault();const selectedOptions=null===(_form$querySelector=form.querySelector("#menuuninstalllang"))||void 0===_form$querySelector?void 0:_form$querySelector.selectedOptions;if(null==selectedOptions||!selectedOptions.length){const alertStrings=await(0,_str.get_strings)(["noenglishuninstalltitle","selectlangs"].map((key=>({key:key,component:"tool_langimport"}))));return void(0,_notification.alert)(...alertStrings)}if(-1!==[...selectedOptions].map((node=>node.value)).indexOf("en")){const alertStrings=await(0,_str.get_strings)(["noenglishuninstalltitle","noenglishuninstall"].map((key=>({key:key,component:"tool_langimport"}))));return void(0,_notification.alert)(...alertStrings)}const confirmationStrings=await(0,_str.get_strings)([{key:"uninstall",component:"tool_langimport"},{key:"uninstallconfirm",component:"tool_langimport",param:[...selectedOptions].map((node=>node.textContent)).join(", ")},{key:"yes",component:"core"}]);(0,_notification.saveCancelPromise)(...confirmationStrings).then((()=>{const url=new URL(form.action);return url.searchParams.append("sesskey",_config.default.sesskey),url.searchParams.append("confirmtouninstall",[...selectedOptions].map((node=>node.value)).join("/")),form.action=url.toString(),form.submit(),!0})).catch((()=>!1))}))}})); +define("tool_langimport/uninstall",["exports","core/str","core/notification","core/config"],(function(_exports,_str,_notification,_config){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_config=(obj=_config)&&obj.__esModule?obj:{default:obj};_exports.init=form=>{null==form||form.addEventListener("submit",(async e=>{var _form$querySelector;e.preventDefault();const selectedOptions=null===(_form$querySelector=form.querySelector("#menuuninstalllang"))||void 0===_form$querySelector?void 0:_form$querySelector.selectedOptions;if(null==selectedOptions||!selectedOptions.length){const alertStrings=await(0,_str.getStrings)(["noenglishuninstalltitle","selectlangs"].map((key=>({key:key,component:"tool_langimport"}))));return void(0,_notification.alert)(...alertStrings)}if(-1!==[...selectedOptions].map((node=>node.value)).indexOf("en")){const alertStrings=await(0,_str.getStrings)(["noenglishuninstalltitle","noenglishuninstall"].map((key=>({key:key,component:"tool_langimport"}))));return void(0,_notification.alert)(...alertStrings)}const confirmationStrings=await(0,_str.getStrings)([{key:"uninstall",component:"tool_langimport"},{key:"uninstallconfirm",component:"tool_langimport",param:[...selectedOptions].map((node=>node.textContent)).join(", ")},{key:"yes",component:"core"}]);(0,_notification.saveCancelPromise)(...confirmationStrings).then((()=>{const url=new URL(form.action);return url.searchParams.append("sesskey",_config.default.sesskey),url.searchParams.append("confirmtouninstall",[...selectedOptions].map((node=>node.value)).join("/")),form.action=url.toString(),form.submit(),!0})).catch((()=>!1))}))}})); //# sourceMappingURL=uninstall.min.js.map \ No newline at end of file diff --git a/admin/tool/langimport/amd/build/uninstall.min.js.map b/admin/tool/langimport/amd/build/uninstall.min.js.map index 0e1331318b0..c399bf42a54 100644 --- a/admin/tool/langimport/amd/build/uninstall.min.js.map +++ b/admin/tool/langimport/amd/build/uninstall.min.js.map @@ -1 +1 @@ -{"version":3,"file":"uninstall.min.js","sources":["../src/uninstall.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\nimport {get_strings as getStrings} from 'core/str';\nimport {saveCancelPromise, alert as displayAlert} from \"core/notification\";\nimport MoodleConfig from 'core/config';\n\nexport const init = (form) => {\n form?.addEventListener('submit', async(e) => {\n e.preventDefault();\n const selectedOptions = form.querySelector('#menuuninstalllang')?.selectedOptions;\n if (!selectedOptions?.length) {\n const alertStrings = await getStrings(\n ['noenglishuninstalltitle', 'selectlangs'].map((key) => ({key, component: 'tool_langimport'})\n ));\n displayAlert(...alertStrings);\n return;\n }\n\n if ([...selectedOptions].map((node) => node.value).indexOf('en') !== -1) {\n const alertStrings = await getStrings(\n ['noenglishuninstalltitle', 'noenglishuninstall'].map((key) => ({key, component: 'tool_langimport'})\n ));\n displayAlert(...alertStrings);\n return;\n }\n\n const confirmationStrings = await getStrings([\n {\n key: 'uninstall',\n component: 'tool_langimport',\n },\n {\n key: 'uninstallconfirm',\n component: 'tool_langimport',\n param: [...selectedOptions].map((node) => node.textContent).join(', '),\n },\n {\n key: 'yes',\n component: 'core',\n },\n ]);\n\n saveCancelPromise(...confirmationStrings).then(() => {\n const url = new URL(form.action);\n url.searchParams.append('sesskey', MoodleConfig.sesskey);\n url.searchParams.append('confirmtouninstall', [...selectedOptions].map((node) => node.value).join('/'));\n form.action = url.toString();\n form.submit();\n return true;\n })\n .catch(() => {\n return false;\n });\n });\n};\n"],"names":["form","addEventListener","async","e","preventDefault","selectedOptions","querySelector","_form$querySelector","length","alertStrings","map","key","component","node","value","indexOf","confirmationStrings","param","textContent","join","then","url","URL","action","searchParams","append","MoodleConfig","sesskey","toString","submit","catch"],"mappings":"sSAmBqBA,OACjBA,MAAAA,MAAAA,KAAMC,iBAAiB,UAAUC,MAAAA,4BAC7BC,EAAEC,uBACIC,4CAAkBL,KAAKM,cAAc,4DAAnBC,oBAA0CF,mBAC7DA,MAAAA,kBAAAA,gBAAiBG,OAAQ,OACpBC,mBAAqB,oBACvB,CAAC,0BAA2B,eAAeC,KAAKC,OAAUA,IAAAA,IAAKC,UAAW,6DAE9DH,kBAIkD,IAAlE,IAAIJ,iBAAiBK,KAAKG,MAASA,KAAKC,QAAOC,QAAQ,MAAc,OAC/DN,mBAAqB,oBACvB,CAAC,0BAA2B,sBAAsBC,KAAKC,OAAUA,IAAAA,IAAKC,UAAW,6DAErEH,oBAIdO,0BAA4B,oBAAW,CACzC,CACIL,IAAK,YACLC,UAAW,mBAEf,CACID,IAAK,mBACLC,UAAW,kBACXK,MAAO,IAAIZ,iBAAiBK,KAAKG,MAASA,KAAKK,cAAaC,KAAK,OAErE,CACIR,IAAK,MACLC,UAAW,iDAIEI,qBAAqBI,MAAK,WACrCC,IAAM,IAAIC,IAAItB,KAAKuB,eACzBF,IAAIG,aAAaC,OAAO,UAAWC,gBAAaC,SAChDN,IAAIG,aAAaC,OAAO,qBAAsB,IAAIpB,iBAAiBK,KAAKG,MAASA,KAAKC,QAAOK,KAAK,MAClGnB,KAAKuB,OAASF,IAAIO,WAClB5B,KAAK6B,UACE,KAEVC,OAAM,KACI"} \ No newline at end of file +{"version":3,"file":"uninstall.min.js","sources":["../src/uninstall.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\nimport {getStrings} from 'core/str';\nimport {saveCancelPromise, alert as displayAlert} from \"core/notification\";\nimport MoodleConfig from 'core/config';\n\nexport const init = (form) => {\n form?.addEventListener('submit', async(e) => {\n e.preventDefault();\n const selectedOptions = form.querySelector('#menuuninstalllang')?.selectedOptions;\n if (!selectedOptions?.length) {\n const alertStrings = await getStrings(\n ['noenglishuninstalltitle', 'selectlangs'].map((key) => ({key, component: 'tool_langimport'})\n ));\n displayAlert(...alertStrings);\n return;\n }\n\n if ([...selectedOptions].map((node) => node.value).indexOf('en') !== -1) {\n const alertStrings = await getStrings(\n ['noenglishuninstalltitle', 'noenglishuninstall'].map((key) => ({key, component: 'tool_langimport'})\n ));\n displayAlert(...alertStrings);\n return;\n }\n\n const confirmationStrings = await getStrings([\n {\n key: 'uninstall',\n component: 'tool_langimport',\n },\n {\n key: 'uninstallconfirm',\n component: 'tool_langimport',\n param: [...selectedOptions].map((node) => node.textContent).join(', '),\n },\n {\n key: 'yes',\n component: 'core',\n },\n ]);\n\n saveCancelPromise(...confirmationStrings).then(() => {\n const url = new URL(form.action);\n url.searchParams.append('sesskey', MoodleConfig.sesskey);\n url.searchParams.append('confirmtouninstall', [...selectedOptions].map((node) => node.value).join('/'));\n form.action = url.toString();\n form.submit();\n return true;\n })\n .catch(() => {\n return false;\n });\n });\n};\n"],"names":["form","addEventListener","async","e","preventDefault","selectedOptions","querySelector","_form$querySelector","length","alertStrings","map","key","component","node","value","indexOf","confirmationStrings","param","textContent","join","then","url","URL","action","searchParams","append","MoodleConfig","sesskey","toString","submit","catch"],"mappings":"sSAmBqBA,OACjBA,MAAAA,MAAAA,KAAMC,iBAAiB,UAAUC,MAAAA,4BAC7BC,EAAEC,uBACIC,4CAAkBL,KAAKM,cAAc,4DAAnBC,oBAA0CF,mBAC7DA,MAAAA,kBAAAA,gBAAiBG,OAAQ,OACpBC,mBAAqB,mBACvB,CAAC,0BAA2B,eAAeC,KAAKC,OAAUA,IAAAA,IAAKC,UAAW,6DAE9DH,kBAIkD,IAAlE,IAAIJ,iBAAiBK,KAAKG,MAASA,KAAKC,QAAOC,QAAQ,MAAc,OAC/DN,mBAAqB,mBACvB,CAAC,0BAA2B,sBAAsBC,KAAKC,OAAUA,IAAAA,IAAKC,UAAW,6DAErEH,oBAIdO,0BAA4B,mBAAW,CACzC,CACIL,IAAK,YACLC,UAAW,mBAEf,CACID,IAAK,mBACLC,UAAW,kBACXK,MAAO,IAAIZ,iBAAiBK,KAAKG,MAASA,KAAKK,cAAaC,KAAK,OAErE,CACIR,IAAK,MACLC,UAAW,iDAIEI,qBAAqBI,MAAK,WACrCC,IAAM,IAAIC,IAAItB,KAAKuB,eACzBF,IAAIG,aAAaC,OAAO,UAAWC,gBAAaC,SAChDN,IAAIG,aAAaC,OAAO,qBAAsB,IAAIpB,iBAAiBK,KAAKG,MAASA,KAAKC,QAAOK,KAAK,MAClGnB,KAAKuB,OAASF,IAAIO,WAClB5B,KAAK6B,UACE,KAEVC,OAAM,KACI"} \ No newline at end of file diff --git a/admin/tool/langimport/amd/src/uninstall.js b/admin/tool/langimport/amd/src/uninstall.js index 5d2e5d241c7..dbf9ac07150 100644 --- a/admin/tool/langimport/amd/src/uninstall.js +++ b/admin/tool/langimport/amd/src/uninstall.js @@ -13,7 +13,7 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -import {get_strings as getStrings} from 'core/str'; +import {getStrings} from 'core/str'; import {saveCancelPromise, alert as displayAlert} from "core/notification"; import MoodleConfig from 'core/config'; diff --git a/admin/tool/usertours/amd/build/managesteps.min.js b/admin/tool/usertours/amd/build/managesteps.min.js index 4c8a4a1ebb4..21d0cd7a4c8 100644 --- a/admin/tool/usertours/amd/build/managesteps.min.js +++ b/admin/tool/usertours/amd/build/managesteps.min.js @@ -1,3 +1,3 @@ -define("tool_usertours/managesteps",["exports","core/prefetch","core/str","core/notification"],(function(_exports,_prefetch,_str,_notification){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setup=void 0;const removeStepHandler=e=>{const deleteButton=e.target.closest('[data-action="delete"]');deleteButton&&(e.preventDefault(),removeStepFromLink(deleteButton.href))},removeStepFromLink=targetUrl=>{(0,_notification.confirm)((0,_str.get_string)("confirmstepremovaltitle","tool_usertours"),(0,_str.get_string)("confirmstepremovalquestion","tool_usertours"),(0,_str.get_string)("yes","core"),(0,_str.get_string)("no","core"),(()=>{window.location=targetUrl}))};_exports.setup=()=>{(0,_prefetch.prefetchStrings)("tool_usertours",["confirmstepremovaltitle","confirmstepremovalquestion"]),(0,_prefetch.prefetchStrings)("core",["yes","no"]),document.querySelector("body").addEventListener("click",removeStepHandler)}})); +define("tool_usertours/managesteps",["exports","core/prefetch","core/str","core/notification"],(function(_exports,_prefetch,_str,_notification){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setup=void 0;const removeStepHandler=e=>{const deleteButton=e.target.closest('[data-action="delete"]');deleteButton&&(e.preventDefault(),removeStepFromLink(deleteButton.href))},removeStepFromLink=targetUrl=>{(0,_notification.confirm)((0,_str.getString)("confirmstepremovaltitle","tool_usertours"),(0,_str.getString)("confirmstepremovalquestion","tool_usertours"),(0,_str.getString)("yes","core"),(0,_str.getString)("no","core"),(()=>{window.location=targetUrl}))};_exports.setup=()=>{(0,_prefetch.prefetchStrings)("tool_usertours",["confirmstepremovaltitle","confirmstepremovalquestion"]),(0,_prefetch.prefetchStrings)("core",["yes","no"]),document.querySelector("body").addEventListener("click",removeStepHandler)}})); //# sourceMappingURL=managesteps.min.js.map \ No newline at end of file diff --git a/admin/tool/usertours/amd/build/managesteps.min.js.map b/admin/tool/usertours/amd/build/managesteps.min.js.map index e42c77cbf65..685ffbaf7db 100644 --- a/admin/tool/usertours/amd/build/managesteps.min.js.map +++ b/admin/tool/usertours/amd/build/managesteps.min.js.map @@ -1 +1 @@ -{"version":3,"file":"managesteps.min.js","sources":["../src/managesteps.js"],"sourcesContent":["/**\n * Step management code.\n *\n * @module tool_usertours/managesteps\n * @copyright 2016 Andrew Nicols \n */\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {confirm as confirmModal} from 'core/notification';\n\n/**\n * Handle step management actions.\n *\n * @param {Event} e\n * @private\n */\nconst removeStepHandler = e => {\n const deleteButton = e.target.closest('[data-action=\"delete\"]');\n if (deleteButton) {\n e.preventDefault();\n removeStepFromLink(deleteButton.href);\n }\n};\n\n/**\n * Handle removal of a step with confirmation.\n *\n * @param {string} targetUrl\n * @private\n */\nconst removeStepFromLink = targetUrl => {\n confirmModal(\n getString('confirmstepremovaltitle', 'tool_usertours'),\n getString('confirmstepremovalquestion', 'tool_usertours'),\n getString('yes', 'core'),\n getString('no', 'core'),\n () => {\n window.location = targetUrl;\n }\n );\n};\n\n/**\n * Set up the step management handlers.\n */\nexport const setup = () => {\n prefetchStrings('tool_usertours', [\n 'confirmstepremovaltitle',\n 'confirmstepremovalquestion',\n ]);\n\n prefetchStrings('core', [\n 'yes',\n 'no',\n ]);\n\n document.querySelector('body').addEventListener('click', removeStepHandler);\n};\n"],"names":["removeStepHandler","e","deleteButton","target","closest","preventDefault","removeStepFromLink","href","targetUrl","window","location","document","querySelector","addEventListener"],"mappings":"oOAgBMA,kBAAoBC,UAChBC,aAAeD,EAAEE,OAAOC,QAAQ,0BAClCF,eACAD,EAAEI,iBACFC,mBAAmBJ,aAAaK,QAUlCD,mBAAqBE,uCAEnB,mBAAU,0BAA2B,mBACrC,mBAAU,6BAA8B,mBACxC,mBAAU,MAAO,SACjB,mBAAU,KAAM,SAChB,KACIC,OAAOC,SAAWF,6BAQT,mCACD,iBAAkB,CAC9B,0BACA,6DAGY,OAAQ,CACpB,MACA,OAGJG,SAASC,cAAc,QAAQC,iBAAiB,QAASb"} \ No newline at end of file +{"version":3,"file":"managesteps.min.js","sources":["../src/managesteps.js"],"sourcesContent":["/**\n * Step management code.\n *\n * @module tool_usertours/managesteps\n * @copyright 2016 Andrew Nicols \n */\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport {confirm as confirmModal} from 'core/notification';\n\n/**\n * Handle step management actions.\n *\n * @param {Event} e\n * @private\n */\nconst removeStepHandler = e => {\n const deleteButton = e.target.closest('[data-action=\"delete\"]');\n if (deleteButton) {\n e.preventDefault();\n removeStepFromLink(deleteButton.href);\n }\n};\n\n/**\n * Handle removal of a step with confirmation.\n *\n * @param {string} targetUrl\n * @private\n */\nconst removeStepFromLink = targetUrl => {\n confirmModal(\n getString('confirmstepremovaltitle', 'tool_usertours'),\n getString('confirmstepremovalquestion', 'tool_usertours'),\n getString('yes', 'core'),\n getString('no', 'core'),\n () => {\n window.location = targetUrl;\n }\n );\n};\n\n/**\n * Set up the step management handlers.\n */\nexport const setup = () => {\n prefetchStrings('tool_usertours', [\n 'confirmstepremovaltitle',\n 'confirmstepremovalquestion',\n ]);\n\n prefetchStrings('core', [\n 'yes',\n 'no',\n ]);\n\n document.querySelector('body').addEventListener('click', removeStepHandler);\n};\n"],"names":["removeStepHandler","e","deleteButton","target","closest","preventDefault","removeStepFromLink","href","targetUrl","window","location","document","querySelector","addEventListener"],"mappings":"oOAgBMA,kBAAoBC,UAChBC,aAAeD,EAAEE,OAAOC,QAAQ,0BAClCF,eACAD,EAAEI,iBACFC,mBAAmBJ,aAAaK,QAUlCD,mBAAqBE,uCAEnB,kBAAU,0BAA2B,mBACrC,kBAAU,6BAA8B,mBACxC,kBAAU,MAAO,SACjB,kBAAU,KAAM,SAChB,KACIC,OAAOC,SAAWF,6BAQT,mCACD,iBAAkB,CAC9B,0BACA,6DAGY,OAAQ,CACpB,MACA,OAGJG,SAASC,cAAc,QAAQC,iBAAiB,QAASb"} \ No newline at end of file diff --git a/admin/tool/usertours/amd/build/managetours.min.js b/admin/tool/usertours/amd/build/managetours.min.js index 301aca618e5..30dd30dd6f9 100644 --- a/admin/tool/usertours/amd/build/managetours.min.js +++ b/admin/tool/usertours/amd/build/managetours.min.js @@ -1,3 +1,3 @@ -define("tool_usertours/managetours",["exports","core/prefetch","core/str","core/notification"],(function(_exports,_prefetch,_str,_notification){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setup=void 0;const removeTourHandler=e=>{const deleteButton=e.target.closest('[data-action="delete"]');deleteButton&&(e.preventDefault(),removeTourFromLink(deleteButton.href))},removeTourFromLink=targetUrl=>{(0,_notification.confirm)((0,_str.get_string)("confirmtourremovaltitle","tool_usertours"),(0,_str.get_string)("confirmtourremovalquestion","tool_usertours"),(0,_str.get_string)("yes","core"),(0,_str.get_string)("no","core"),(()=>{window.location=targetUrl}))};_exports.setup=()=>{(0,_prefetch.prefetchStrings)("tool_usertours",["confirmtourremovaltitle","confirmtourremovalquestion"]),(0,_prefetch.prefetchStrings)("core",["yes","no"]),document.querySelector("body").addEventListener("click",removeTourHandler)}})); +define("tool_usertours/managetours",["exports","core/prefetch","core/str","core/notification"],(function(_exports,_prefetch,_str,_notification){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setup=void 0;const removeTourHandler=e=>{const deleteButton=e.target.closest('[data-action="delete"]');deleteButton&&(e.preventDefault(),removeTourFromLink(deleteButton.href))},removeTourFromLink=targetUrl=>{(0,_notification.confirm)((0,_str.getString)("confirmtourremovaltitle","tool_usertours"),(0,_str.getString)("confirmtourremovalquestion","tool_usertours"),(0,_str.getString)("yes","core"),(0,_str.getString)("no","core"),(()=>{window.location=targetUrl}))};_exports.setup=()=>{(0,_prefetch.prefetchStrings)("tool_usertours",["confirmtourremovaltitle","confirmtourremovalquestion"]),(0,_prefetch.prefetchStrings)("core",["yes","no"]),document.querySelector("body").addEventListener("click",removeTourHandler)}})); //# sourceMappingURL=managetours.min.js.map \ No newline at end of file diff --git a/admin/tool/usertours/amd/build/managetours.min.js.map b/admin/tool/usertours/amd/build/managetours.min.js.map index 702cd1a661c..5da5ac3d2ee 100644 --- a/admin/tool/usertours/amd/build/managetours.min.js.map +++ b/admin/tool/usertours/amd/build/managetours.min.js.map @@ -1 +1 @@ -{"version":3,"file":"managetours.min.js","sources":["../src/managetours.js"],"sourcesContent":["/**\n * Tour management code.\n *\n * @module tool_usertours/managetours\n * @copyright 2016 Andrew Nicols \n */\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {confirm as confirmModal} from 'core/notification';\n\n/**\n * Handle tour management actions.\n *\n * @param {Event} e\n * @private\n */\nconst removeTourHandler = e => {\n const deleteButton = e.target.closest('[data-action=\"delete\"]');\n if (deleteButton) {\n e.preventDefault();\n removeTourFromLink(deleteButton.href);\n }\n};\n\n/**\n * Handle removal of a tour with confirmation.\n *\n * @param {string} targetUrl\n * @private\n */\nconst removeTourFromLink = targetUrl => {\n confirmModal(\n getString('confirmtourremovaltitle', 'tool_usertours'),\n getString('confirmtourremovalquestion', 'tool_usertours'),\n getString('yes', 'core'),\n getString('no', 'core'),\n () => {\n window.location = targetUrl;\n }\n );\n};\n\n/**\n * Set up the tour management handlers.\n */\nexport const setup = () => {\n prefetchStrings('tool_usertours', [\n 'confirmtourremovaltitle',\n 'confirmtourremovalquestion',\n ]);\n\n prefetchStrings('core', [\n 'yes',\n 'no',\n ]);\n\n document.querySelector('body').addEventListener('click', removeTourHandler);\n};\n"],"names":["removeTourHandler","e","deleteButton","target","closest","preventDefault","removeTourFromLink","href","targetUrl","window","location","document","querySelector","addEventListener"],"mappings":"oOAgBMA,kBAAoBC,UAChBC,aAAeD,EAAEE,OAAOC,QAAQ,0BAClCF,eACAD,EAAEI,iBACFC,mBAAmBJ,aAAaK,QAUlCD,mBAAqBE,uCAEnB,mBAAU,0BAA2B,mBACrC,mBAAU,6BAA8B,mBACxC,mBAAU,MAAO,SACjB,mBAAU,KAAM,SAChB,KACIC,OAAOC,SAAWF,6BAQT,mCACD,iBAAkB,CAC9B,0BACA,6DAGY,OAAQ,CACpB,MACA,OAGJG,SAASC,cAAc,QAAQC,iBAAiB,QAASb"} \ No newline at end of file +{"version":3,"file":"managetours.min.js","sources":["../src/managetours.js"],"sourcesContent":["/**\n * Tour management code.\n *\n * @module tool_usertours/managetours\n * @copyright 2016 Andrew Nicols \n */\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport {confirm as confirmModal} from 'core/notification';\n\n/**\n * Handle tour management actions.\n *\n * @param {Event} e\n * @private\n */\nconst removeTourHandler = e => {\n const deleteButton = e.target.closest('[data-action=\"delete\"]');\n if (deleteButton) {\n e.preventDefault();\n removeTourFromLink(deleteButton.href);\n }\n};\n\n/**\n * Handle removal of a tour with confirmation.\n *\n * @param {string} targetUrl\n * @private\n */\nconst removeTourFromLink = targetUrl => {\n confirmModal(\n getString('confirmtourremovaltitle', 'tool_usertours'),\n getString('confirmtourremovalquestion', 'tool_usertours'),\n getString('yes', 'core'),\n getString('no', 'core'),\n () => {\n window.location = targetUrl;\n }\n );\n};\n\n/**\n * Set up the tour management handlers.\n */\nexport const setup = () => {\n prefetchStrings('tool_usertours', [\n 'confirmtourremovaltitle',\n 'confirmtourremovalquestion',\n ]);\n\n prefetchStrings('core', [\n 'yes',\n 'no',\n ]);\n\n document.querySelector('body').addEventListener('click', removeTourHandler);\n};\n"],"names":["removeTourHandler","e","deleteButton","target","closest","preventDefault","removeTourFromLink","href","targetUrl","window","location","document","querySelector","addEventListener"],"mappings":"oOAgBMA,kBAAoBC,UAChBC,aAAeD,EAAEE,OAAOC,QAAQ,0BAClCF,eACAD,EAAEI,iBACFC,mBAAmBJ,aAAaK,QAUlCD,mBAAqBE,uCAEnB,kBAAU,0BAA2B,mBACrC,kBAAU,6BAA8B,mBACxC,kBAAU,MAAO,SACjB,kBAAU,KAAM,SAChB,KACIC,OAAOC,SAAWF,6BAQT,mCACD,iBAAkB,CAC9B,0BACA,6DAGY,OAAQ,CACpB,MACA,OAGJG,SAASC,cAAc,QAAQC,iBAAiB,QAASb"} \ No newline at end of file diff --git a/admin/tool/usertours/amd/build/tour.min.js b/admin/tool/usertours/amd/build/tour.min.js index 369daa5b773..c9931647074 100644 --- a/admin/tool/usertours/amd/build/tour.min.js +++ b/admin/tool/usertours/amd/build/tour.min.js @@ -1,3 +1,3 @@ -define("tool_usertours/tour",["exports","jquery","core/aria","core/popper","core/event_dispatcher","./events","core/str","core/prefetch","core/event"],(function(_exports,_jquery,Aria,_popper,_event_dispatcher,_events,_str,_prefetch,_event){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),Aria=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Aria),_popper=_interopRequireDefault(_popper);var _default=class{constructor(config){var obj,key,value;value=!1,(key="tourRunning")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.init(config)}init(config){this.eventHandlers={},this.reset(),this.originalConfiguration=config||{},this.configure.apply(this,arguments),this.possitionNeedToBeRecalculated=!1,this.recalculatedNo=0;try{this.storage=window.sessionStorage,this.storageKey="tourstate_"+this.tourName}catch(e){this.storage=!1,this.storageKey=""}return(0,_prefetch.prefetchStrings)("tool_usertours",["nextstep_sequence","skip_tour"]),this}reset(){return this.hide(),this.eventHandlers=[],this.resetStepListeners(),this.originalConfiguration={},this.steps=[],this.currentStepNumber=0,this}configure(config){if("object"==typeof config){if(void 0!==config.tourName&&(this.tourName=config.tourName),config.eventHandlers)for(let eventName in config.eventHandlers)config.eventHandlers[eventName].forEach((function(handler){this.addEventHandler(eventName,handler)}),this);this.resetStepDefaults(!0),"object"==typeof config.steps&&(this.steps=config.steps),void 0!==config.template&&(this.templateContent=config.template)}return this.checkMinimumRequirements(),this}checkMinimumRequirements(){if(!this.tourName)throw new Error("Tour Name required");if(!this.steps||!this.steps.length)throw new Error("Steps must be specified")}resetStepDefaults(loadOriginalConfiguration){return void 0===loadOriginalConfiguration&&(loadOriginalConfiguration=!0),this.stepDefaults={},loadOriginalConfiguration&&void 0!==this.originalConfiguration.stepDefaults?this.setStepDefaults(this.originalConfiguration.stepDefaults):this.setStepDefaults({}),this}setStepDefaults(stepDefaults){return this.stepDefaults||(this.stepDefaults={}),_jquery.default.extend(this.stepDefaults,{element:"",placement:"top",delay:0,moveOnClick:!1,moveAfterTime:0,orphan:!1,direction:1},stepDefaults),this}getCurrentStepNumber(){return parseInt(this.currentStepNumber,10)}setCurrentStepNumber(stepNumber){if(this.currentStepNumber=stepNumber,this.storage)try{this.storage.setItem(this.storageKey,stepNumber)}catch(e){e.code===DOMException.QUOTA_EXCEEDED_ERR&&this.storage.removeItem(this.storageKey)}}getNextStepNumber(stepNumber){void 0===stepNumber&&(stepNumber=this.getCurrentStepNumber());let nextStepNumber=stepNumber+1;for(;nextStepNumber<=this.steps.length;){if(this.isStepPotentiallyVisible(this.getStepConfig(nextStepNumber)))return nextStepNumber;nextStepNumber++}return null}getPreviousStepNumber(stepNumber){void 0===stepNumber&&(stepNumber=this.getCurrentStepNumber());let previousStepNumber=stepNumber-1;for(;previousStepNumber>=0;){if(this.isStepPotentiallyVisible(this.getStepConfig(previousStepNumber)))return previousStepNumber;previousStepNumber--}return null}isLastStep(stepNumber){return null===this.getNextStepNumber(stepNumber)}isStepPotentiallyVisible(stepConfig){return!!stepConfig&&(!!this.isStepActuallyVisible(stepConfig)||(!(void 0===stepConfig.orphan||!stepConfig.orphan)||!(void 0===stepConfig.delay||!stepConfig.delay)))}getPotentiallyVisibleSteps(){let position=1,result=[];for(let stepNumber=0;stepNumber=this.steps.length)return null;let stepConfig=this.normalizeStepConfig(this.steps[stepNumber]);return stepConfig=_jquery.default.extend(stepConfig,{stepNumber:stepNumber}),stepConfig}normalizeStepConfig(stepConfig){return void 0!==stepConfig.reflex&&void 0===stepConfig.moveAfterClick&&(stepConfig.moveAfterClick=stepConfig.reflex),void 0!==stepConfig.element&&void 0===stepConfig.target&&(stepConfig.target=stepConfig.element),void 0!==stepConfig.content&&void 0===stepConfig.body&&(stepConfig.body=stepConfig.content),stepConfig=_jquery.default.extend({},this.stepDefaults,stepConfig),(stepConfig=_jquery.default.extend({},{attachTo:stepConfig.target,attachPoint:"after"},stepConfig)).attachTo&&(stepConfig.attachTo=(0,_jquery.default)(stepConfig.attachTo).first()),stepConfig}getStepTarget(stepConfig){return stepConfig.target?(0,_jquery.default)(stepConfig.target):null}dispatchEvent(eventName){let detail=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},cancelable=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return(0,_event_dispatcher.dispatchEvent)(eventName,{tour:this,...detail},document,{cancelable:cancelable})}addEventHandler(eventName,handler){return void 0===this.eventHandlers[eventName]&&(this.eventHandlers[eventName]=[]),this.eventHandlers[eventName].push(handler),this}processStepListeners(stepConfig){if(this.listeners.push({node:this.currentStepNode,args:["click",'[data-role="next"]',_jquery.default.proxy(this.next,this)]},{node:this.currentStepNode,args:["click",'[data-role="end"]',_jquery.default.proxy(this.endTour,this)]},{node:(0,_jquery.default)('[data-flexitour="backdrop"]'),args:["click",_jquery.default.proxy(this.hide,this)]},{node:(0,_jquery.default)("body"),args:["keydown",_jquery.default.proxy(this.handleKeyDown,this)]}),stepConfig.moveOnClick){var targetNode=this.getStepTarget(stepConfig);this.listeners.push({node:targetNode,args:["click",_jquery.default.proxy((function(e){0===(0,_jquery.default)(e.target).parents('[data-flexitour="container"]').length&&window.setTimeout(_jquery.default.proxy(this.next,this),500)}),this)]})}return this.listeners.forEach((function(listener){listener.node.on.apply(listener.node,listener.args)})),this}resetStepListeners(){return this.listeners&&this.listeners.forEach((function(listener){listener.node.off.apply(listener.node,listener.args)})),this.listeners=[],this}renderStep(stepConfig){this.currentStepConfig=stepConfig,this.setCurrentStepNumber(stepConfig.stepNumber);let template=(0,_jquery.default)(this.getTemplateContent());template.find('[data-placeholder="title"]').html(stepConfig.title),template.find('[data-placeholder="body"]').html(stepConfig.body);const nextBtn=template.find('[data-role="next"]'),endBtn=template.find('[data-role="end"]');if(this.isLastStep(stepConfig.stepNumber)?(nextBtn.hide(),endBtn.removeClass("btn-secondary").addClass("btn-primary")):(nextBtn.prop("disabled",!1),(0,_str.get_string)("skip_tour","tool_usertours").then((value=>{endBtn.html(value)})).catch()),nextBtn.attr("role","button"),endBtn.attr("role","button"),this.originalConfiguration.displaystepnumbers){const stepsPotentiallyVisible=this.getPotentiallyVisibleSteps(),totalStepsPotentiallyVisible=stepsPotentiallyVisible.length,position=stepsPotentiallyVisible[stepConfig.stepNumber].position;totalStepsPotentiallyVisible>1&&(0,_str.get_string)("nextstep_sequence","tool_usertours",{position:position,total:totalStepsPotentiallyVisible}).then((value=>{nextBtn.html(value)})).catch()}return stepConfig.template=template,this.addStepToPage(stepConfig),this.processStepListeners(stepConfig),this}getTemplateContent(){return(0,_jquery.default)(this.templateContent).clone()}addStepToPage(stepConfig){let currentStepNode=(0,_jquery.default)('').html(stepConfig.template).hide();(0,_event.notifyFilterContentUpdated)(currentStepNode);let animationTarget=(0,_jquery.default)("body, html").stop(!0,!0);if(this.isStepActuallyVisible(stepConfig)){let targetNode=this.getStepTarget(stepConfig);targetNode.parents('[data-usertour="scroller"]').length&&(animationTarget=targetNode.parents('[data-usertour="scroller"]')),targetNode.data("flexitour","target");let zIndex=this.calculateZIndex(targetNode);zIndex&&(stepConfig.zIndex=zIndex+1),stepConfig.zIndex&¤tStepNode.css("zIndex",stepConfig.zIndex+1),this.positionBackdrop(stepConfig),(0,_jquery.default)(document.body).append(currentStepNode),this.currentStepNode=currentStepNode,this.currentStepNode.css({top:0,left:0}),animationTarget.animate({scrollTop:this.calculateScrollTop(stepConfig)}).promise().then(function(){this.positionStep(stepConfig),this.revealStep(stepConfig)}.bind(this)).catch((function(){}))}else stepConfig.orphan&&(stepConfig.isOrphan=!0,stepConfig.attachTo=(0,_jquery.default)("body").first(),stepConfig.attachPoint="append",this.positionBackdrop(stepConfig),currentStepNode.addClass("orphan"),(0,_jquery.default)(document.body).append(currentStepNode),this.currentStepNode=currentStepNode,this.currentStepNode.css("position","fixed"),this.currentStepPopper=new _popper.default((0,_jquery.default)("body"),this.currentStepNode[0],{removeOnDestroy:!0,placement:stepConfig.placement+"-start",arrowElement:'[data-role="arrow"]',modifiers:{hide:{enabled:!1},applyStyle:{onLoad:null,enabled:!1}},onCreate:()=>{const images=this.currentStepNode.find("img");images.length&&images.on("load",(()=>{this.calculateStepPositionInPage(currentStepNode)})),this.calculateStepPositionInPage(currentStepNode)}}),this.revealStep(stepConfig));return this}revealStep(stepConfig){return this.currentStepNode.fadeIn("",_jquery.default.proxy((function(){this.announceStep(stepConfig),this.currentStepNode.focus(),window.setTimeout(_jquery.default.proxy((function(){this.currentStepNode&&this.currentStepNode.focus()}),this),100)}),this)),this}announceStep(stepConfig){let stepId="tour-step-"+this.tourName+"-"+stepConfig.stepNumber;this.currentStepNode.attr("id",stepId);let bodyRegion=this.currentStepNode.find('[data-placeholder="body"]').first();bodyRegion.attr("id",stepId+"-body"),bodyRegion.attr("role","document");let headerRegion=this.currentStepNode.find('[data-placeholder="title"]').first();headerRegion.attr("id",stepId+"-title"),headerRegion.attr("aria-labelledby",stepId+"-body"),this.currentStepNode.attr("role","dialog"),this.currentStepNode.attr("tabindex",0),this.currentStepNode.attr("aria-labelledby",stepId+"-title"),this.currentStepNode.attr("aria-describedby",stepId+"-body");let target=this.getStepTarget(stepConfig);return target&&(target.data("original-tabindex",target.attr("tabindex")),target.attr("tabindex")||target.attr("tabindex",0),target.data("original-describedby",target.attr("aria-describedby")).attr("aria-describedby",stepId+"-body")),this.accessibilityShow(stepConfig),this}handleKeyDown(e){let tabbableSelector="a[href], link[href], [draggable=true], [contenteditable=true], ";switch(tabbableSelector+=":input:enabled, [tabindex], button:enabled",e.keyCode){case 27:this.endTour();break;case 9:(function(){if(!this.currentStepConfig.hasBackdrop)return;let currentIndex,nextIndex,nextNode,focusRelevant,activeElement=(0,_jquery.default)(document.activeElement),stepTarget=this.getStepTarget(this.currentStepConfig),tabbableNodes=(0,_jquery.default)(tabbableSelector),dialogContainer=(0,_jquery.default)('span[data-flexitour="container"]');if(stepTarget&&(tabbableNodes=tabbableNodes.filter((function(index,element){return null!==stepTarget&&(stepTarget.has(element).length||dialogContainer.has(element).length||stepTarget.is(element)||dialogContainer.is(element))}))),tabbableNodes.each((function(index,element){return!activeElement.is(element)||(currentIndex=index,!1)})),null!=currentIndex){let direction=1;e.shiftKey&&(direction=-1),nextIndex=currentIndex;do{nextIndex+=direction,nextNode=(0,_jquery.default)(tabbableNodes[nextIndex])}while(nextNode.length&&nextNode.is(":disabled")||nextNode.is(":hidden"));nextNode.length?(focusRelevant=nextNode.closest(stepTarget).length,focusRelevant=focusRelevant||nextNode.closest(this.currentStepNode).length):focusRelevant=!1}focusRelevant?nextNode.focus():e.shiftKey?this.currentStepNode.find(tabbableSelector).last().focus():this.currentStepConfig.isOrphan?this.currentStepNode.focus():stepTarget.focus(),e.preventDefault()}).call(this)}}startTour(startAt){if(this.storage&&void 0===startAt){let storageStartValue=this.storage.getItem(this.storageKey);if(storageStartValue){let storageStartAt=parseInt(storageStartValue,10);storageStartAt<=this.steps.length&&(startAt=storageStartAt)}}void 0===startAt&&(startAt=this.getCurrentStepNumber());return this.dispatchEvent(_events.eventTypes.tourStart,{startAt:startAt},!0).defaultPrevented||(this.gotoStep(startAt),this.tourRunning=!0,this.dispatchEvent(_events.eventTypes.tourStarted,{startAt:startAt})),this}restartTour(){return this.startTour(0)}endTour(){if(this.dispatchEvent(_events.eventTypes.tourEnd,{},!0).defaultPrevented)return this;if(this.currentStepConfig){let previousTarget=this.getStepTarget(this.currentStepConfig);previousTarget&&(previousTarget.attr("tabindex")||previousTarget.attr("tabindex","-1"),previousTarget.first().focus())}return this.hide(!0),this.tourRunning=!1,this.dispatchEvent(_events.eventTypes.tourEnded),this}hide(transition){if(this.dispatchEvent(_events.eventTypes.stepHide,{},!0).defaultPrevented)return this;if(this.currentStepNode&&this.currentStepNode.length&&(this.currentStepNode.hide(),this.currentStepPopper&&this.currentStepPopper.destroy()),this.currentStepConfig){let target=this.getStepTarget(this.currentStepConfig);target&&(target.data("original-labelledby")&&target.attr("aria-labelledby",target.data("original-labelledby")),target.data("original-describedby")&&target.attr("aria-describedby",target.data("original-describedby")),target.data("original-tabindex")?target.attr("tabindex",target.data("tabindex")):window.setTimeout((()=>{target.removeAttr("tabindex")}),400)),this.currentStepConfig=null}let fadeTime=0;if(transition&&(fadeTime=400),(0,_jquery.default)('[data-flexitour="step-background"]').remove(),(0,_jquery.default)('[data-flexitour="step-backdrop"]').removeAttr("data-flexitour"),(0,_jquery.default)('[data-flexitour="backdrop"]').fadeOut(fadeTime,(function(){(0,_jquery.default)(this).remove()})),this.currentStepNode&&this.currentStepNode.length){let stepId=this.currentStepNode.attr("id");if(stepId){let currentStepElement='[aria-describedby="'+stepId+'-body"]';(0,_jquery.default)(currentStepElement).removeAttr("tabindex"),(0,_jquery.default)(currentStepElement).removeAttr("aria-describedby")}}return this.resetStepListeners(),this.accessibilityHide(),this.dispatchEvent(_events.eventTypes.stepHidden),this.currentStepNode=null,this.currentStepPopper=null,this}show(){let startAt=this.getCurrentStepNumber();return this.gotoStep(startAt)}getStepContainer(){return(0,_jquery.default)(this.currentStepNode)}calculateScrollTop(stepConfig){let viewportHeight=(0,_jquery.default)(window).height(),targetNode=this.getStepTarget(stepConfig),scrollParent=(0,_jquery.default)(window);targetNode.parents('[data-usertour="scroller"]').length&&(scrollParent=targetNode.parents('[data-usertour="scroller"]'));let scrollTop=scrollParent.scrollTop();return scrollTop="top"===stepConfig.placement?targetNode.offset().top-viewportHeight/2:"bottom"===stepConfig.placement?targetNode.offset().top+targetNode.height()+scrollTop-viewportHeight/2:targetNode.height()<=.8*viewportHeight?targetNode.offset().top-(viewportHeight-targetNode.height())/2:targetNode.offset().top-.2*viewportHeight,scrollTop=Math.max(0,scrollTop),scrollTop=Math.min((0,_jquery.default)(document).height()-viewportHeight,scrollTop),Math.ceil(scrollTop)}calculateStepPositionInPage(currentStepNode){let top=10;const viewportHeight=(0,_jquery.default)(window).height(),stepHeight=currentStepNode.height(),viewportWidth=(0,_jquery.default)(window).width(),stepWidth=currentStepNode.width();if(viewportHeight>=stepHeight+20)top=Math.ceil((viewportHeight-stepHeight)/2);else{var _currentStepNode$find,_currentStepNode$find2;const maxHeight=viewportHeight-20-(null!==(_currentStepNode$find=currentStepNode.find(".modal-header").first().outerHeight())&&void 0!==_currentStepNode$find?_currentStepNode$find:0)-(null!==(_currentStepNode$find2=currentStepNode.find(".modal-footer").first().outerHeight())&&void 0!==_currentStepNode$find2?_currentStepNode$find2:0);currentStepNode.find('[data-placeholder="body"]').first().css({"max-height":maxHeight+"px",overflow:"auto"})}currentStepNode.offset({top:top,left:Math.ceil((viewportWidth-stepWidth)/2)})}positionStep(stepConfig){let flipBehavior,content=this.currentStepNode,thisT=this;if(!content||!content.length)return this;switch(stepConfig.placement=this.recalculatePlacement(stepConfig),stepConfig.placement){case"left":flipBehavior=["left","right","top","bottom"];break;case"right":flipBehavior=["right","left","top","bottom"];break;case"top":flipBehavior=["top","bottom","right","left"];break;case"bottom":flipBehavior=["bottom","top","right","left"];break;default:flipBehavior="flip"}let target=this.getStepTarget(stepConfig);var config={placement:stepConfig.placement+"-start",removeOnDestroy:!0,modifiers:{flip:{behaviour:flipBehavior},arrow:{element:'[data-role="arrow"]'}},onCreate:function(data){recalculateArrowPosition(data),recalculateStepPosition(data)},onUpdate:function(data){recalculateArrowPosition(data),thisT.possitionNeedToBeRecalculated&&(thisT.recalculatedNo++,thisT.possitionNeedToBeRecalculated=!1,recalculateStepPosition(data))}};let recalculateArrowPosition=function(data){let placement=data.placement.split("-")[0];const isVertical=-1!==["left","right"].indexOf(placement),arrowElement=data.instance.popper.querySelector('[data-role="arrow"]'),stepElement=(0,_jquery.default)(data.instance.popper.querySelector('[data-role="flexitour-step"]'));if(isVertical){let arrowHeight=parseFloat(window.getComputedStyle(arrowElement).height),arrowOffset=parseFloat(window.getComputedStyle(arrowElement).top),popperHeight=parseFloat(window.getComputedStyle(data.instance.popper).height),popperOffset=parseFloat(window.getComputedStyle(data.instance.popper).top),popperBorderWidth=parseFloat(stepElement.css("borderTopWidth")),popperBorderRadiusWidth=2*parseFloat(stepElement.css("borderTopLeftRadius")),arrowPos=arrowOffset+arrowHeight/2,maxPos=popperHeight+popperOffset-popperBorderWidth-popperBorderRadiusWidth,minPos=popperOffset+popperBorderWidth+popperBorderRadiusWidth;if(arrowPos>=maxPos||arrowPos<=minPos){let newArrowPos=0;newArrowPos=arrowPos>popperHeight/2?maxPos-arrowHeight:minPos+arrowHeight,(0,_jquery.default)(arrowElement).css("top",newArrowPos)}}else{let arrowWidth=parseFloat(window.getComputedStyle(arrowElement).width),arrowOffset=parseFloat(window.getComputedStyle(arrowElement).left),popperWidth=parseFloat(window.getComputedStyle(data.instance.popper).width),popperOffset=parseFloat(window.getComputedStyle(data.instance.popper).left),popperBorderWidth=parseFloat(stepElement.css("borderTopWidth")),popperBorderRadiusWidth=2*parseFloat(stepElement.css("borderTopLeftRadius")),arrowPos=arrowOffset+arrowWidth/2,maxPos=popperWidth+popperOffset-popperBorderWidth-popperBorderRadiusWidth,minPos=popperOffset+popperBorderWidth+popperBorderRadiusWidth;if(arrowPos>=maxPos||arrowPos<=minPos){let newArrowPos=0;newArrowPos=arrowPos>popperWidth/2?maxPos-arrowWidth:minPos+arrowWidth,(0,_jquery.default)(arrowElement).css("left",newArrowPos)}}};const recalculateStepPosition=function(data){var _headerEle$outerHeigh,_footerEle$outerHeigh;const placement=data.placement.split("-")[0],isVertical=-1!==["left","right"].indexOf(placement),popperElement=(0,_jquery.default)(data.instance.popper),targetElement=(0,_jquery.default)(data.instance.reference),arrowElement=popperElement.find('[data-role="arrow"]'),stepElement=popperElement.find('[data-role="flexitour-step"]'),viewportHeight=(0,_jquery.default)(window).height(),viewportWidth=(0,_jquery.default)(window).width(),arrowHeight=parseFloat(arrowElement.outerHeight(!0)),popperHeight=parseFloat(popperElement.outerHeight(!0)),targetHeight=parseFloat(targetElement.outerHeight(!0)),arrowWidth=parseFloat(arrowElement.outerWidth(!0)),popperWidth=parseFloat(popperElement.outerWidth(!0)),targetWidth=parseFloat(targetElement.outerWidth(!0));let maxHeight;if(thisT.recalculatedNo>1&&(thisT.currentStepPopper.options.placement=isVertical?"auto-left":"auto-bottom"),thisT.recalculatedNo>2)return;if(isVertical){const leftSpace=targetElement.offset().left>0?targetElement.offset().left:0,rightSpace=viewportWidth-leftSpace-targetWidth,remainingSpace=leftSpace>=rightSpace?leftSpace:rightSpace;if(maxHeight=viewportHeight-20,remainingSpace0&&(popperElement.css({"max-width":maxWidth+"px"}),thisT.possitionNeedToBeRecalculated=!0)}else maxHeight0?targetElement.offset().top:0,bottomSpace=viewportHeight-topSpace-targetHeight,remainingSpace=topSpace>=bottomSpace?topSpace:bottomSpace;maxHeight=remainingSpace-10-arrowHeight,remainingSpace0?(headerEle.removeClass("minimal"),footerEle.removeClass("minimal"),currentStepBody.css({"max-height":maxHeight+"px",overflow:"auto"})):(headerEle.addClass("minimal"),footerEle.addClass("minimal")),thisT.currentStepPopper.update()};let background=(0,_jquery.default)('[data-flexitour="step-background"]');return background.length&&(target=background),this.currentStepPopper=new _popper.default(target,content[0],config),this}recalculatePlacement(stepConfig){let target=this.getStepTarget(stepConfig),widthContent=this.currentStepNode.width()+16,targetOffsetLeft=target.offset().left-10,targetOffsetRight=target.offset().left+target.width()+10,placement=stepConfig.placement;return-1!==["left","right"].indexOf(placement)&&targetOffsetLeftdocument.documentElement.clientWidth&&(placement="top"),placement}positionBackdrop(stepConfig){if(stepConfig.backdrop){this.currentStepConfig.hasBackdrop=!0;let backdrop=(0,_jquery.default)('
');if(stepConfig.zIndex?"append"===stepConfig.attachPoint?stepConfig.attachTo.append(backdrop):backdrop.insertAfter(stepConfig.attachTo):(0,_jquery.default)("body").append(backdrop),this.isStepActuallyVisible(stepConfig)){let background=(0,_jquery.default)('[data-flexitour="step-background"]');background.length||(background=(0,_jquery.default)('
'));let targetNode=this.getStepTarget(stepConfig),buffer=10,colorNode=targetNode;buffer&&(colorNode=(0,_jquery.default)("body"));let drawertop=0;if(targetNode.parents('[data-usertour="scroller"]').length){const scrollerElement=targetNode.parents('[data-usertour="scroller"]'),navigationBuffer=scrollerElement.offset().top;scrollerElement.scrollTop()>=navigationBuffer&&(drawertop=scrollerElement.scrollTop()-navigationBuffer,background.css({position:"fixed"}))}background.css({width:targetNode.outerWidth()+buffer+buffer,height:targetNode.outerHeight()+buffer+buffer,left:targetNode.offset().left-buffer,top:targetNode.offset().top+drawertop-buffer,backgroundColor:this.calculateInherittedBackgroundColor(colorNode)}),targetNode.offset().left").hide();(0,_jquery.default)("body").append(fakeNode);let fakeElemColor=fakeNode.css("backgroundColor");for(fakeNode.remove(),elem=(0,_jquery.default)(elem);elem.length&&elem[0]!==document;){let color=elem.css("backgroundColor");if(color!==fakeElemColor)return color;elem=elem.parent()}return null}calculatePosition(elem){for(elem=(0,_jquery.default)(elem);elem.length&&elem[0]!==document;){let position=elem.css("position");if("static"!==position)return position;elem=elem.parent()}return null}accessibilityShow(){let hideFunction=function(child){let flexitourRole=child.data("flexitour");if(flexitourRole)switch(flexitourRole){case"container":case"target":return}child.attr("aria-hidden")||(child.attr("data-has-hidden",!0),Aria.hide(child))};this.currentStepNode.siblings().each((function(index,node){hideFunction((0,_jquery.default)(node))})),this.currentStepNode.parentsUntil("body").siblings().each((function(index,node){hideFunction((0,_jquery.default)(node))}))}accessibilityHide(){(0,_jquery.default)("[data-has-hidden]").each((function(index,node){var child;void 0!==(child=(0,_jquery.default)(node)).attr("data-has-hidden")&&(child.removeAttr("data-has-hidden"),Aria.unhide(child))}))}};return _exports.default=_default,_exports.default})); +define("tool_usertours/tour",["exports","jquery","core/aria","core/popper","core/event_dispatcher","./events","core/str","core/prefetch","core/event"],(function(_exports,_jquery,Aria,_popper,_event_dispatcher,_events,_str,_prefetch,_event){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),Aria=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Aria),_popper=_interopRequireDefault(_popper);var _default=class{constructor(config){var obj,key,value;value=!1,(key="tourRunning")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.init(config)}init(config){this.eventHandlers={},this.reset(),this.originalConfiguration=config||{},this.configure.apply(this,arguments),this.possitionNeedToBeRecalculated=!1,this.recalculatedNo=0;try{this.storage=window.sessionStorage,this.storageKey="tourstate_"+this.tourName}catch(e){this.storage=!1,this.storageKey=""}return(0,_prefetch.prefetchStrings)("tool_usertours",["nextstep_sequence","skip_tour"]),this}reset(){return this.hide(),this.eventHandlers=[],this.resetStepListeners(),this.originalConfiguration={},this.steps=[],this.currentStepNumber=0,this}configure(config){if("object"==typeof config){if(void 0!==config.tourName&&(this.tourName=config.tourName),config.eventHandlers)for(let eventName in config.eventHandlers)config.eventHandlers[eventName].forEach((function(handler){this.addEventHandler(eventName,handler)}),this);this.resetStepDefaults(!0),"object"==typeof config.steps&&(this.steps=config.steps),void 0!==config.template&&(this.templateContent=config.template)}return this.checkMinimumRequirements(),this}checkMinimumRequirements(){if(!this.tourName)throw new Error("Tour Name required");if(!this.steps||!this.steps.length)throw new Error("Steps must be specified")}resetStepDefaults(loadOriginalConfiguration){return void 0===loadOriginalConfiguration&&(loadOriginalConfiguration=!0),this.stepDefaults={},loadOriginalConfiguration&&void 0!==this.originalConfiguration.stepDefaults?this.setStepDefaults(this.originalConfiguration.stepDefaults):this.setStepDefaults({}),this}setStepDefaults(stepDefaults){return this.stepDefaults||(this.stepDefaults={}),_jquery.default.extend(this.stepDefaults,{element:"",placement:"top",delay:0,moveOnClick:!1,moveAfterTime:0,orphan:!1,direction:1},stepDefaults),this}getCurrentStepNumber(){return parseInt(this.currentStepNumber,10)}setCurrentStepNumber(stepNumber){if(this.currentStepNumber=stepNumber,this.storage)try{this.storage.setItem(this.storageKey,stepNumber)}catch(e){e.code===DOMException.QUOTA_EXCEEDED_ERR&&this.storage.removeItem(this.storageKey)}}getNextStepNumber(stepNumber){void 0===stepNumber&&(stepNumber=this.getCurrentStepNumber());let nextStepNumber=stepNumber+1;for(;nextStepNumber<=this.steps.length;){if(this.isStepPotentiallyVisible(this.getStepConfig(nextStepNumber)))return nextStepNumber;nextStepNumber++}return null}getPreviousStepNumber(stepNumber){void 0===stepNumber&&(stepNumber=this.getCurrentStepNumber());let previousStepNumber=stepNumber-1;for(;previousStepNumber>=0;){if(this.isStepPotentiallyVisible(this.getStepConfig(previousStepNumber)))return previousStepNumber;previousStepNumber--}return null}isLastStep(stepNumber){return null===this.getNextStepNumber(stepNumber)}isStepPotentiallyVisible(stepConfig){return!!stepConfig&&(!!this.isStepActuallyVisible(stepConfig)||(!(void 0===stepConfig.orphan||!stepConfig.orphan)||!(void 0===stepConfig.delay||!stepConfig.delay)))}getPotentiallyVisibleSteps(){let position=1,result=[];for(let stepNumber=0;stepNumber=this.steps.length)return null;let stepConfig=this.normalizeStepConfig(this.steps[stepNumber]);return stepConfig=_jquery.default.extend(stepConfig,{stepNumber:stepNumber}),stepConfig}normalizeStepConfig(stepConfig){return void 0!==stepConfig.reflex&&void 0===stepConfig.moveAfterClick&&(stepConfig.moveAfterClick=stepConfig.reflex),void 0!==stepConfig.element&&void 0===stepConfig.target&&(stepConfig.target=stepConfig.element),void 0!==stepConfig.content&&void 0===stepConfig.body&&(stepConfig.body=stepConfig.content),stepConfig=_jquery.default.extend({},this.stepDefaults,stepConfig),(stepConfig=_jquery.default.extend({},{attachTo:stepConfig.target,attachPoint:"after"},stepConfig)).attachTo&&(stepConfig.attachTo=(0,_jquery.default)(stepConfig.attachTo).first()),stepConfig}getStepTarget(stepConfig){return stepConfig.target?(0,_jquery.default)(stepConfig.target):null}dispatchEvent(eventName){let detail=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},cancelable=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return(0,_event_dispatcher.dispatchEvent)(eventName,{tour:this,...detail},document,{cancelable:cancelable})}addEventHandler(eventName,handler){return void 0===this.eventHandlers[eventName]&&(this.eventHandlers[eventName]=[]),this.eventHandlers[eventName].push(handler),this}processStepListeners(stepConfig){if(this.listeners.push({node:this.currentStepNode,args:["click",'[data-role="next"]',_jquery.default.proxy(this.next,this)]},{node:this.currentStepNode,args:["click",'[data-role="end"]',_jquery.default.proxy(this.endTour,this)]},{node:(0,_jquery.default)('[data-flexitour="backdrop"]'),args:["click",_jquery.default.proxy(this.hide,this)]},{node:(0,_jquery.default)("body"),args:["keydown",_jquery.default.proxy(this.handleKeyDown,this)]}),stepConfig.moveOnClick){var targetNode=this.getStepTarget(stepConfig);this.listeners.push({node:targetNode,args:["click",_jquery.default.proxy((function(e){0===(0,_jquery.default)(e.target).parents('[data-flexitour="container"]').length&&window.setTimeout(_jquery.default.proxy(this.next,this),500)}),this)]})}return this.listeners.forEach((function(listener){listener.node.on.apply(listener.node,listener.args)})),this}resetStepListeners(){return this.listeners&&this.listeners.forEach((function(listener){listener.node.off.apply(listener.node,listener.args)})),this.listeners=[],this}renderStep(stepConfig){this.currentStepConfig=stepConfig,this.setCurrentStepNumber(stepConfig.stepNumber);let template=(0,_jquery.default)(this.getTemplateContent());template.find('[data-placeholder="title"]').html(stepConfig.title),template.find('[data-placeholder="body"]').html(stepConfig.body);const nextBtn=template.find('[data-role="next"]'),endBtn=template.find('[data-role="end"]');if(this.isLastStep(stepConfig.stepNumber)?(nextBtn.hide(),endBtn.removeClass("btn-secondary").addClass("btn-primary")):(nextBtn.prop("disabled",!1),(0,_str.getString)("skip_tour","tool_usertours").then((value=>{endBtn.html(value)})).catch()),nextBtn.attr("role","button"),endBtn.attr("role","button"),this.originalConfiguration.displaystepnumbers){const stepsPotentiallyVisible=this.getPotentiallyVisibleSteps(),totalStepsPotentiallyVisible=stepsPotentiallyVisible.length,position=stepsPotentiallyVisible[stepConfig.stepNumber].position;totalStepsPotentiallyVisible>1&&(0,_str.getString)("nextstep_sequence","tool_usertours",{position:position,total:totalStepsPotentiallyVisible}).then((value=>{nextBtn.html(value)})).catch()}return stepConfig.template=template,this.addStepToPage(stepConfig),this.processStepListeners(stepConfig),this}getTemplateContent(){return(0,_jquery.default)(this.templateContent).clone()}addStepToPage(stepConfig){let currentStepNode=(0,_jquery.default)('').html(stepConfig.template).hide();(0,_event.notifyFilterContentUpdated)(currentStepNode);let animationTarget=(0,_jquery.default)("body, html").stop(!0,!0);if(this.isStepActuallyVisible(stepConfig)){let targetNode=this.getStepTarget(stepConfig);targetNode.parents('[data-usertour="scroller"]').length&&(animationTarget=targetNode.parents('[data-usertour="scroller"]')),targetNode.data("flexitour","target");let zIndex=this.calculateZIndex(targetNode);zIndex&&(stepConfig.zIndex=zIndex+1),stepConfig.zIndex&¤tStepNode.css("zIndex",stepConfig.zIndex+1),this.positionBackdrop(stepConfig),(0,_jquery.default)(document.body).append(currentStepNode),this.currentStepNode=currentStepNode,this.currentStepNode.css({top:0,left:0}),animationTarget.animate({scrollTop:this.calculateScrollTop(stepConfig)}).promise().then(function(){this.positionStep(stepConfig),this.revealStep(stepConfig)}.bind(this)).catch((function(){}))}else stepConfig.orphan&&(stepConfig.isOrphan=!0,stepConfig.attachTo=(0,_jquery.default)("body").first(),stepConfig.attachPoint="append",this.positionBackdrop(stepConfig),currentStepNode.addClass("orphan"),(0,_jquery.default)(document.body).append(currentStepNode),this.currentStepNode=currentStepNode,this.currentStepNode.css("position","fixed"),this.currentStepPopper=new _popper.default((0,_jquery.default)("body"),this.currentStepNode[0],{removeOnDestroy:!0,placement:stepConfig.placement+"-start",arrowElement:'[data-role="arrow"]',modifiers:{hide:{enabled:!1},applyStyle:{onLoad:null,enabled:!1}},onCreate:()=>{const images=this.currentStepNode.find("img");images.length&&images.on("load",(()=>{this.calculateStepPositionInPage(currentStepNode)})),this.calculateStepPositionInPage(currentStepNode)}}),this.revealStep(stepConfig));return this}revealStep(stepConfig){return this.currentStepNode.fadeIn("",_jquery.default.proxy((function(){this.announceStep(stepConfig),this.currentStepNode.focus(),window.setTimeout(_jquery.default.proxy((function(){this.currentStepNode&&this.currentStepNode.focus()}),this),100)}),this)),this}announceStep(stepConfig){let stepId="tour-step-"+this.tourName+"-"+stepConfig.stepNumber;this.currentStepNode.attr("id",stepId);let bodyRegion=this.currentStepNode.find('[data-placeholder="body"]').first();bodyRegion.attr("id",stepId+"-body"),bodyRegion.attr("role","document");let headerRegion=this.currentStepNode.find('[data-placeholder="title"]').first();headerRegion.attr("id",stepId+"-title"),headerRegion.attr("aria-labelledby",stepId+"-body"),this.currentStepNode.attr("role","dialog"),this.currentStepNode.attr("tabindex",0),this.currentStepNode.attr("aria-labelledby",stepId+"-title"),this.currentStepNode.attr("aria-describedby",stepId+"-body");let target=this.getStepTarget(stepConfig);return target&&(target.data("original-tabindex",target.attr("tabindex")),target.attr("tabindex")||target.attr("tabindex",0),target.data("original-describedby",target.attr("aria-describedby")).attr("aria-describedby",stepId+"-body")),this.accessibilityShow(stepConfig),this}handleKeyDown(e){let tabbableSelector="a[href], link[href], [draggable=true], [contenteditable=true], ";switch(tabbableSelector+=":input:enabled, [tabindex], button:enabled",e.keyCode){case 27:this.endTour();break;case 9:(function(){if(!this.currentStepConfig.hasBackdrop)return;let currentIndex,nextIndex,nextNode,focusRelevant,activeElement=(0,_jquery.default)(document.activeElement),stepTarget=this.getStepTarget(this.currentStepConfig),tabbableNodes=(0,_jquery.default)(tabbableSelector),dialogContainer=(0,_jquery.default)('span[data-flexitour="container"]');if(stepTarget&&(tabbableNodes=tabbableNodes.filter((function(index,element){return null!==stepTarget&&(stepTarget.has(element).length||dialogContainer.has(element).length||stepTarget.is(element)||dialogContainer.is(element))}))),tabbableNodes.each((function(index,element){return!activeElement.is(element)||(currentIndex=index,!1)})),null!=currentIndex){let direction=1;e.shiftKey&&(direction=-1),nextIndex=currentIndex;do{nextIndex+=direction,nextNode=(0,_jquery.default)(tabbableNodes[nextIndex])}while(nextNode.length&&nextNode.is(":disabled")||nextNode.is(":hidden"));nextNode.length?(focusRelevant=nextNode.closest(stepTarget).length,focusRelevant=focusRelevant||nextNode.closest(this.currentStepNode).length):focusRelevant=!1}focusRelevant?nextNode.focus():e.shiftKey?this.currentStepNode.find(tabbableSelector).last().focus():this.currentStepConfig.isOrphan?this.currentStepNode.focus():stepTarget.focus(),e.preventDefault()}).call(this)}}startTour(startAt){if(this.storage&&void 0===startAt){let storageStartValue=this.storage.getItem(this.storageKey);if(storageStartValue){let storageStartAt=parseInt(storageStartValue,10);storageStartAt<=this.steps.length&&(startAt=storageStartAt)}}void 0===startAt&&(startAt=this.getCurrentStepNumber());return this.dispatchEvent(_events.eventTypes.tourStart,{startAt:startAt},!0).defaultPrevented||(this.gotoStep(startAt),this.tourRunning=!0,this.dispatchEvent(_events.eventTypes.tourStarted,{startAt:startAt})),this}restartTour(){return this.startTour(0)}endTour(){if(this.dispatchEvent(_events.eventTypes.tourEnd,{},!0).defaultPrevented)return this;if(this.currentStepConfig){let previousTarget=this.getStepTarget(this.currentStepConfig);previousTarget&&(previousTarget.attr("tabindex")||previousTarget.attr("tabindex","-1"),previousTarget.first().focus())}return this.hide(!0),this.tourRunning=!1,this.dispatchEvent(_events.eventTypes.tourEnded),this}hide(transition){if(this.dispatchEvent(_events.eventTypes.stepHide,{},!0).defaultPrevented)return this;if(this.currentStepNode&&this.currentStepNode.length&&(this.currentStepNode.hide(),this.currentStepPopper&&this.currentStepPopper.destroy()),this.currentStepConfig){let target=this.getStepTarget(this.currentStepConfig);target&&(target.data("original-labelledby")&&target.attr("aria-labelledby",target.data("original-labelledby")),target.data("original-describedby")&&target.attr("aria-describedby",target.data("original-describedby")),target.data("original-tabindex")?target.attr("tabindex",target.data("tabindex")):window.setTimeout((()=>{target.removeAttr("tabindex")}),400)),this.currentStepConfig=null}let fadeTime=0;if(transition&&(fadeTime=400),(0,_jquery.default)('[data-flexitour="step-background"]').remove(),(0,_jquery.default)('[data-flexitour="step-backdrop"]').removeAttr("data-flexitour"),(0,_jquery.default)('[data-flexitour="backdrop"]').fadeOut(fadeTime,(function(){(0,_jquery.default)(this).remove()})),this.currentStepNode&&this.currentStepNode.length){let stepId=this.currentStepNode.attr("id");if(stepId){let currentStepElement='[aria-describedby="'+stepId+'-body"]';(0,_jquery.default)(currentStepElement).removeAttr("tabindex"),(0,_jquery.default)(currentStepElement).removeAttr("aria-describedby")}}return this.resetStepListeners(),this.accessibilityHide(),this.dispatchEvent(_events.eventTypes.stepHidden),this.currentStepNode=null,this.currentStepPopper=null,this}show(){let startAt=this.getCurrentStepNumber();return this.gotoStep(startAt)}getStepContainer(){return(0,_jquery.default)(this.currentStepNode)}calculateScrollTop(stepConfig){let viewportHeight=(0,_jquery.default)(window).height(),targetNode=this.getStepTarget(stepConfig),scrollParent=(0,_jquery.default)(window);targetNode.parents('[data-usertour="scroller"]').length&&(scrollParent=targetNode.parents('[data-usertour="scroller"]'));let scrollTop=scrollParent.scrollTop();return scrollTop="top"===stepConfig.placement?targetNode.offset().top-viewportHeight/2:"bottom"===stepConfig.placement?targetNode.offset().top+targetNode.height()+scrollTop-viewportHeight/2:targetNode.height()<=.8*viewportHeight?targetNode.offset().top-(viewportHeight-targetNode.height())/2:targetNode.offset().top-.2*viewportHeight,scrollTop=Math.max(0,scrollTop),scrollTop=Math.min((0,_jquery.default)(document).height()-viewportHeight,scrollTop),Math.ceil(scrollTop)}calculateStepPositionInPage(currentStepNode){let top=10;const viewportHeight=(0,_jquery.default)(window).height(),stepHeight=currentStepNode.height(),viewportWidth=(0,_jquery.default)(window).width(),stepWidth=currentStepNode.width();if(viewportHeight>=stepHeight+20)top=Math.ceil((viewportHeight-stepHeight)/2);else{var _currentStepNode$find,_currentStepNode$find2;const maxHeight=viewportHeight-20-(null!==(_currentStepNode$find=currentStepNode.find(".modal-header").first().outerHeight())&&void 0!==_currentStepNode$find?_currentStepNode$find:0)-(null!==(_currentStepNode$find2=currentStepNode.find(".modal-footer").first().outerHeight())&&void 0!==_currentStepNode$find2?_currentStepNode$find2:0);currentStepNode.find('[data-placeholder="body"]').first().css({"max-height":maxHeight+"px",overflow:"auto"})}currentStepNode.offset({top:top,left:Math.ceil((viewportWidth-stepWidth)/2)})}positionStep(stepConfig){let flipBehavior,content=this.currentStepNode,thisT=this;if(!content||!content.length)return this;switch(stepConfig.placement=this.recalculatePlacement(stepConfig),stepConfig.placement){case"left":flipBehavior=["left","right","top","bottom"];break;case"right":flipBehavior=["right","left","top","bottom"];break;case"top":flipBehavior=["top","bottom","right","left"];break;case"bottom":flipBehavior=["bottom","top","right","left"];break;default:flipBehavior="flip"}let target=this.getStepTarget(stepConfig);var config={placement:stepConfig.placement+"-start",removeOnDestroy:!0,modifiers:{flip:{behaviour:flipBehavior},arrow:{element:'[data-role="arrow"]'}},onCreate:function(data){recalculateArrowPosition(data),recalculateStepPosition(data)},onUpdate:function(data){recalculateArrowPosition(data),thisT.possitionNeedToBeRecalculated&&(thisT.recalculatedNo++,thisT.possitionNeedToBeRecalculated=!1,recalculateStepPosition(data))}};let recalculateArrowPosition=function(data){let placement=data.placement.split("-")[0];const isVertical=-1!==["left","right"].indexOf(placement),arrowElement=data.instance.popper.querySelector('[data-role="arrow"]'),stepElement=(0,_jquery.default)(data.instance.popper.querySelector('[data-role="flexitour-step"]'));if(isVertical){let arrowHeight=parseFloat(window.getComputedStyle(arrowElement).height),arrowOffset=parseFloat(window.getComputedStyle(arrowElement).top),popperHeight=parseFloat(window.getComputedStyle(data.instance.popper).height),popperOffset=parseFloat(window.getComputedStyle(data.instance.popper).top),popperBorderWidth=parseFloat(stepElement.css("borderTopWidth")),popperBorderRadiusWidth=2*parseFloat(stepElement.css("borderTopLeftRadius")),arrowPos=arrowOffset+arrowHeight/2,maxPos=popperHeight+popperOffset-popperBorderWidth-popperBorderRadiusWidth,minPos=popperOffset+popperBorderWidth+popperBorderRadiusWidth;if(arrowPos>=maxPos||arrowPos<=minPos){let newArrowPos=0;newArrowPos=arrowPos>popperHeight/2?maxPos-arrowHeight:minPos+arrowHeight,(0,_jquery.default)(arrowElement).css("top",newArrowPos)}}else{let arrowWidth=parseFloat(window.getComputedStyle(arrowElement).width),arrowOffset=parseFloat(window.getComputedStyle(arrowElement).left),popperWidth=parseFloat(window.getComputedStyle(data.instance.popper).width),popperOffset=parseFloat(window.getComputedStyle(data.instance.popper).left),popperBorderWidth=parseFloat(stepElement.css("borderTopWidth")),popperBorderRadiusWidth=2*parseFloat(stepElement.css("borderTopLeftRadius")),arrowPos=arrowOffset+arrowWidth/2,maxPos=popperWidth+popperOffset-popperBorderWidth-popperBorderRadiusWidth,minPos=popperOffset+popperBorderWidth+popperBorderRadiusWidth;if(arrowPos>=maxPos||arrowPos<=minPos){let newArrowPos=0;newArrowPos=arrowPos>popperWidth/2?maxPos-arrowWidth:minPos+arrowWidth,(0,_jquery.default)(arrowElement).css("left",newArrowPos)}}};const recalculateStepPosition=function(data){var _headerEle$outerHeigh,_footerEle$outerHeigh;const placement=data.placement.split("-")[0],isVertical=-1!==["left","right"].indexOf(placement),popperElement=(0,_jquery.default)(data.instance.popper),targetElement=(0,_jquery.default)(data.instance.reference),arrowElement=popperElement.find('[data-role="arrow"]'),stepElement=popperElement.find('[data-role="flexitour-step"]'),viewportHeight=(0,_jquery.default)(window).height(),viewportWidth=(0,_jquery.default)(window).width(),arrowHeight=parseFloat(arrowElement.outerHeight(!0)),popperHeight=parseFloat(popperElement.outerHeight(!0)),targetHeight=parseFloat(targetElement.outerHeight(!0)),arrowWidth=parseFloat(arrowElement.outerWidth(!0)),popperWidth=parseFloat(popperElement.outerWidth(!0)),targetWidth=parseFloat(targetElement.outerWidth(!0));let maxHeight;if(thisT.recalculatedNo>1&&(thisT.currentStepPopper.options.placement=isVertical?"auto-left":"auto-bottom"),thisT.recalculatedNo>2)return;if(isVertical){const leftSpace=targetElement.offset().left>0?targetElement.offset().left:0,rightSpace=viewportWidth-leftSpace-targetWidth,remainingSpace=leftSpace>=rightSpace?leftSpace:rightSpace;if(maxHeight=viewportHeight-20,remainingSpace0&&(popperElement.css({"max-width":maxWidth+"px"}),thisT.possitionNeedToBeRecalculated=!0)}else maxHeight0?targetElement.offset().top:0,bottomSpace=viewportHeight-topSpace-targetHeight,remainingSpace=topSpace>=bottomSpace?topSpace:bottomSpace;maxHeight=remainingSpace-10-arrowHeight,remainingSpace0?(headerEle.removeClass("minimal"),footerEle.removeClass("minimal"),currentStepBody.css({"max-height":maxHeight+"px",overflow:"auto"})):(headerEle.addClass("minimal"),footerEle.addClass("minimal")),thisT.currentStepPopper.update()};let background=(0,_jquery.default)('[data-flexitour="step-background"]');return background.length&&(target=background),this.currentStepPopper=new _popper.default(target,content[0],config),this}recalculatePlacement(stepConfig){let target=this.getStepTarget(stepConfig),widthContent=this.currentStepNode.width()+16,targetOffsetLeft=target.offset().left-10,targetOffsetRight=target.offset().left+target.width()+10,placement=stepConfig.placement;return-1!==["left","right"].indexOf(placement)&&targetOffsetLeftdocument.documentElement.clientWidth&&(placement="top"),placement}positionBackdrop(stepConfig){if(stepConfig.backdrop){this.currentStepConfig.hasBackdrop=!0;let backdrop=(0,_jquery.default)('
');if(stepConfig.zIndex?"append"===stepConfig.attachPoint?stepConfig.attachTo.append(backdrop):backdrop.insertAfter(stepConfig.attachTo):(0,_jquery.default)("body").append(backdrop),this.isStepActuallyVisible(stepConfig)){let background=(0,_jquery.default)('[data-flexitour="step-background"]');background.length||(background=(0,_jquery.default)('
'));let targetNode=this.getStepTarget(stepConfig),buffer=10,colorNode=targetNode;buffer&&(colorNode=(0,_jquery.default)("body"));let drawertop=0;if(targetNode.parents('[data-usertour="scroller"]').length){const scrollerElement=targetNode.parents('[data-usertour="scroller"]'),navigationBuffer=scrollerElement.offset().top;scrollerElement.scrollTop()>=navigationBuffer&&(drawertop=scrollerElement.scrollTop()-navigationBuffer,background.css({position:"fixed"}))}background.css({width:targetNode.outerWidth()+buffer+buffer,height:targetNode.outerHeight()+buffer+buffer,left:targetNode.offset().left-buffer,top:targetNode.offset().top+drawertop-buffer,backgroundColor:this.calculateInherittedBackgroundColor(colorNode)}),targetNode.offset().left").hide();(0,_jquery.default)("body").append(fakeNode);let fakeElemColor=fakeNode.css("backgroundColor");for(fakeNode.remove(),elem=(0,_jquery.default)(elem);elem.length&&elem[0]!==document;){let color=elem.css("backgroundColor");if(color!==fakeElemColor)return color;elem=elem.parent()}return null}calculatePosition(elem){for(elem=(0,_jquery.default)(elem);elem.length&&elem[0]!==document;){let position=elem.css("position");if("static"!==position)return position;elem=elem.parent()}return null}accessibilityShow(){let hideFunction=function(child){let flexitourRole=child.data("flexitour");if(flexitourRole)switch(flexitourRole){case"container":case"target":return}child.attr("aria-hidden")||(child.attr("data-has-hidden",!0),Aria.hide(child))};this.currentStepNode.siblings().each((function(index,node){hideFunction((0,_jquery.default)(node))})),this.currentStepNode.parentsUntil("body").siblings().each((function(index,node){hideFunction((0,_jquery.default)(node))}))}accessibilityHide(){(0,_jquery.default)("[data-has-hidden]").each((function(index,node){var child;void 0!==(child=(0,_jquery.default)(node)).attr("data-has-hidden")&&(child.removeAttr("data-has-hidden"),Aria.unhide(child))}))}};return _exports.default=_default,_exports.default})); //# sourceMappingURL=tour.min.js.map \ No newline at end of file diff --git a/admin/tool/usertours/amd/build/tour.min.js.map b/admin/tool/usertours/amd/build/tour.min.js.map index 625ac201d78..b9519c52aea 100644 --- a/admin/tool/usertours/amd/build/tour.min.js.map +++ b/admin/tool/usertours/amd/build/tour.min.js.map @@ -1 +1 @@ -{"version":3,"file":"tour.min.js","sources":["../src/tour.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 * A user tour.\n *\n * @module tool_usertours/tour\n * @copyright 2018 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A list of steps.\n *\n * @typedef {Object[]} StepList\n * @property {Number} stepId The id of the step in the database\n * @property {Number} position The position of the step within the tour (zero-indexed)\n */\n\nimport $ from 'jquery';\nimport * as Aria from 'core/aria';\nimport Popper from 'core/popper';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport {eventTypes} from './events';\nimport {get_string as getString} from 'core/str';\nimport {prefetchStrings} from 'core/prefetch';\nimport {notifyFilterContentUpdated} from 'core/event';\n\n/**\n * The minimum spacing for tour step to display.\n *\n * @private\n * @constant\n * @type {number}\n */\nconst MINSPACING = 10;\n\n/**\n * A user tour.\n *\n * @class tool_usertours/tour\n * @property {boolean} tourRunning Whether the tour is currently running.\n */\nconst Tour = class {\n tourRunning = false;\n\n /**\n * @param {object} config The configuration object.\n */\n constructor(config) {\n this.init(config);\n }\n\n /**\n * Initialise the tour.\n *\n * @method init\n * @param {Object} config The configuration object.\n * @chainable\n * @return {Object} this.\n */\n init(config) {\n // Unset all handlers.\n this.eventHandlers = {};\n\n // Reset the current tour states.\n this.reset();\n\n // Store the initial configuration.\n this.originalConfiguration = config || {};\n\n // Apply configuration.\n this.configure.apply(this, arguments);\n\n // Unset recalculate state.\n this.possitionNeedToBeRecalculated = false;\n\n // Unset recalculate count.\n this.recalculatedNo = 0;\n\n try {\n this.storage = window.sessionStorage;\n this.storageKey = 'tourstate_' + this.tourName;\n } catch (e) {\n this.storage = false;\n this.storageKey = '';\n }\n\n prefetchStrings('tool_usertours', [\n 'nextstep_sequence',\n 'skip_tour'\n ]);\n\n return this;\n }\n\n /**\n * Reset the current tour state.\n *\n * @method reset\n * @chainable\n * @return {Object} this.\n */\n reset() {\n // Hide the current step.\n this.hide();\n\n // Unset all handlers.\n this.eventHandlers = [];\n\n // Unset all listeners.\n this.resetStepListeners();\n\n // Unset the original configuration.\n this.originalConfiguration = {};\n\n // Reset the current step number and list of steps.\n this.steps = [];\n\n // Reset the current step number.\n this.currentStepNumber = 0;\n\n return this;\n }\n\n /**\n * Prepare tour configuration.\n *\n * @method configure\n * @param {Object} config The configuration object.\n * @chainable\n * @return {Object} this.\n */\n configure(config) {\n if (typeof config === 'object') {\n // Tour name.\n if (typeof config.tourName !== 'undefined') {\n this.tourName = config.tourName;\n }\n\n // Set up eventHandlers.\n if (config.eventHandlers) {\n for (let eventName in config.eventHandlers) {\n config.eventHandlers[eventName].forEach(function(handler) {\n this.addEventHandler(eventName, handler);\n }, this);\n }\n }\n\n // Reset the step configuration.\n this.resetStepDefaults(true);\n\n // Configure the steps.\n if (typeof config.steps === 'object') {\n this.steps = config.steps;\n }\n\n if (typeof config.template !== 'undefined') {\n this.templateContent = config.template;\n }\n }\n\n // Check that we have enough to start the tour.\n this.checkMinimumRequirements();\n\n return this;\n }\n\n /**\n * Check that the configuration meets the minimum requirements.\n *\n * @method checkMinimumRequirements\n */\n checkMinimumRequirements() {\n // Need a tourName.\n if (!this.tourName) {\n throw new Error(\"Tour Name required\");\n }\n\n // Need a minimum of one step.\n if (!this.steps || !this.steps.length) {\n throw new Error(\"Steps must be specified\");\n }\n }\n\n /**\n * Reset step default configuration.\n *\n * @method resetStepDefaults\n * @param {Boolean} loadOriginalConfiguration Whether to load the original configuration supplied with the Tour.\n * @chainable\n * @return {Object} this.\n */\n resetStepDefaults(loadOriginalConfiguration) {\n if (typeof loadOriginalConfiguration === 'undefined') {\n loadOriginalConfiguration = true;\n }\n\n this.stepDefaults = {};\n if (!loadOriginalConfiguration || typeof this.originalConfiguration.stepDefaults === 'undefined') {\n this.setStepDefaults({});\n } else {\n this.setStepDefaults(this.originalConfiguration.stepDefaults);\n }\n\n return this;\n }\n\n /**\n * Set the step defaults.\n *\n * @method setStepDefaults\n * @param {Object} stepDefaults The step defaults to apply to all steps\n * @chainable\n * @return {Object} this.\n */\n setStepDefaults(stepDefaults) {\n if (!this.stepDefaults) {\n this.stepDefaults = {};\n }\n $.extend(\n this.stepDefaults,\n {\n element: '',\n placement: 'top',\n delay: 0,\n moveOnClick: false,\n moveAfterTime: 0,\n orphan: false,\n direction: 1,\n },\n stepDefaults\n );\n\n return this;\n }\n\n /**\n * Retrieve the current step number.\n *\n * @method getCurrentStepNumber\n * @return {Number} The current step number\n */\n getCurrentStepNumber() {\n return parseInt(this.currentStepNumber, 10);\n }\n\n /**\n * Store the current step number.\n *\n * @method setCurrentStepNumber\n * @param {Number} stepNumber The current step number\n * @chainable\n */\n setCurrentStepNumber(stepNumber) {\n this.currentStepNumber = stepNumber;\n if (this.storage) {\n try {\n this.storage.setItem(this.storageKey, stepNumber);\n } catch (e) {\n if (e.code === DOMException.QUOTA_EXCEEDED_ERR) {\n this.storage.removeItem(this.storageKey);\n }\n }\n }\n }\n\n /**\n * Get the next step number after the currently displayed step.\n *\n * @method getNextStepNumber\n * @param {Number} stepNumber The current step number\n * @return {Number} The next step number to display\n */\n getNextStepNumber(stepNumber) {\n if (typeof stepNumber === 'undefined') {\n stepNumber = this.getCurrentStepNumber();\n }\n let nextStepNumber = stepNumber + 1;\n\n // Keep checking the remaining steps.\n while (nextStepNumber <= this.steps.length) {\n if (this.isStepPotentiallyVisible(this.getStepConfig(nextStepNumber))) {\n return nextStepNumber;\n }\n nextStepNumber++;\n }\n\n return null;\n }\n\n /**\n * Get the previous step number before the currently displayed step.\n *\n * @method getPreviousStepNumber\n * @param {Number} stepNumber The current step number\n * @return {Number} The previous step number to display\n */\n getPreviousStepNumber(stepNumber) {\n if (typeof stepNumber === 'undefined') {\n stepNumber = this.getCurrentStepNumber();\n }\n let previousStepNumber = stepNumber - 1;\n\n // Keep checking the remaining steps.\n while (previousStepNumber >= 0) {\n if (this.isStepPotentiallyVisible(this.getStepConfig(previousStepNumber))) {\n return previousStepNumber;\n }\n previousStepNumber--;\n }\n\n return null;\n }\n\n /**\n * Is the step the final step number?\n *\n * @method isLastStep\n * @param {Number} stepNumber Step number to test\n * @return {Boolean} Whether the step is the final step\n */\n isLastStep(stepNumber) {\n let nextStepNumber = this.getNextStepNumber(stepNumber);\n\n return nextStepNumber === null;\n }\n\n /**\n * Is this step potentially visible?\n *\n * @method isStepPotentiallyVisible\n * @param {Object} stepConfig The step configuration to normalise\n * @return {Boolean} Whether the step is the potentially visible\n */\n isStepPotentiallyVisible(stepConfig) {\n if (!stepConfig) {\n // Without step config, there can be no step.\n return false;\n }\n\n if (this.isStepActuallyVisible(stepConfig)) {\n // If it is actually visible, it is already potentially visible.\n return true;\n }\n\n if (typeof stepConfig.orphan !== 'undefined' && stepConfig.orphan) {\n // Orphan steps have no target. They are always visible.\n return true;\n }\n\n if (typeof stepConfig.delay !== 'undefined' && stepConfig.delay) {\n // Only return true if the activated has not been used yet.\n return true;\n }\n\n // Not theoretically, or actually visible.\n return false;\n }\n\n /**\n * Get potentially visible steps in a tour.\n *\n * @returns {StepList} A list of ordered steps\n */\n getPotentiallyVisibleSteps() {\n let position = 1;\n let result = [];\n // Checking the total steps.\n for (let stepNumber = 0; stepNumber < this.steps.length; stepNumber++) {\n const stepConfig = this.getStepConfig(stepNumber);\n if (this.isStepPotentiallyVisible(stepConfig)) {\n result[stepNumber] = {stepId: stepConfig.stepid, position: position};\n position++;\n }\n }\n\n return result;\n }\n\n /**\n * Is this step actually visible?\n *\n * @method isStepActuallyVisible\n * @param {Object} stepConfig The step configuration to normalise\n * @return {Boolean} Whether the step is actually visible\n */\n isStepActuallyVisible(stepConfig) {\n if (!stepConfig) {\n // Without step config, there can be no step.\n return false;\n }\n\n // Check if the CSS styles are allowed on the browser or not.\n if (!this.isCSSAllowed()) {\n return false;\n }\n\n let target = this.getStepTarget(stepConfig);\n if (target && target.length && target.is(':visible')) {\n // Without a target, there can be no step.\n return !!target.length;\n }\n\n return false;\n }\n\n /**\n * Is the browser actually allow CSS styles?\n *\n * @returns {boolean} True if the browser is allowing CSS styles\n */\n isCSSAllowed() {\n const testCSSElement = document.createElement('div');\n testCSSElement.classList.add('hide');\n document.body.appendChild(testCSSElement);\n const styles = window.getComputedStyle(testCSSElement);\n const isAllowed = styles.display === 'none';\n testCSSElement.remove();\n\n return isAllowed;\n }\n\n /**\n * Go to the next step in the tour.\n *\n * @method next\n * @chainable\n * @return {Object} this.\n */\n next() {\n return this.gotoStep(this.getNextStepNumber());\n }\n\n /**\n * Go to the previous step in the tour.\n *\n * @method previous\n * @chainable\n * @return {Object} this.\n */\n previous() {\n return this.gotoStep(this.getPreviousStepNumber(), -1);\n }\n\n /**\n * Go to the specified step in the tour.\n *\n * @method gotoStep\n * @param {Number} stepNumber The step number to display\n * @param {Number} direction Next or previous step\n * @chainable\n * @return {Object} this.\n * @fires tool_usertours/stepRender\n * @fires tool_usertours/stepRendered\n * @fires tool_usertours/stepHide\n * @fires tool_usertours/stepHidden\n */\n gotoStep(stepNumber, direction) {\n if (stepNumber < 0) {\n return this.endTour();\n }\n\n let stepConfig = this.getStepConfig(stepNumber);\n if (stepConfig === null) {\n return this.endTour();\n }\n\n return this._gotoStep(stepConfig, direction);\n }\n\n _gotoStep(stepConfig, direction) {\n if (!stepConfig) {\n return this.endTour();\n }\n\n if (typeof stepConfig.delay !== 'undefined' && stepConfig.delay && !stepConfig.delayed) {\n stepConfig.delayed = true;\n window.setTimeout(this._gotoStep.bind(this), stepConfig.delay, stepConfig, direction);\n\n return this;\n } else if (!stepConfig.orphan && !this.isStepActuallyVisible(stepConfig)) {\n let fn = direction == -1 ? 'getPreviousStepNumber' : 'getNextStepNumber';\n return this.gotoStep(this[fn](stepConfig.stepNumber), direction);\n }\n\n this.hide();\n\n const stepRenderEvent = this.dispatchEvent(eventTypes.stepRender, {stepConfig}, true);\n if (!stepRenderEvent.defaultPrevented) {\n this.renderStep(stepConfig);\n this.dispatchEvent(eventTypes.stepRendered, {stepConfig});\n }\n\n return this;\n }\n\n /**\n * Fetch the normalised step configuration for the specified step number.\n *\n * @method getStepConfig\n * @param {Number} stepNumber The step number to fetch configuration for\n * @return {Object} The step configuration\n */\n getStepConfig(stepNumber) {\n if (stepNumber === null || stepNumber < 0 || stepNumber >= this.steps.length) {\n return null;\n }\n\n // Normalise the step configuration.\n let stepConfig = this.normalizeStepConfig(this.steps[stepNumber]);\n\n // Add the stepNumber to the stepConfig.\n stepConfig = $.extend(stepConfig, {stepNumber: stepNumber});\n\n return stepConfig;\n }\n\n /**\n * Normalise the supplied step configuration.\n *\n * @method normalizeStepConfig\n * @param {Object} stepConfig The step configuration to normalise\n * @return {Object} The normalised step configuration\n */\n normalizeStepConfig(stepConfig) {\n\n if (typeof stepConfig.reflex !== 'undefined' && typeof stepConfig.moveAfterClick === 'undefined') {\n stepConfig.moveAfterClick = stepConfig.reflex;\n }\n\n if (typeof stepConfig.element !== 'undefined' && typeof stepConfig.target === 'undefined') {\n stepConfig.target = stepConfig.element;\n }\n\n if (typeof stepConfig.content !== 'undefined' && typeof stepConfig.body === 'undefined') {\n stepConfig.body = stepConfig.content;\n }\n\n stepConfig = $.extend({}, this.stepDefaults, stepConfig);\n\n stepConfig = $.extend({}, {\n attachTo: stepConfig.target,\n attachPoint: 'after',\n }, stepConfig);\n\n if (stepConfig.attachTo) {\n stepConfig.attachTo = $(stepConfig.attachTo).first();\n }\n\n return stepConfig;\n }\n\n /**\n * Fetch the actual step target from the selector.\n *\n * This should not be called until after any delay has completed.\n *\n * @method getStepTarget\n * @param {Object} stepConfig The step configuration\n * @return {$}\n */\n getStepTarget(stepConfig) {\n if (stepConfig.target) {\n return $(stepConfig.target);\n }\n\n return null;\n }\n\n /**\n * Fire any event handlers for the specified event.\n *\n * @param {String} eventName The name of the event\n * @param {Object} [detail={}] Any additional details to pass into the eveent\n * @param {Boolean} [cancelable=false] Whether preventDefault() can be called\n * @returns {CustomEvent}\n */\n dispatchEvent(\n eventName,\n detail = {},\n cancelable = false\n ) {\n return dispatchEvent(eventName, {\n // Add the tour to the detail.\n tour: this,\n ...detail,\n }, document, {\n cancelable,\n });\n }\n\n /**\n * @method addEventHandler\n * @param {string} eventName The name of the event to listen for\n * @param {function} handler The event handler to call\n * @return {Object} this.\n */\n addEventHandler(eventName, handler) {\n if (typeof this.eventHandlers[eventName] === 'undefined') {\n this.eventHandlers[eventName] = [];\n }\n\n this.eventHandlers[eventName].push(handler);\n\n return this;\n }\n\n /**\n * Process listeners for the step being shown.\n *\n * @method processStepListeners\n * @param {object} stepConfig The configuration for the step\n * @chainable\n * @return {Object} this.\n */\n processStepListeners(stepConfig) {\n this.listeners.push(\n // Next button.\n {\n node: this.currentStepNode,\n args: ['click', '[data-role=\"next\"]', $.proxy(this.next, this)]\n },\n\n // Close and end tour buttons.\n {\n node: this.currentStepNode,\n args: ['click', '[data-role=\"end\"]', $.proxy(this.endTour, this)]\n },\n\n // Click backdrop and hide tour.\n {\n node: $('[data-flexitour=\"backdrop\"]'),\n args: ['click', $.proxy(this.hide, this)]\n },\n\n // Keypresses.\n {\n node: $('body'),\n args: ['keydown', $.proxy(this.handleKeyDown, this)]\n });\n\n if (stepConfig.moveOnClick) {\n var targetNode = this.getStepTarget(stepConfig);\n this.listeners.push({\n node: targetNode,\n args: ['click', $.proxy(function(e) {\n if ($(e.target).parents('[data-flexitour=\"container\"]').length === 0) {\n // Ignore clicks when they are in the flexitour.\n window.setTimeout($.proxy(this.next, this), 500);\n }\n }, this)]\n });\n }\n\n this.listeners.forEach(function(listener) {\n listener.node.on.apply(listener.node, listener.args);\n });\n\n return this;\n }\n\n /**\n * Reset step listeners.\n *\n * @method resetStepListeners\n * @chainable\n * @return {Object} this.\n */\n resetStepListeners() {\n // Stop listening to all external handlers.\n if (this.listeners) {\n this.listeners.forEach(function(listener) {\n listener.node.off.apply(listener.node, listener.args);\n });\n }\n this.listeners = [];\n\n return this;\n }\n\n /**\n * The standard step renderer.\n *\n * @method renderStep\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n renderStep(stepConfig) {\n // Store the current step configuration for later.\n this.currentStepConfig = stepConfig;\n this.setCurrentStepNumber(stepConfig.stepNumber);\n\n // Fetch the template and convert it to a $ object.\n let template = $(this.getTemplateContent());\n\n // Title.\n template.find('[data-placeholder=\"title\"]')\n .html(stepConfig.title);\n\n // Body.\n template.find('[data-placeholder=\"body\"]')\n .html(stepConfig.body);\n\n // Buttons.\n const nextBtn = template.find('[data-role=\"next\"]');\n const endBtn = template.find('[data-role=\"end\"]');\n\n // Is this the final step?\n if (this.isLastStep(stepConfig.stepNumber)) {\n nextBtn.hide();\n endBtn.removeClass(\"btn-secondary\").addClass(\"btn-primary\");\n } else {\n nextBtn.prop('disabled', false);\n // Use Skip tour label for the End tour button.\n getString('skip_tour', 'tool_usertours').then(value => {\n endBtn.html(value);\n return;\n }).catch();\n }\n\n nextBtn.attr('role', 'button');\n endBtn.attr('role', 'button');\n\n if (this.originalConfiguration.displaystepnumbers) {\n const stepsPotentiallyVisible = this.getPotentiallyVisibleSteps();\n const totalStepsPotentiallyVisible = stepsPotentiallyVisible.length;\n const position = stepsPotentiallyVisible[stepConfig.stepNumber].position;\n if (totalStepsPotentiallyVisible > 1) {\n // Change the label of the Next button to include the sequence.\n getString('nextstep_sequence', 'tool_usertours',\n {position: position, total: totalStepsPotentiallyVisible}).then(value => {\n nextBtn.html(value);\n return;\n }).catch();\n }\n }\n\n // Replace the template with the updated version.\n stepConfig.template = template;\n\n // Add to the page.\n this.addStepToPage(stepConfig);\n\n // Process step listeners after adding to the page.\n // This uses the currentNode.\n this.processStepListeners(stepConfig);\n\n return this;\n }\n\n /**\n * Getter for the template content.\n *\n * @method getTemplateContent\n * @return {$}\n */\n getTemplateContent() {\n return $(this.templateContent).clone();\n }\n\n /**\n * Helper to add a step to the page.\n *\n * @method addStepToPage\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n addStepToPage(stepConfig) {\n // Create the stepNode from the template data.\n let currentStepNode = $('')\n .html(stepConfig.template)\n .hide();\n // Trigger the Moodle filters.\n notifyFilterContentUpdated(currentStepNode);\n\n // The scroll animation occurs on the body or html.\n let animationTarget = $('body, html')\n .stop(true, true);\n\n if (this.isStepActuallyVisible(stepConfig)) {\n let targetNode = this.getStepTarget(stepConfig);\n\n if (targetNode.parents('[data-usertour=\"scroller\"]').length) {\n animationTarget = targetNode.parents('[data-usertour=\"scroller\"]');\n }\n\n targetNode.data('flexitour', 'target');\n\n let zIndex = this.calculateZIndex(targetNode);\n if (zIndex) {\n stepConfig.zIndex = zIndex + 1;\n }\n\n if (stepConfig.zIndex) {\n currentStepNode.css('zIndex', stepConfig.zIndex + 1);\n }\n\n // Add the backdrop.\n this.positionBackdrop(stepConfig);\n\n $(document.body).append(currentStepNode);\n this.currentStepNode = currentStepNode;\n\n // Ensure that the step node is positioned.\n // Some situations mean that the value is not properly calculated without this step.\n this.currentStepNode.css({\n top: 0,\n left: 0,\n });\n\n animationTarget\n .animate({\n scrollTop: this.calculateScrollTop(stepConfig),\n }).promise().then(function() {\n this.positionStep(stepConfig);\n this.revealStep(stepConfig);\n return;\n }.bind(this))\n .catch(function() {\n // Silently fail.\n });\n\n } else if (stepConfig.orphan) {\n stepConfig.isOrphan = true;\n\n // This will be appended to the body instead.\n stepConfig.attachTo = $('body').first();\n stepConfig.attachPoint = 'append';\n\n // Add the backdrop.\n this.positionBackdrop(stepConfig);\n\n // This is an orphaned step.\n currentStepNode.addClass('orphan');\n\n // It lives in the body.\n $(document.body).append(currentStepNode);\n this.currentStepNode = currentStepNode;\n\n this.currentStepNode.css('position', 'fixed');\n\n this.currentStepPopper = new Popper(\n $('body'),\n this.currentStepNode[0], {\n removeOnDestroy: true,\n placement: stepConfig.placement + '-start',\n arrowElement: '[data-role=\"arrow\"]',\n // Empty the modifiers. We've already placed the step and don't want it moved.\n modifiers: {\n hide: {\n enabled: false,\n },\n applyStyle: {\n onLoad: null,\n enabled: false,\n },\n },\n onCreate: () => {\n // First, we need to check if the step's content contains any images.\n const images = this.currentStepNode.find('img');\n if (images.length) {\n // Images found, need to calculate the position when the image is loaded.\n images.on('load', () => {\n this.calculateStepPositionInPage(currentStepNode);\n });\n }\n this.calculateStepPositionInPage(currentStepNode);\n }\n }\n );\n\n this.revealStep(stepConfig);\n }\n\n return this;\n }\n\n /**\n * Make the given step visible.\n *\n * @method revealStep\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n revealStep(stepConfig) {\n // Fade the step in.\n this.currentStepNode.fadeIn('', $.proxy(function() {\n // Announce via ARIA.\n this.announceStep(stepConfig);\n\n // Focus on the current step Node.\n this.currentStepNode.focus();\n window.setTimeout($.proxy(function() {\n // After a brief delay, focus again.\n // There seems to be an issue with Jaws where it only reads the dialogue title initially.\n // This second focus helps it to read the full dialogue.\n if (this.currentStepNode) {\n this.currentStepNode.focus();\n }\n }, this), 100);\n\n }, this));\n\n return this;\n }\n\n /**\n * Helper to announce the step on the page.\n *\n * @method announceStep\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n announceStep(stepConfig) {\n // Setup the step Dialogue as per:\n // * https://www.w3.org/TR/wai-aria-practices/#dialog_nonmodal\n // * https://www.w3.org/TR/wai-aria-practices/#dialog_modal\n\n // Generate an ID for the current step node.\n let stepId = 'tour-step-' + this.tourName + '-' + stepConfig.stepNumber;\n this.currentStepNode.attr('id', stepId);\n\n let bodyRegion = this.currentStepNode.find('[data-placeholder=\"body\"]').first();\n bodyRegion.attr('id', stepId + '-body');\n bodyRegion.attr('role', 'document');\n\n let headerRegion = this.currentStepNode.find('[data-placeholder=\"title\"]').first();\n headerRegion.attr('id', stepId + '-title');\n headerRegion.attr('aria-labelledby', stepId + '-body');\n\n // Generally, a modal dialog has a role of dialog.\n this.currentStepNode.attr('role', 'dialog');\n this.currentStepNode.attr('tabindex', 0);\n this.currentStepNode.attr('aria-labelledby', stepId + '-title');\n this.currentStepNode.attr('aria-describedby', stepId + '-body');\n\n // Configure ARIA attributes on the target.\n let target = this.getStepTarget(stepConfig);\n if (target) {\n target.data('original-tabindex', target.attr('tabindex'));\n if (!target.attr('tabindex')) {\n target.attr('tabindex', 0);\n }\n\n target\n .data('original-describedby', target.attr('aria-describedby'))\n .attr('aria-describedby', stepId + '-body')\n ;\n }\n\n this.accessibilityShow(stepConfig);\n\n return this;\n }\n\n /**\n * Handle key down events.\n *\n * @method handleKeyDown\n * @param {EventFacade} e\n */\n handleKeyDown(e) {\n let tabbableSelector = 'a[href], link[href], [draggable=true], [contenteditable=true], ';\n tabbableSelector += ':input:enabled, [tabindex], button:enabled';\n switch (e.keyCode) {\n case 27:\n this.endTour();\n break;\n\n // 9 == Tab - trap focus for items with a backdrop.\n case 9:\n // Tab must be handled on key up only in this instance.\n (function() {\n if (!this.currentStepConfig.hasBackdrop) {\n // Trapping tab focus is only handled for those steps with a backdrop.\n return;\n }\n\n // Find all tabbable locations.\n let activeElement = $(document.activeElement);\n let stepTarget = this.getStepTarget(this.currentStepConfig);\n let tabbableNodes = $(tabbableSelector);\n let dialogContainer = $('span[data-flexitour=\"container\"]');\n let currentIndex;\n // Filter out element which is not belong to target section or dialogue.\n if (stepTarget) {\n tabbableNodes = tabbableNodes.filter(function(index, element) {\n return stepTarget !== null\n && (stepTarget.has(element).length\n || dialogContainer.has(element).length\n || stepTarget.is(element)\n || dialogContainer.is(element));\n });\n }\n\n // Find index of focusing element.\n tabbableNodes.each(function(index, element) {\n if (activeElement.is(element)) {\n currentIndex = index;\n return false;\n }\n // Keep looping.\n return true;\n });\n\n let nextIndex;\n let nextNode;\n let focusRelevant;\n if (currentIndex != void 0) {\n let direction = 1;\n if (e.shiftKey) {\n direction = -1;\n }\n nextIndex = currentIndex;\n do {\n nextIndex += direction;\n nextNode = $(tabbableNodes[nextIndex]);\n } while (nextNode.length && nextNode.is(':disabled') || nextNode.is(':hidden'));\n if (nextNode.length) {\n // A new f\n focusRelevant = nextNode.closest(stepTarget).length;\n focusRelevant = focusRelevant || nextNode.closest(this.currentStepNode).length;\n } else {\n // Unable to find the target somehow.\n focusRelevant = false;\n }\n }\n\n if (focusRelevant) {\n nextNode.focus();\n } else {\n if (e.shiftKey) {\n // Focus on the last tabbable node in the step.\n this.currentStepNode.find(tabbableSelector).last().focus();\n } else {\n if (this.currentStepConfig.isOrphan) {\n // Focus on the step - there is no target.\n this.currentStepNode.focus();\n } else {\n // Focus on the step target.\n stepTarget.focus();\n }\n }\n }\n e.preventDefault();\n }).call(this);\n break;\n }\n }\n\n /**\n * Start the current tour.\n *\n * @method startTour\n * @param {Number} startAt Which step number to start at. If not specified, starts at the last point.\n * @chainable\n * @return {Object} this.\n * @fires tool_usertours/tourStart\n * @fires tool_usertours/tourStarted\n */\n startTour(startAt) {\n if (this.storage && typeof startAt === 'undefined') {\n let storageStartValue = this.storage.getItem(this.storageKey);\n if (storageStartValue) {\n let storageStartAt = parseInt(storageStartValue, 10);\n if (storageStartAt <= this.steps.length) {\n startAt = storageStartAt;\n }\n }\n }\n\n if (typeof startAt === 'undefined') {\n startAt = this.getCurrentStepNumber();\n }\n\n const tourStartEvent = this.dispatchEvent(eventTypes.tourStart, {startAt}, true);\n if (!tourStartEvent.defaultPrevented) {\n this.gotoStep(startAt);\n this.tourRunning = true;\n this.dispatchEvent(eventTypes.tourStarted, {startAt});\n }\n\n return this;\n }\n\n /**\n * Restart the tour from the beginning, resetting the completionlag.\n *\n * @method restartTour\n * @chainable\n * @return {Object} this.\n */\n restartTour() {\n return this.startTour(0);\n }\n\n /**\n * End the current tour.\n *\n * @method endTour\n * @chainable\n * @return {Object} this.\n * @fires tool_usertours/tourEnd\n * @fires tool_usertours/tourEnded\n */\n endTour() {\n const tourEndEvent = this.dispatchEvent(eventTypes.tourEnd, {}, true);\n if (tourEndEvent.defaultPrevented) {\n return this;\n }\n\n if (this.currentStepConfig) {\n let previousTarget = this.getStepTarget(this.currentStepConfig);\n if (previousTarget) {\n if (!previousTarget.attr('tabindex')) {\n previousTarget.attr('tabindex', '-1');\n }\n previousTarget.first().focus();\n }\n }\n\n this.hide(true);\n\n this.tourRunning = false;\n this.dispatchEvent(eventTypes.tourEnded);\n\n return this;\n }\n\n /**\n * Hide any currently visible steps.\n *\n * @method hide\n * @param {Bool} transition Animate the visibility change\n * @chainable\n * @return {Object} this.\n * @fires tool_usertours/stepHide\n * @fires tool_usertours/stepHidden\n */\n hide(transition) {\n const stepHideEvent = this.dispatchEvent(eventTypes.stepHide, {}, true);\n if (stepHideEvent.defaultPrevented) {\n return this;\n }\n\n if (this.currentStepNode && this.currentStepNode.length) {\n this.currentStepNode.hide();\n if (this.currentStepPopper) {\n this.currentStepPopper.destroy();\n }\n }\n\n // Restore original target configuration.\n if (this.currentStepConfig) {\n let target = this.getStepTarget(this.currentStepConfig);\n if (target) {\n if (target.data('original-labelledby')) {\n target.attr('aria-labelledby', target.data('original-labelledby'));\n }\n\n if (target.data('original-describedby')) {\n target.attr('aria-describedby', target.data('original-describedby'));\n }\n\n if (target.data('original-tabindex')) {\n target.attr('tabindex', target.data('tabindex'));\n } else {\n // If the target does not have the tabindex attribute at the beginning. We need to remove it.\n // We should wait a little here before removing the attribute to prevent the browser from adding it again.\n window.setTimeout(() => {\n target.removeAttr('tabindex');\n }, 400);\n }\n }\n\n // Clear the step configuration.\n this.currentStepConfig = null;\n }\n\n let fadeTime = 0;\n if (transition) {\n fadeTime = 400;\n }\n\n // Remove the backdrop features.\n $('[data-flexitour=\"step-background\"]').remove();\n $('[data-flexitour=\"step-backdrop\"]').removeAttr('data-flexitour');\n $('[data-flexitour=\"backdrop\"]').fadeOut(fadeTime, function() {\n $(this).remove();\n });\n\n // Remove aria-describedby and tabindex attributes.\n if (this.currentStepNode && this.currentStepNode.length) {\n let stepId = this.currentStepNode.attr('id');\n if (stepId) {\n let currentStepElement = '[aria-describedby=\"' + stepId + '-body\"]';\n $(currentStepElement).removeAttr('tabindex');\n $(currentStepElement).removeAttr('aria-describedby');\n }\n }\n\n // Reset the listeners.\n this.resetStepListeners();\n\n this.accessibilityHide();\n\n this.dispatchEvent(eventTypes.stepHidden);\n\n this.currentStepNode = null;\n this.currentStepPopper = null;\n return this;\n }\n\n /**\n * Show the current steps.\n *\n * @method show\n * @chainable\n * @return {Object} this.\n */\n show() {\n // Show the current step.\n let startAt = this.getCurrentStepNumber();\n\n return this.gotoStep(startAt);\n }\n\n /**\n * Return the current step node.\n *\n * @method getStepContainer\n * @return {jQuery}\n */\n getStepContainer() {\n return $(this.currentStepNode);\n }\n\n /**\n * Calculate scrollTop.\n *\n * @method calculateScrollTop\n * @param {Object} stepConfig The step configuration of the step\n * @return {Number}\n */\n calculateScrollTop(stepConfig) {\n let viewportHeight = $(window).height();\n let targetNode = this.getStepTarget(stepConfig);\n\n let scrollParent = $(window);\n if (targetNode.parents('[data-usertour=\"scroller\"]').length) {\n scrollParent = targetNode.parents('[data-usertour=\"scroller\"]');\n }\n let scrollTop = scrollParent.scrollTop();\n\n if (stepConfig.placement === 'top') {\n // If the placement is top, center scroll at the top of the target.\n scrollTop = targetNode.offset().top - (viewportHeight / 2);\n } else if (stepConfig.placement === 'bottom') {\n // If the placement is bottom, center scroll at the bottom of the target.\n scrollTop = targetNode.offset().top + targetNode.height() + scrollTop - (viewportHeight / 2);\n } else if (targetNode.height() <= (viewportHeight * 0.8)) {\n // If the placement is left/right, and the target fits in the viewport, centre screen on the target\n scrollTop = targetNode.offset().top - ((viewportHeight - targetNode.height()) / 2);\n } else {\n // If the placement is left/right, and the target is bigger than the viewport, set scrollTop to target.top + buffer\n // and change step attachmentTarget to top+.\n scrollTop = targetNode.offset().top - (viewportHeight * 0.2);\n }\n\n // Never scroll over the top.\n scrollTop = Math.max(0, scrollTop);\n\n // Never scroll beyond the bottom.\n scrollTop = Math.min($(document).height() - viewportHeight, scrollTop);\n\n return Math.ceil(scrollTop);\n }\n\n /**\n * Calculate dialogue position for page middle.\n *\n * @param {jQuery} currentStepNode Current step node\n * @method calculateScrollTop\n */\n calculateStepPositionInPage(currentStepNode) {\n let top = MINSPACING;\n const viewportHeight = $(window).height();\n const stepHeight = currentStepNode.height();\n const viewportWidth = $(window).width();\n const stepWidth = currentStepNode.width();\n if (viewportHeight >= (stepHeight + (MINSPACING * 2))) {\n top = Math.ceil((viewportHeight - stepHeight) / 2);\n } else {\n const headerHeight = currentStepNode.find('.modal-header').first().outerHeight() ?? 0;\n const footerHeight = currentStepNode.find('.modal-footer').first().outerHeight() ?? 0;\n const currentStepBody = currentStepNode.find('[data-placeholder=\"body\"]').first();\n const maxHeight = viewportHeight - (MINSPACING * 2) - headerHeight - footerHeight;\n currentStepBody.css({\n 'max-height': maxHeight + 'px',\n 'overflow': 'auto',\n });\n }\n currentStepNode.offset({\n top: top,\n left: Math.ceil((viewportWidth - stepWidth) / 2)\n });\n }\n\n /**\n * Position the step on the page.\n *\n * @method positionStep\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n positionStep(stepConfig) {\n let content = this.currentStepNode;\n let thisT = this;\n if (!content || !content.length) {\n // Unable to find the step node.\n return this;\n }\n\n stepConfig.placement = this.recalculatePlacement(stepConfig);\n let flipBehavior;\n switch (stepConfig.placement) {\n case 'left':\n flipBehavior = ['left', 'right', 'top', 'bottom'];\n break;\n case 'right':\n flipBehavior = ['right', 'left', 'top', 'bottom'];\n break;\n case 'top':\n flipBehavior = ['top', 'bottom', 'right', 'left'];\n break;\n case 'bottom':\n flipBehavior = ['bottom', 'top', 'right', 'left'];\n break;\n default:\n flipBehavior = 'flip';\n break;\n }\n\n let target = this.getStepTarget(stepConfig);\n var config = {\n placement: stepConfig.placement + '-start',\n removeOnDestroy: true,\n modifiers: {\n flip: {\n behaviour: flipBehavior,\n },\n arrow: {\n element: '[data-role=\"arrow\"]',\n },\n },\n onCreate: function(data) {\n recalculateArrowPosition(data);\n recalculateStepPosition(data);\n },\n onUpdate: function(data) {\n recalculateArrowPosition(data);\n if (thisT.possitionNeedToBeRecalculated) {\n thisT.recalculatedNo++;\n thisT.possitionNeedToBeRecalculated = false;\n recalculateStepPosition(data);\n }\n },\n };\n\n let recalculateArrowPosition = function(data) {\n let placement = data.placement.split('-')[0];\n const isVertical = ['left', 'right'].indexOf(placement) !== -1;\n const arrowElement = data.instance.popper.querySelector('[data-role=\"arrow\"]');\n const stepElement = $(data.instance.popper.querySelector('[data-role=\"flexitour-step\"]'));\n if (isVertical) {\n let arrowHeight = parseFloat(window.getComputedStyle(arrowElement).height);\n let arrowOffset = parseFloat(window.getComputedStyle(arrowElement).top);\n let popperHeight = parseFloat(window.getComputedStyle(data.instance.popper).height);\n let popperOffset = parseFloat(window.getComputedStyle(data.instance.popper).top);\n let popperBorderWidth = parseFloat(stepElement.css('borderTopWidth'));\n let popperBorderRadiusWidth = parseFloat(stepElement.css('borderTopLeftRadius')) * 2;\n let arrowPos = arrowOffset + (arrowHeight / 2);\n let maxPos = popperHeight + popperOffset - popperBorderWidth - popperBorderRadiusWidth;\n let minPos = popperOffset + popperBorderWidth + popperBorderRadiusWidth;\n if (arrowPos >= maxPos || arrowPos <= minPos) {\n let newArrowPos = 0;\n if (arrowPos > (popperHeight / 2)) {\n newArrowPos = maxPos - arrowHeight;\n } else {\n newArrowPos = minPos + arrowHeight;\n }\n $(arrowElement).css('top', newArrowPos);\n }\n } else {\n let arrowWidth = parseFloat(window.getComputedStyle(arrowElement).width);\n let arrowOffset = parseFloat(window.getComputedStyle(arrowElement).left);\n let popperWidth = parseFloat(window.getComputedStyle(data.instance.popper).width);\n let popperOffset = parseFloat(window.getComputedStyle(data.instance.popper).left);\n let popperBorderWidth = parseFloat(stepElement.css('borderTopWidth'));\n let popperBorderRadiusWidth = parseFloat(stepElement.css('borderTopLeftRadius')) * 2;\n let arrowPos = arrowOffset + (arrowWidth / 2);\n let maxPos = popperWidth + popperOffset - popperBorderWidth - popperBorderRadiusWidth;\n let minPos = popperOffset + popperBorderWidth + popperBorderRadiusWidth;\n if (arrowPos >= maxPos || arrowPos <= minPos) {\n let newArrowPos = 0;\n if (arrowPos > (popperWidth / 2)) {\n newArrowPos = maxPos - arrowWidth;\n } else {\n newArrowPos = minPos + arrowWidth;\n }\n $(arrowElement).css('left', newArrowPos);\n }\n }\n };\n\n const recalculateStepPosition = function(data) {\n const placement = data.placement.split('-')[0];\n const isVertical = ['left', 'right'].indexOf(placement) !== -1;\n const popperElement = $(data.instance.popper);\n const targetElement = $(data.instance.reference);\n const arrowElement = popperElement.find('[data-role=\"arrow\"]');\n const stepElement = popperElement.find('[data-role=\"flexitour-step\"]');\n const viewportHeight = $(window).height();\n const viewportWidth = $(window).width();\n const arrowHeight = parseFloat(arrowElement.outerHeight(true));\n const popperHeight = parseFloat(popperElement.outerHeight(true));\n const targetHeight = parseFloat(targetElement.outerHeight(true));\n const arrowWidth = parseFloat(arrowElement.outerWidth(true));\n const popperWidth = parseFloat(popperElement.outerWidth(true));\n const targetWidth = parseFloat(targetElement.outerWidth(true));\n let maxHeight;\n\n if (thisT.recalculatedNo > 1) {\n // The current screen is too small, and cannot fit with the original placement.\n // We should set the placement to auto so the PopperJS can calculate the perfect placement.\n thisT.currentStepPopper.options.placement = isVertical ? 'auto-left' : 'auto-bottom';\n }\n if (thisT.recalculatedNo > 2) {\n // Return here to prevent recursive calling.\n return;\n }\n\n if (isVertical) {\n // Find the best place to put the tour: Left of right.\n const leftSpace = targetElement.offset().left > 0 ? targetElement.offset().left : 0;\n const rightSpace = viewportWidth - leftSpace - targetWidth;\n const remainingSpace = leftSpace >= rightSpace ? leftSpace : rightSpace;\n maxHeight = viewportHeight - MINSPACING * 2;\n if (remainingSpace < (popperWidth + arrowWidth)) {\n const maxWidth = remainingSpace - MINSPACING - arrowWidth;\n if (maxWidth > 0) {\n popperElement.css({\n 'max-width': maxWidth + 'px',\n });\n // Not enough space, flag true to make Popper to recalculate the position.\n thisT.possitionNeedToBeRecalculated = true;\n }\n } else if (maxHeight < popperHeight) {\n // Check if the Popper's height can fit the viewport height or not.\n // If not, set the correct max-height value for the Popper element.\n popperElement.css({\n 'max-height': maxHeight + 'px',\n });\n }\n } else {\n // Find the best place to put the tour: Top of bottom.\n const topSpace = targetElement.offset().top > 0 ? targetElement.offset().top : 0;\n const bottomSpace = viewportHeight - topSpace - targetHeight;\n const remainingSpace = topSpace >= bottomSpace ? topSpace : bottomSpace;\n maxHeight = remainingSpace - MINSPACING - arrowHeight;\n if (remainingSpace < (popperHeight + arrowHeight)) {\n // Not enough space, flag true to make Popper to recalculate the position.\n thisT.possitionNeedToBeRecalculated = true;\n }\n }\n\n // Check if the Popper's height can fit the viewport height or not.\n // If not, set the correct max-height value for the body.\n const currentStepBody = stepElement.find('[data-placeholder=\"body\"]').first();\n const headerEle = stepElement.find('.modal-header').first();\n const footerEle = stepElement.find('.modal-footer').first();\n const headerHeight = headerEle.outerHeight(true) ?? 0;\n const footerHeight = footerEle.outerHeight(true) ?? 0;\n maxHeight = maxHeight - headerHeight - footerHeight;\n if (maxHeight > 0) {\n headerEle.removeClass('minimal');\n footerEle.removeClass('minimal');\n currentStepBody.css({\n 'max-height': maxHeight + 'px',\n 'overflow': 'auto',\n });\n } else {\n headerEle.addClass('minimal');\n footerEle.addClass('minimal');\n }\n // Call the Popper update method to update the position.\n thisT.currentStepPopper.update();\n };\n\n let background = $('[data-flexitour=\"step-background\"]');\n if (background.length) {\n target = background;\n }\n this.currentStepPopper = new Popper(target, content[0], config);\n\n return this;\n }\n\n /**\n * For left/right placement, checks that there is room for the step at current window size.\n *\n * If there is not enough room, changes placement to 'top'.\n *\n * @method recalculatePlacement\n * @param {Object} stepConfig The step configuration of the step\n * @return {String} The placement after recalculate\n */\n recalculatePlacement(stepConfig) {\n const buffer = 10;\n const arrowWidth = 16;\n let target = this.getStepTarget(stepConfig);\n let widthContent = this.currentStepNode.width() + arrowWidth;\n let targetOffsetLeft = target.offset().left - buffer;\n let targetOffsetRight = target.offset().left + target.width() + buffer;\n let placement = stepConfig.placement;\n\n if (['left', 'right'].indexOf(placement) !== -1) {\n if ((targetOffsetLeft < (widthContent + buffer)) &&\n ((targetOffsetRight + widthContent + buffer) > document.documentElement.clientWidth)) {\n placement = 'top';\n }\n }\n return placement;\n }\n\n /**\n * Add the backdrop.\n *\n * @method positionBackdrop\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n positionBackdrop(stepConfig) {\n if (stepConfig.backdrop) {\n this.currentStepConfig.hasBackdrop = true;\n let backdrop = $('
');\n\n if (stepConfig.zIndex) {\n if (stepConfig.attachPoint === 'append') {\n stepConfig.attachTo.append(backdrop);\n } else {\n backdrop.insertAfter(stepConfig.attachTo);\n }\n } else {\n $('body').append(backdrop);\n }\n\n if (this.isStepActuallyVisible(stepConfig)) {\n // The step has a visible target.\n // Punch a hole through the backdrop.\n let background = $('[data-flexitour=\"step-background\"]');\n if (!background.length) {\n background = $('
');\n }\n\n let targetNode = this.getStepTarget(stepConfig);\n\n let buffer = 10;\n\n let colorNode = targetNode;\n if (buffer) {\n colorNode = $('body');\n }\n\n let drawertop = 0;\n if (targetNode.parents('[data-usertour=\"scroller\"]').length) {\n const scrollerElement = targetNode.parents('[data-usertour=\"scroller\"]');\n const navigationBuffer = scrollerElement.offset().top;\n if (scrollerElement.scrollTop() >= navigationBuffer) {\n drawertop = scrollerElement.scrollTop() - navigationBuffer;\n background.css({\n position: 'fixed'\n });\n }\n }\n\n background.css({\n width: targetNode.outerWidth() + buffer + buffer,\n height: targetNode.outerHeight() + buffer + buffer,\n left: targetNode.offset().left - buffer,\n top: targetNode.offset().top + drawertop - buffer,\n backgroundColor: this.calculateInherittedBackgroundColor(colorNode),\n });\n\n if (targetNode.offset().left < buffer) {\n background.css({\n width: targetNode.outerWidth() + targetNode.offset().left + buffer,\n left: targetNode.offset().left,\n });\n }\n\n if ((targetNode.offset().top + drawertop) < buffer) {\n background.css({\n height: targetNode.outerHeight() + targetNode.offset().top + buffer,\n top: targetNode.offset().top,\n });\n }\n\n let targetRadius = targetNode.css('borderRadius');\n if (targetRadius && targetRadius !== $('body').css('borderRadius')) {\n background.css('borderRadius', targetRadius);\n }\n\n let targetPosition = this.calculatePosition(targetNode);\n if (targetPosition === 'absolute') {\n background.css('position', 'fixed');\n }\n\n let fader = background.clone();\n fader.css({\n backgroundColor: backdrop.css('backgroundColor'),\n opacity: backdrop.css('opacity'),\n });\n fader.attr('data-flexitour', 'step-background-fader');\n\n if (targetNode.parents('[data-region=\"fixed-drawer\"]').length || !stepConfig.zIndex) {\n let targetClone = targetNode.clone();\n background.append(targetClone.first());\n }\n\n if (stepConfig.zIndex) {\n if (stepConfig.attachPoint === 'append') {\n stepConfig.attachTo.append(background);\n } else {\n fader.insertAfter(stepConfig.attachTo);\n background.insertAfter(stepConfig.attachTo);\n }\n } else {\n $('body').append(fader);\n $('body').append(background);\n }\n\n // Add the backdrop data to the actual target.\n // This is the part which actually does the work.\n targetNode.attr('data-flexitour', 'step-backdrop');\n\n if (stepConfig.zIndex) {\n backdrop.css('zIndex', stepConfig.zIndex);\n background.css('zIndex', stepConfig.zIndex + 1);\n targetNode.css('zIndex', stepConfig.zIndex + 2);\n }\n\n fader.fadeOut('2000', function() {\n $(this).remove();\n });\n }\n }\n return this;\n }\n\n /**\n * Calculate the inheritted z-index.\n *\n * @method calculateZIndex\n * @param {jQuery} elem The element to calculate z-index for\n * @return {Number} Calculated z-index\n */\n calculateZIndex(elem) {\n elem = $(elem);\n while (elem.length && elem[0] !== document) {\n // Ignore z-index if position is set to a value where z-index is ignored by the browser\n // This makes behavior of this function consistent across browsers\n // WebKit always returns auto if the element is positioned.\n let position = elem.css(\"position\");\n if (position === \"absolute\" || position === \"fixed\") {\n // IE returns 0 when zIndex is not specified\n // other browsers return a string\n // we ignore the case of nested elements with an explicit value of 0\n //
\n let value = parseInt(elem.css(\"zIndex\"), 10);\n if (!isNaN(value) && value !== 0) {\n return value;\n }\n }\n elem = elem.parent();\n }\n\n return 0;\n }\n\n /**\n * Calculate the inheritted background colour.\n *\n * @method calculateInherittedBackgroundColor\n * @param {jQuery} elem The element to calculate colour for\n * @return {String} Calculated background colour\n */\n calculateInherittedBackgroundColor(elem) {\n // Use a fake node to compare each element against.\n let fakeNode = $('
').hide();\n $('body').append(fakeNode);\n let fakeElemColor = fakeNode.css('backgroundColor');\n fakeNode.remove();\n\n elem = $(elem);\n while (elem.length && elem[0] !== document) {\n let color = elem.css('backgroundColor');\n if (color !== fakeElemColor) {\n return color;\n }\n elem = elem.parent();\n }\n\n return null;\n }\n\n /**\n * Calculate the inheritted position.\n *\n * @method calculatePosition\n * @param {jQuery} elem The element to calculate position for\n * @return {String} Calculated position\n */\n calculatePosition(elem) {\n elem = $(elem);\n while (elem.length && elem[0] !== document) {\n let position = elem.css('position');\n if (position !== 'static') {\n return position;\n }\n elem = elem.parent();\n }\n\n return null;\n }\n\n /**\n * Perform accessibility changes for step shown.\n *\n * This will add aria-hidden=\"true\" to all siblings and parent siblings.\n *\n * @method accessibilityShow\n */\n accessibilityShow() {\n let stateHolder = 'data-has-hidden';\n let attrName = 'aria-hidden';\n let hideFunction = function(child) {\n let flexitourRole = child.data('flexitour');\n if (flexitourRole) {\n switch (flexitourRole) {\n case 'container':\n case 'target':\n return;\n }\n }\n\n let hidden = child.attr(attrName);\n if (!hidden) {\n child.attr(stateHolder, true);\n Aria.hide(child);\n }\n };\n\n this.currentStepNode.siblings().each(function(index, node) {\n hideFunction($(node));\n });\n this.currentStepNode.parentsUntil('body').siblings().each(function(index, node) {\n hideFunction($(node));\n });\n }\n\n /**\n * Perform accessibility changes for step hidden.\n *\n * This will remove any newly added aria-hidden=\"true\".\n *\n * @method accessibilityHide\n */\n accessibilityHide() {\n let stateHolder = 'data-has-hidden';\n let showFunction = function(child) {\n let hidden = child.attr(stateHolder);\n if (typeof hidden !== 'undefined') {\n child.removeAttr(stateHolder);\n Aria.unhide(child);\n }\n };\n\n $('[' + stateHolder + ']').each(function(index, node) {\n showFunction($(node));\n });\n }\n};\n\nexport default Tour;\n"],"names":["constructor","config","init","eventHandlers","reset","originalConfiguration","configure","apply","this","arguments","possitionNeedToBeRecalculated","recalculatedNo","storage","window","sessionStorage","storageKey","tourName","e","hide","resetStepListeners","steps","currentStepNumber","eventName","forEach","handler","addEventHandler","resetStepDefaults","template","templateContent","checkMinimumRequirements","Error","length","loadOriginalConfiguration","stepDefaults","setStepDefaults","extend","element","placement","delay","moveOnClick","moveAfterTime","orphan","direction","getCurrentStepNumber","parseInt","setCurrentStepNumber","stepNumber","setItem","code","DOMException","QUOTA_EXCEEDED_ERR","removeItem","getNextStepNumber","nextStepNumber","isStepPotentiallyVisible","getStepConfig","getPreviousStepNumber","previousStepNumber","isLastStep","stepConfig","isStepActuallyVisible","getPotentiallyVisibleSteps","position","result","stepId","stepid","isCSSAllowed","target","getStepTarget","is","testCSSElement","document","createElement","classList","add","body","appendChild","isAllowed","getComputedStyle","display","remove","next","gotoStep","previous","endTour","_gotoStep","delayed","setTimeout","bind","fn","dispatchEvent","eventTypes","stepRender","defaultPrevented","renderStep","stepRendered","normalizeStepConfig","$","reflex","moveAfterClick","content","attachTo","attachPoint","first","detail","cancelable","tour","push","processStepListeners","listeners","node","currentStepNode","args","proxy","handleKeyDown","targetNode","parents","listener","on","off","currentStepConfig","getTemplateContent","find","html","title","nextBtn","endBtn","removeClass","addClass","prop","then","value","catch","attr","displaystepnumbers","stepsPotentiallyVisible","totalStepsPotentiallyVisible","total","addStepToPage","clone","animationTarget","stop","data","zIndex","calculateZIndex","css","positionBackdrop","append","top","left","animate","scrollTop","calculateScrollTop","promise","positionStep","revealStep","isOrphan","currentStepPopper","Popper","removeOnDestroy","arrowElement","modifiers","enabled","applyStyle","onLoad","onCreate","images","calculateStepPositionInPage","fadeIn","announceStep","focus","bodyRegion","headerRegion","accessibilityShow","tabbableSelector","keyCode","hasBackdrop","currentIndex","nextIndex","nextNode","focusRelevant","activeElement","stepTarget","tabbableNodes","dialogContainer","filter","index","has","each","shiftKey","closest","last","preventDefault","call","startTour","startAt","storageStartValue","getItem","storageStartAt","tourStart","tourRunning","tourStarted","restartTour","tourEnd","previousTarget","tourEnded","transition","stepHide","destroy","removeAttr","fadeTime","fadeOut","currentStepElement","accessibilityHide","stepHidden","show","getStepContainer","viewportHeight","height","scrollParent","offset","Math","max","min","ceil","stepHeight","viewportWidth","width","stepWidth","MINSPACING","maxHeight","outerHeight","flipBehavior","thisT","recalculatePlacement","flip","behaviour","arrow","recalculateArrowPosition","recalculateStepPosition","onUpdate","split","isVertical","indexOf","instance","popper","querySelector","stepElement","arrowHeight","parseFloat","arrowOffset","popperHeight","popperOffset","popperBorderWidth","popperBorderRadiusWidth","arrowPos","maxPos","minPos","newArrowPos","arrowWidth","popperWidth","popperElement","targetElement","reference","targetHeight","outerWidth","targetWidth","options","leftSpace","rightSpace","remainingSpace","maxWidth","topSpace","bottomSpace","currentStepBody","headerEle","footerEle","update","background","widthContent","targetOffsetLeft","targetOffsetRight","documentElement","clientWidth","backdrop","insertAfter","buffer","colorNode","drawertop","scrollerElement","navigationBuffer","backgroundColor","calculateInherittedBackgroundColor","targetRadius","calculatePosition","fader","opacity","targetClone","elem","isNaN","parent","fakeNode","fakeElemColor","color","hideFunction","child","flexitourRole","Aria","siblings","parentsUntil","unhide"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"tour.min.js","sources":["../src/tour.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 * A user tour.\n *\n * @module tool_usertours/tour\n * @copyright 2018 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A list of steps.\n *\n * @typedef {Object[]} StepList\n * @property {Number} stepId The id of the step in the database\n * @property {Number} position The position of the step within the tour (zero-indexed)\n */\n\nimport $ from 'jquery';\nimport * as Aria from 'core/aria';\nimport Popper from 'core/popper';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport {eventTypes} from './events';\nimport {getString} from 'core/str';\nimport {prefetchStrings} from 'core/prefetch';\nimport {notifyFilterContentUpdated} from 'core/event';\n\n/**\n * The minimum spacing for tour step to display.\n *\n * @private\n * @constant\n * @type {number}\n */\nconst MINSPACING = 10;\n\n/**\n * A user tour.\n *\n * @class tool_usertours/tour\n * @property {boolean} tourRunning Whether the tour is currently running.\n */\nconst Tour = class {\n tourRunning = false;\n\n /**\n * @param {object} config The configuration object.\n */\n constructor(config) {\n this.init(config);\n }\n\n /**\n * Initialise the tour.\n *\n * @method init\n * @param {Object} config The configuration object.\n * @chainable\n * @return {Object} this.\n */\n init(config) {\n // Unset all handlers.\n this.eventHandlers = {};\n\n // Reset the current tour states.\n this.reset();\n\n // Store the initial configuration.\n this.originalConfiguration = config || {};\n\n // Apply configuration.\n this.configure.apply(this, arguments);\n\n // Unset recalculate state.\n this.possitionNeedToBeRecalculated = false;\n\n // Unset recalculate count.\n this.recalculatedNo = 0;\n\n try {\n this.storage = window.sessionStorage;\n this.storageKey = 'tourstate_' + this.tourName;\n } catch (e) {\n this.storage = false;\n this.storageKey = '';\n }\n\n prefetchStrings('tool_usertours', [\n 'nextstep_sequence',\n 'skip_tour'\n ]);\n\n return this;\n }\n\n /**\n * Reset the current tour state.\n *\n * @method reset\n * @chainable\n * @return {Object} this.\n */\n reset() {\n // Hide the current step.\n this.hide();\n\n // Unset all handlers.\n this.eventHandlers = [];\n\n // Unset all listeners.\n this.resetStepListeners();\n\n // Unset the original configuration.\n this.originalConfiguration = {};\n\n // Reset the current step number and list of steps.\n this.steps = [];\n\n // Reset the current step number.\n this.currentStepNumber = 0;\n\n return this;\n }\n\n /**\n * Prepare tour configuration.\n *\n * @method configure\n * @param {Object} config The configuration object.\n * @chainable\n * @return {Object} this.\n */\n configure(config) {\n if (typeof config === 'object') {\n // Tour name.\n if (typeof config.tourName !== 'undefined') {\n this.tourName = config.tourName;\n }\n\n // Set up eventHandlers.\n if (config.eventHandlers) {\n for (let eventName in config.eventHandlers) {\n config.eventHandlers[eventName].forEach(function(handler) {\n this.addEventHandler(eventName, handler);\n }, this);\n }\n }\n\n // Reset the step configuration.\n this.resetStepDefaults(true);\n\n // Configure the steps.\n if (typeof config.steps === 'object') {\n this.steps = config.steps;\n }\n\n if (typeof config.template !== 'undefined') {\n this.templateContent = config.template;\n }\n }\n\n // Check that we have enough to start the tour.\n this.checkMinimumRequirements();\n\n return this;\n }\n\n /**\n * Check that the configuration meets the minimum requirements.\n *\n * @method checkMinimumRequirements\n */\n checkMinimumRequirements() {\n // Need a tourName.\n if (!this.tourName) {\n throw new Error(\"Tour Name required\");\n }\n\n // Need a minimum of one step.\n if (!this.steps || !this.steps.length) {\n throw new Error(\"Steps must be specified\");\n }\n }\n\n /**\n * Reset step default configuration.\n *\n * @method resetStepDefaults\n * @param {Boolean} loadOriginalConfiguration Whether to load the original configuration supplied with the Tour.\n * @chainable\n * @return {Object} this.\n */\n resetStepDefaults(loadOriginalConfiguration) {\n if (typeof loadOriginalConfiguration === 'undefined') {\n loadOriginalConfiguration = true;\n }\n\n this.stepDefaults = {};\n if (!loadOriginalConfiguration || typeof this.originalConfiguration.stepDefaults === 'undefined') {\n this.setStepDefaults({});\n } else {\n this.setStepDefaults(this.originalConfiguration.stepDefaults);\n }\n\n return this;\n }\n\n /**\n * Set the step defaults.\n *\n * @method setStepDefaults\n * @param {Object} stepDefaults The step defaults to apply to all steps\n * @chainable\n * @return {Object} this.\n */\n setStepDefaults(stepDefaults) {\n if (!this.stepDefaults) {\n this.stepDefaults = {};\n }\n $.extend(\n this.stepDefaults,\n {\n element: '',\n placement: 'top',\n delay: 0,\n moveOnClick: false,\n moveAfterTime: 0,\n orphan: false,\n direction: 1,\n },\n stepDefaults\n );\n\n return this;\n }\n\n /**\n * Retrieve the current step number.\n *\n * @method getCurrentStepNumber\n * @return {Number} The current step number\n */\n getCurrentStepNumber() {\n return parseInt(this.currentStepNumber, 10);\n }\n\n /**\n * Store the current step number.\n *\n * @method setCurrentStepNumber\n * @param {Number} stepNumber The current step number\n * @chainable\n */\n setCurrentStepNumber(stepNumber) {\n this.currentStepNumber = stepNumber;\n if (this.storage) {\n try {\n this.storage.setItem(this.storageKey, stepNumber);\n } catch (e) {\n if (e.code === DOMException.QUOTA_EXCEEDED_ERR) {\n this.storage.removeItem(this.storageKey);\n }\n }\n }\n }\n\n /**\n * Get the next step number after the currently displayed step.\n *\n * @method getNextStepNumber\n * @param {Number} stepNumber The current step number\n * @return {Number} The next step number to display\n */\n getNextStepNumber(stepNumber) {\n if (typeof stepNumber === 'undefined') {\n stepNumber = this.getCurrentStepNumber();\n }\n let nextStepNumber = stepNumber + 1;\n\n // Keep checking the remaining steps.\n while (nextStepNumber <= this.steps.length) {\n if (this.isStepPotentiallyVisible(this.getStepConfig(nextStepNumber))) {\n return nextStepNumber;\n }\n nextStepNumber++;\n }\n\n return null;\n }\n\n /**\n * Get the previous step number before the currently displayed step.\n *\n * @method getPreviousStepNumber\n * @param {Number} stepNumber The current step number\n * @return {Number} The previous step number to display\n */\n getPreviousStepNumber(stepNumber) {\n if (typeof stepNumber === 'undefined') {\n stepNumber = this.getCurrentStepNumber();\n }\n let previousStepNumber = stepNumber - 1;\n\n // Keep checking the remaining steps.\n while (previousStepNumber >= 0) {\n if (this.isStepPotentiallyVisible(this.getStepConfig(previousStepNumber))) {\n return previousStepNumber;\n }\n previousStepNumber--;\n }\n\n return null;\n }\n\n /**\n * Is the step the final step number?\n *\n * @method isLastStep\n * @param {Number} stepNumber Step number to test\n * @return {Boolean} Whether the step is the final step\n */\n isLastStep(stepNumber) {\n let nextStepNumber = this.getNextStepNumber(stepNumber);\n\n return nextStepNumber === null;\n }\n\n /**\n * Is this step potentially visible?\n *\n * @method isStepPotentiallyVisible\n * @param {Object} stepConfig The step configuration to normalise\n * @return {Boolean} Whether the step is the potentially visible\n */\n isStepPotentiallyVisible(stepConfig) {\n if (!stepConfig) {\n // Without step config, there can be no step.\n return false;\n }\n\n if (this.isStepActuallyVisible(stepConfig)) {\n // If it is actually visible, it is already potentially visible.\n return true;\n }\n\n if (typeof stepConfig.orphan !== 'undefined' && stepConfig.orphan) {\n // Orphan steps have no target. They are always visible.\n return true;\n }\n\n if (typeof stepConfig.delay !== 'undefined' && stepConfig.delay) {\n // Only return true if the activated has not been used yet.\n return true;\n }\n\n // Not theoretically, or actually visible.\n return false;\n }\n\n /**\n * Get potentially visible steps in a tour.\n *\n * @returns {StepList} A list of ordered steps\n */\n getPotentiallyVisibleSteps() {\n let position = 1;\n let result = [];\n // Checking the total steps.\n for (let stepNumber = 0; stepNumber < this.steps.length; stepNumber++) {\n const stepConfig = this.getStepConfig(stepNumber);\n if (this.isStepPotentiallyVisible(stepConfig)) {\n result[stepNumber] = {stepId: stepConfig.stepid, position: position};\n position++;\n }\n }\n\n return result;\n }\n\n /**\n * Is this step actually visible?\n *\n * @method isStepActuallyVisible\n * @param {Object} stepConfig The step configuration to normalise\n * @return {Boolean} Whether the step is actually visible\n */\n isStepActuallyVisible(stepConfig) {\n if (!stepConfig) {\n // Without step config, there can be no step.\n return false;\n }\n\n // Check if the CSS styles are allowed on the browser or not.\n if (!this.isCSSAllowed()) {\n return false;\n }\n\n let target = this.getStepTarget(stepConfig);\n if (target && target.length && target.is(':visible')) {\n // Without a target, there can be no step.\n return !!target.length;\n }\n\n return false;\n }\n\n /**\n * Is the browser actually allow CSS styles?\n *\n * @returns {boolean} True if the browser is allowing CSS styles\n */\n isCSSAllowed() {\n const testCSSElement = document.createElement('div');\n testCSSElement.classList.add('hide');\n document.body.appendChild(testCSSElement);\n const styles = window.getComputedStyle(testCSSElement);\n const isAllowed = styles.display === 'none';\n testCSSElement.remove();\n\n return isAllowed;\n }\n\n /**\n * Go to the next step in the tour.\n *\n * @method next\n * @chainable\n * @return {Object} this.\n */\n next() {\n return this.gotoStep(this.getNextStepNumber());\n }\n\n /**\n * Go to the previous step in the tour.\n *\n * @method previous\n * @chainable\n * @return {Object} this.\n */\n previous() {\n return this.gotoStep(this.getPreviousStepNumber(), -1);\n }\n\n /**\n * Go to the specified step in the tour.\n *\n * @method gotoStep\n * @param {Number} stepNumber The step number to display\n * @param {Number} direction Next or previous step\n * @chainable\n * @return {Object} this.\n * @fires tool_usertours/stepRender\n * @fires tool_usertours/stepRendered\n * @fires tool_usertours/stepHide\n * @fires tool_usertours/stepHidden\n */\n gotoStep(stepNumber, direction) {\n if (stepNumber < 0) {\n return this.endTour();\n }\n\n let stepConfig = this.getStepConfig(stepNumber);\n if (stepConfig === null) {\n return this.endTour();\n }\n\n return this._gotoStep(stepConfig, direction);\n }\n\n _gotoStep(stepConfig, direction) {\n if (!stepConfig) {\n return this.endTour();\n }\n\n if (typeof stepConfig.delay !== 'undefined' && stepConfig.delay && !stepConfig.delayed) {\n stepConfig.delayed = true;\n window.setTimeout(this._gotoStep.bind(this), stepConfig.delay, stepConfig, direction);\n\n return this;\n } else if (!stepConfig.orphan && !this.isStepActuallyVisible(stepConfig)) {\n let fn = direction == -1 ? 'getPreviousStepNumber' : 'getNextStepNumber';\n return this.gotoStep(this[fn](stepConfig.stepNumber), direction);\n }\n\n this.hide();\n\n const stepRenderEvent = this.dispatchEvent(eventTypes.stepRender, {stepConfig}, true);\n if (!stepRenderEvent.defaultPrevented) {\n this.renderStep(stepConfig);\n this.dispatchEvent(eventTypes.stepRendered, {stepConfig});\n }\n\n return this;\n }\n\n /**\n * Fetch the normalised step configuration for the specified step number.\n *\n * @method getStepConfig\n * @param {Number} stepNumber The step number to fetch configuration for\n * @return {Object} The step configuration\n */\n getStepConfig(stepNumber) {\n if (stepNumber === null || stepNumber < 0 || stepNumber >= this.steps.length) {\n return null;\n }\n\n // Normalise the step configuration.\n let stepConfig = this.normalizeStepConfig(this.steps[stepNumber]);\n\n // Add the stepNumber to the stepConfig.\n stepConfig = $.extend(stepConfig, {stepNumber: stepNumber});\n\n return stepConfig;\n }\n\n /**\n * Normalise the supplied step configuration.\n *\n * @method normalizeStepConfig\n * @param {Object} stepConfig The step configuration to normalise\n * @return {Object} The normalised step configuration\n */\n normalizeStepConfig(stepConfig) {\n\n if (typeof stepConfig.reflex !== 'undefined' && typeof stepConfig.moveAfterClick === 'undefined') {\n stepConfig.moveAfterClick = stepConfig.reflex;\n }\n\n if (typeof stepConfig.element !== 'undefined' && typeof stepConfig.target === 'undefined') {\n stepConfig.target = stepConfig.element;\n }\n\n if (typeof stepConfig.content !== 'undefined' && typeof stepConfig.body === 'undefined') {\n stepConfig.body = stepConfig.content;\n }\n\n stepConfig = $.extend({}, this.stepDefaults, stepConfig);\n\n stepConfig = $.extend({}, {\n attachTo: stepConfig.target,\n attachPoint: 'after',\n }, stepConfig);\n\n if (stepConfig.attachTo) {\n stepConfig.attachTo = $(stepConfig.attachTo).first();\n }\n\n return stepConfig;\n }\n\n /**\n * Fetch the actual step target from the selector.\n *\n * This should not be called until after any delay has completed.\n *\n * @method getStepTarget\n * @param {Object} stepConfig The step configuration\n * @return {$}\n */\n getStepTarget(stepConfig) {\n if (stepConfig.target) {\n return $(stepConfig.target);\n }\n\n return null;\n }\n\n /**\n * Fire any event handlers for the specified event.\n *\n * @param {String} eventName The name of the event\n * @param {Object} [detail={}] Any additional details to pass into the eveent\n * @param {Boolean} [cancelable=false] Whether preventDefault() can be called\n * @returns {CustomEvent}\n */\n dispatchEvent(\n eventName,\n detail = {},\n cancelable = false\n ) {\n return dispatchEvent(eventName, {\n // Add the tour to the detail.\n tour: this,\n ...detail,\n }, document, {\n cancelable,\n });\n }\n\n /**\n * @method addEventHandler\n * @param {string} eventName The name of the event to listen for\n * @param {function} handler The event handler to call\n * @return {Object} this.\n */\n addEventHandler(eventName, handler) {\n if (typeof this.eventHandlers[eventName] === 'undefined') {\n this.eventHandlers[eventName] = [];\n }\n\n this.eventHandlers[eventName].push(handler);\n\n return this;\n }\n\n /**\n * Process listeners for the step being shown.\n *\n * @method processStepListeners\n * @param {object} stepConfig The configuration for the step\n * @chainable\n * @return {Object} this.\n */\n processStepListeners(stepConfig) {\n this.listeners.push(\n // Next button.\n {\n node: this.currentStepNode,\n args: ['click', '[data-role=\"next\"]', $.proxy(this.next, this)]\n },\n\n // Close and end tour buttons.\n {\n node: this.currentStepNode,\n args: ['click', '[data-role=\"end\"]', $.proxy(this.endTour, this)]\n },\n\n // Click backdrop and hide tour.\n {\n node: $('[data-flexitour=\"backdrop\"]'),\n args: ['click', $.proxy(this.hide, this)]\n },\n\n // Keypresses.\n {\n node: $('body'),\n args: ['keydown', $.proxy(this.handleKeyDown, this)]\n });\n\n if (stepConfig.moveOnClick) {\n var targetNode = this.getStepTarget(stepConfig);\n this.listeners.push({\n node: targetNode,\n args: ['click', $.proxy(function(e) {\n if ($(e.target).parents('[data-flexitour=\"container\"]').length === 0) {\n // Ignore clicks when they are in the flexitour.\n window.setTimeout($.proxy(this.next, this), 500);\n }\n }, this)]\n });\n }\n\n this.listeners.forEach(function(listener) {\n listener.node.on.apply(listener.node, listener.args);\n });\n\n return this;\n }\n\n /**\n * Reset step listeners.\n *\n * @method resetStepListeners\n * @chainable\n * @return {Object} this.\n */\n resetStepListeners() {\n // Stop listening to all external handlers.\n if (this.listeners) {\n this.listeners.forEach(function(listener) {\n listener.node.off.apply(listener.node, listener.args);\n });\n }\n this.listeners = [];\n\n return this;\n }\n\n /**\n * The standard step renderer.\n *\n * @method renderStep\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n renderStep(stepConfig) {\n // Store the current step configuration for later.\n this.currentStepConfig = stepConfig;\n this.setCurrentStepNumber(stepConfig.stepNumber);\n\n // Fetch the template and convert it to a $ object.\n let template = $(this.getTemplateContent());\n\n // Title.\n template.find('[data-placeholder=\"title\"]')\n .html(stepConfig.title);\n\n // Body.\n template.find('[data-placeholder=\"body\"]')\n .html(stepConfig.body);\n\n // Buttons.\n const nextBtn = template.find('[data-role=\"next\"]');\n const endBtn = template.find('[data-role=\"end\"]');\n\n // Is this the final step?\n if (this.isLastStep(stepConfig.stepNumber)) {\n nextBtn.hide();\n endBtn.removeClass(\"btn-secondary\").addClass(\"btn-primary\");\n } else {\n nextBtn.prop('disabled', false);\n // Use Skip tour label for the End tour button.\n getString('skip_tour', 'tool_usertours').then(value => {\n endBtn.html(value);\n return;\n }).catch();\n }\n\n nextBtn.attr('role', 'button');\n endBtn.attr('role', 'button');\n\n if (this.originalConfiguration.displaystepnumbers) {\n const stepsPotentiallyVisible = this.getPotentiallyVisibleSteps();\n const totalStepsPotentiallyVisible = stepsPotentiallyVisible.length;\n const position = stepsPotentiallyVisible[stepConfig.stepNumber].position;\n if (totalStepsPotentiallyVisible > 1) {\n // Change the label of the Next button to include the sequence.\n getString('nextstep_sequence', 'tool_usertours',\n {position: position, total: totalStepsPotentiallyVisible}).then(value => {\n nextBtn.html(value);\n return;\n }).catch();\n }\n }\n\n // Replace the template with the updated version.\n stepConfig.template = template;\n\n // Add to the page.\n this.addStepToPage(stepConfig);\n\n // Process step listeners after adding to the page.\n // This uses the currentNode.\n this.processStepListeners(stepConfig);\n\n return this;\n }\n\n /**\n * Getter for the template content.\n *\n * @method getTemplateContent\n * @return {$}\n */\n getTemplateContent() {\n return $(this.templateContent).clone();\n }\n\n /**\n * Helper to add a step to the page.\n *\n * @method addStepToPage\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n addStepToPage(stepConfig) {\n // Create the stepNode from the template data.\n let currentStepNode = $('')\n .html(stepConfig.template)\n .hide();\n // Trigger the Moodle filters.\n notifyFilterContentUpdated(currentStepNode);\n\n // The scroll animation occurs on the body or html.\n let animationTarget = $('body, html')\n .stop(true, true);\n\n if (this.isStepActuallyVisible(stepConfig)) {\n let targetNode = this.getStepTarget(stepConfig);\n\n if (targetNode.parents('[data-usertour=\"scroller\"]').length) {\n animationTarget = targetNode.parents('[data-usertour=\"scroller\"]');\n }\n\n targetNode.data('flexitour', 'target');\n\n let zIndex = this.calculateZIndex(targetNode);\n if (zIndex) {\n stepConfig.zIndex = zIndex + 1;\n }\n\n if (stepConfig.zIndex) {\n currentStepNode.css('zIndex', stepConfig.zIndex + 1);\n }\n\n // Add the backdrop.\n this.positionBackdrop(stepConfig);\n\n $(document.body).append(currentStepNode);\n this.currentStepNode = currentStepNode;\n\n // Ensure that the step node is positioned.\n // Some situations mean that the value is not properly calculated without this step.\n this.currentStepNode.css({\n top: 0,\n left: 0,\n });\n\n animationTarget\n .animate({\n scrollTop: this.calculateScrollTop(stepConfig),\n }).promise().then(function() {\n this.positionStep(stepConfig);\n this.revealStep(stepConfig);\n return;\n }.bind(this))\n .catch(function() {\n // Silently fail.\n });\n\n } else if (stepConfig.orphan) {\n stepConfig.isOrphan = true;\n\n // This will be appended to the body instead.\n stepConfig.attachTo = $('body').first();\n stepConfig.attachPoint = 'append';\n\n // Add the backdrop.\n this.positionBackdrop(stepConfig);\n\n // This is an orphaned step.\n currentStepNode.addClass('orphan');\n\n // It lives in the body.\n $(document.body).append(currentStepNode);\n this.currentStepNode = currentStepNode;\n\n this.currentStepNode.css('position', 'fixed');\n\n this.currentStepPopper = new Popper(\n $('body'),\n this.currentStepNode[0], {\n removeOnDestroy: true,\n placement: stepConfig.placement + '-start',\n arrowElement: '[data-role=\"arrow\"]',\n // Empty the modifiers. We've already placed the step and don't want it moved.\n modifiers: {\n hide: {\n enabled: false,\n },\n applyStyle: {\n onLoad: null,\n enabled: false,\n },\n },\n onCreate: () => {\n // First, we need to check if the step's content contains any images.\n const images = this.currentStepNode.find('img');\n if (images.length) {\n // Images found, need to calculate the position when the image is loaded.\n images.on('load', () => {\n this.calculateStepPositionInPage(currentStepNode);\n });\n }\n this.calculateStepPositionInPage(currentStepNode);\n }\n }\n );\n\n this.revealStep(stepConfig);\n }\n\n return this;\n }\n\n /**\n * Make the given step visible.\n *\n * @method revealStep\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n revealStep(stepConfig) {\n // Fade the step in.\n this.currentStepNode.fadeIn('', $.proxy(function() {\n // Announce via ARIA.\n this.announceStep(stepConfig);\n\n // Focus on the current step Node.\n this.currentStepNode.focus();\n window.setTimeout($.proxy(function() {\n // After a brief delay, focus again.\n // There seems to be an issue with Jaws where it only reads the dialogue title initially.\n // This second focus helps it to read the full dialogue.\n if (this.currentStepNode) {\n this.currentStepNode.focus();\n }\n }, this), 100);\n\n }, this));\n\n return this;\n }\n\n /**\n * Helper to announce the step on the page.\n *\n * @method announceStep\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n announceStep(stepConfig) {\n // Setup the step Dialogue as per:\n // * https://www.w3.org/TR/wai-aria-practices/#dialog_nonmodal\n // * https://www.w3.org/TR/wai-aria-practices/#dialog_modal\n\n // Generate an ID for the current step node.\n let stepId = 'tour-step-' + this.tourName + '-' + stepConfig.stepNumber;\n this.currentStepNode.attr('id', stepId);\n\n let bodyRegion = this.currentStepNode.find('[data-placeholder=\"body\"]').first();\n bodyRegion.attr('id', stepId + '-body');\n bodyRegion.attr('role', 'document');\n\n let headerRegion = this.currentStepNode.find('[data-placeholder=\"title\"]').first();\n headerRegion.attr('id', stepId + '-title');\n headerRegion.attr('aria-labelledby', stepId + '-body');\n\n // Generally, a modal dialog has a role of dialog.\n this.currentStepNode.attr('role', 'dialog');\n this.currentStepNode.attr('tabindex', 0);\n this.currentStepNode.attr('aria-labelledby', stepId + '-title');\n this.currentStepNode.attr('aria-describedby', stepId + '-body');\n\n // Configure ARIA attributes on the target.\n let target = this.getStepTarget(stepConfig);\n if (target) {\n target.data('original-tabindex', target.attr('tabindex'));\n if (!target.attr('tabindex')) {\n target.attr('tabindex', 0);\n }\n\n target\n .data('original-describedby', target.attr('aria-describedby'))\n .attr('aria-describedby', stepId + '-body')\n ;\n }\n\n this.accessibilityShow(stepConfig);\n\n return this;\n }\n\n /**\n * Handle key down events.\n *\n * @method handleKeyDown\n * @param {EventFacade} e\n */\n handleKeyDown(e) {\n let tabbableSelector = 'a[href], link[href], [draggable=true], [contenteditable=true], ';\n tabbableSelector += ':input:enabled, [tabindex], button:enabled';\n switch (e.keyCode) {\n case 27:\n this.endTour();\n break;\n\n // 9 == Tab - trap focus for items with a backdrop.\n case 9:\n // Tab must be handled on key up only in this instance.\n (function() {\n if (!this.currentStepConfig.hasBackdrop) {\n // Trapping tab focus is only handled for those steps with a backdrop.\n return;\n }\n\n // Find all tabbable locations.\n let activeElement = $(document.activeElement);\n let stepTarget = this.getStepTarget(this.currentStepConfig);\n let tabbableNodes = $(tabbableSelector);\n let dialogContainer = $('span[data-flexitour=\"container\"]');\n let currentIndex;\n // Filter out element which is not belong to target section or dialogue.\n if (stepTarget) {\n tabbableNodes = tabbableNodes.filter(function(index, element) {\n return stepTarget !== null\n && (stepTarget.has(element).length\n || dialogContainer.has(element).length\n || stepTarget.is(element)\n || dialogContainer.is(element));\n });\n }\n\n // Find index of focusing element.\n tabbableNodes.each(function(index, element) {\n if (activeElement.is(element)) {\n currentIndex = index;\n return false;\n }\n // Keep looping.\n return true;\n });\n\n let nextIndex;\n let nextNode;\n let focusRelevant;\n if (currentIndex != void 0) {\n let direction = 1;\n if (e.shiftKey) {\n direction = -1;\n }\n nextIndex = currentIndex;\n do {\n nextIndex += direction;\n nextNode = $(tabbableNodes[nextIndex]);\n } while (nextNode.length && nextNode.is(':disabled') || nextNode.is(':hidden'));\n if (nextNode.length) {\n // A new f\n focusRelevant = nextNode.closest(stepTarget).length;\n focusRelevant = focusRelevant || nextNode.closest(this.currentStepNode).length;\n } else {\n // Unable to find the target somehow.\n focusRelevant = false;\n }\n }\n\n if (focusRelevant) {\n nextNode.focus();\n } else {\n if (e.shiftKey) {\n // Focus on the last tabbable node in the step.\n this.currentStepNode.find(tabbableSelector).last().focus();\n } else {\n if (this.currentStepConfig.isOrphan) {\n // Focus on the step - there is no target.\n this.currentStepNode.focus();\n } else {\n // Focus on the step target.\n stepTarget.focus();\n }\n }\n }\n e.preventDefault();\n }).call(this);\n break;\n }\n }\n\n /**\n * Start the current tour.\n *\n * @method startTour\n * @param {Number} startAt Which step number to start at. If not specified, starts at the last point.\n * @chainable\n * @return {Object} this.\n * @fires tool_usertours/tourStart\n * @fires tool_usertours/tourStarted\n */\n startTour(startAt) {\n if (this.storage && typeof startAt === 'undefined') {\n let storageStartValue = this.storage.getItem(this.storageKey);\n if (storageStartValue) {\n let storageStartAt = parseInt(storageStartValue, 10);\n if (storageStartAt <= this.steps.length) {\n startAt = storageStartAt;\n }\n }\n }\n\n if (typeof startAt === 'undefined') {\n startAt = this.getCurrentStepNumber();\n }\n\n const tourStartEvent = this.dispatchEvent(eventTypes.tourStart, {startAt}, true);\n if (!tourStartEvent.defaultPrevented) {\n this.gotoStep(startAt);\n this.tourRunning = true;\n this.dispatchEvent(eventTypes.tourStarted, {startAt});\n }\n\n return this;\n }\n\n /**\n * Restart the tour from the beginning, resetting the completionlag.\n *\n * @method restartTour\n * @chainable\n * @return {Object} this.\n */\n restartTour() {\n return this.startTour(0);\n }\n\n /**\n * End the current tour.\n *\n * @method endTour\n * @chainable\n * @return {Object} this.\n * @fires tool_usertours/tourEnd\n * @fires tool_usertours/tourEnded\n */\n endTour() {\n const tourEndEvent = this.dispatchEvent(eventTypes.tourEnd, {}, true);\n if (tourEndEvent.defaultPrevented) {\n return this;\n }\n\n if (this.currentStepConfig) {\n let previousTarget = this.getStepTarget(this.currentStepConfig);\n if (previousTarget) {\n if (!previousTarget.attr('tabindex')) {\n previousTarget.attr('tabindex', '-1');\n }\n previousTarget.first().focus();\n }\n }\n\n this.hide(true);\n\n this.tourRunning = false;\n this.dispatchEvent(eventTypes.tourEnded);\n\n return this;\n }\n\n /**\n * Hide any currently visible steps.\n *\n * @method hide\n * @param {Bool} transition Animate the visibility change\n * @chainable\n * @return {Object} this.\n * @fires tool_usertours/stepHide\n * @fires tool_usertours/stepHidden\n */\n hide(transition) {\n const stepHideEvent = this.dispatchEvent(eventTypes.stepHide, {}, true);\n if (stepHideEvent.defaultPrevented) {\n return this;\n }\n\n if (this.currentStepNode && this.currentStepNode.length) {\n this.currentStepNode.hide();\n if (this.currentStepPopper) {\n this.currentStepPopper.destroy();\n }\n }\n\n // Restore original target configuration.\n if (this.currentStepConfig) {\n let target = this.getStepTarget(this.currentStepConfig);\n if (target) {\n if (target.data('original-labelledby')) {\n target.attr('aria-labelledby', target.data('original-labelledby'));\n }\n\n if (target.data('original-describedby')) {\n target.attr('aria-describedby', target.data('original-describedby'));\n }\n\n if (target.data('original-tabindex')) {\n target.attr('tabindex', target.data('tabindex'));\n } else {\n // If the target does not have the tabindex attribute at the beginning. We need to remove it.\n // We should wait a little here before removing the attribute to prevent the browser from adding it again.\n window.setTimeout(() => {\n target.removeAttr('tabindex');\n }, 400);\n }\n }\n\n // Clear the step configuration.\n this.currentStepConfig = null;\n }\n\n let fadeTime = 0;\n if (transition) {\n fadeTime = 400;\n }\n\n // Remove the backdrop features.\n $('[data-flexitour=\"step-background\"]').remove();\n $('[data-flexitour=\"step-backdrop\"]').removeAttr('data-flexitour');\n $('[data-flexitour=\"backdrop\"]').fadeOut(fadeTime, function() {\n $(this).remove();\n });\n\n // Remove aria-describedby and tabindex attributes.\n if (this.currentStepNode && this.currentStepNode.length) {\n let stepId = this.currentStepNode.attr('id');\n if (stepId) {\n let currentStepElement = '[aria-describedby=\"' + stepId + '-body\"]';\n $(currentStepElement).removeAttr('tabindex');\n $(currentStepElement).removeAttr('aria-describedby');\n }\n }\n\n // Reset the listeners.\n this.resetStepListeners();\n\n this.accessibilityHide();\n\n this.dispatchEvent(eventTypes.stepHidden);\n\n this.currentStepNode = null;\n this.currentStepPopper = null;\n return this;\n }\n\n /**\n * Show the current steps.\n *\n * @method show\n * @chainable\n * @return {Object} this.\n */\n show() {\n // Show the current step.\n let startAt = this.getCurrentStepNumber();\n\n return this.gotoStep(startAt);\n }\n\n /**\n * Return the current step node.\n *\n * @method getStepContainer\n * @return {jQuery}\n */\n getStepContainer() {\n return $(this.currentStepNode);\n }\n\n /**\n * Calculate scrollTop.\n *\n * @method calculateScrollTop\n * @param {Object} stepConfig The step configuration of the step\n * @return {Number}\n */\n calculateScrollTop(stepConfig) {\n let viewportHeight = $(window).height();\n let targetNode = this.getStepTarget(stepConfig);\n\n let scrollParent = $(window);\n if (targetNode.parents('[data-usertour=\"scroller\"]').length) {\n scrollParent = targetNode.parents('[data-usertour=\"scroller\"]');\n }\n let scrollTop = scrollParent.scrollTop();\n\n if (stepConfig.placement === 'top') {\n // If the placement is top, center scroll at the top of the target.\n scrollTop = targetNode.offset().top - (viewportHeight / 2);\n } else if (stepConfig.placement === 'bottom') {\n // If the placement is bottom, center scroll at the bottom of the target.\n scrollTop = targetNode.offset().top + targetNode.height() + scrollTop - (viewportHeight / 2);\n } else if (targetNode.height() <= (viewportHeight * 0.8)) {\n // If the placement is left/right, and the target fits in the viewport, centre screen on the target\n scrollTop = targetNode.offset().top - ((viewportHeight - targetNode.height()) / 2);\n } else {\n // If the placement is left/right, and the target is bigger than the viewport, set scrollTop to target.top + buffer\n // and change step attachmentTarget to top+.\n scrollTop = targetNode.offset().top - (viewportHeight * 0.2);\n }\n\n // Never scroll over the top.\n scrollTop = Math.max(0, scrollTop);\n\n // Never scroll beyond the bottom.\n scrollTop = Math.min($(document).height() - viewportHeight, scrollTop);\n\n return Math.ceil(scrollTop);\n }\n\n /**\n * Calculate dialogue position for page middle.\n *\n * @param {jQuery} currentStepNode Current step node\n * @method calculateScrollTop\n */\n calculateStepPositionInPage(currentStepNode) {\n let top = MINSPACING;\n const viewportHeight = $(window).height();\n const stepHeight = currentStepNode.height();\n const viewportWidth = $(window).width();\n const stepWidth = currentStepNode.width();\n if (viewportHeight >= (stepHeight + (MINSPACING * 2))) {\n top = Math.ceil((viewportHeight - stepHeight) / 2);\n } else {\n const headerHeight = currentStepNode.find('.modal-header').first().outerHeight() ?? 0;\n const footerHeight = currentStepNode.find('.modal-footer').first().outerHeight() ?? 0;\n const currentStepBody = currentStepNode.find('[data-placeholder=\"body\"]').first();\n const maxHeight = viewportHeight - (MINSPACING * 2) - headerHeight - footerHeight;\n currentStepBody.css({\n 'max-height': maxHeight + 'px',\n 'overflow': 'auto',\n });\n }\n currentStepNode.offset({\n top: top,\n left: Math.ceil((viewportWidth - stepWidth) / 2)\n });\n }\n\n /**\n * Position the step on the page.\n *\n * @method positionStep\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n positionStep(stepConfig) {\n let content = this.currentStepNode;\n let thisT = this;\n if (!content || !content.length) {\n // Unable to find the step node.\n return this;\n }\n\n stepConfig.placement = this.recalculatePlacement(stepConfig);\n let flipBehavior;\n switch (stepConfig.placement) {\n case 'left':\n flipBehavior = ['left', 'right', 'top', 'bottom'];\n break;\n case 'right':\n flipBehavior = ['right', 'left', 'top', 'bottom'];\n break;\n case 'top':\n flipBehavior = ['top', 'bottom', 'right', 'left'];\n break;\n case 'bottom':\n flipBehavior = ['bottom', 'top', 'right', 'left'];\n break;\n default:\n flipBehavior = 'flip';\n break;\n }\n\n let target = this.getStepTarget(stepConfig);\n var config = {\n placement: stepConfig.placement + '-start',\n removeOnDestroy: true,\n modifiers: {\n flip: {\n behaviour: flipBehavior,\n },\n arrow: {\n element: '[data-role=\"arrow\"]',\n },\n },\n onCreate: function(data) {\n recalculateArrowPosition(data);\n recalculateStepPosition(data);\n },\n onUpdate: function(data) {\n recalculateArrowPosition(data);\n if (thisT.possitionNeedToBeRecalculated) {\n thisT.recalculatedNo++;\n thisT.possitionNeedToBeRecalculated = false;\n recalculateStepPosition(data);\n }\n },\n };\n\n let recalculateArrowPosition = function(data) {\n let placement = data.placement.split('-')[0];\n const isVertical = ['left', 'right'].indexOf(placement) !== -1;\n const arrowElement = data.instance.popper.querySelector('[data-role=\"arrow\"]');\n const stepElement = $(data.instance.popper.querySelector('[data-role=\"flexitour-step\"]'));\n if (isVertical) {\n let arrowHeight = parseFloat(window.getComputedStyle(arrowElement).height);\n let arrowOffset = parseFloat(window.getComputedStyle(arrowElement).top);\n let popperHeight = parseFloat(window.getComputedStyle(data.instance.popper).height);\n let popperOffset = parseFloat(window.getComputedStyle(data.instance.popper).top);\n let popperBorderWidth = parseFloat(stepElement.css('borderTopWidth'));\n let popperBorderRadiusWidth = parseFloat(stepElement.css('borderTopLeftRadius')) * 2;\n let arrowPos = arrowOffset + (arrowHeight / 2);\n let maxPos = popperHeight + popperOffset - popperBorderWidth - popperBorderRadiusWidth;\n let minPos = popperOffset + popperBorderWidth + popperBorderRadiusWidth;\n if (arrowPos >= maxPos || arrowPos <= minPos) {\n let newArrowPos = 0;\n if (arrowPos > (popperHeight / 2)) {\n newArrowPos = maxPos - arrowHeight;\n } else {\n newArrowPos = minPos + arrowHeight;\n }\n $(arrowElement).css('top', newArrowPos);\n }\n } else {\n let arrowWidth = parseFloat(window.getComputedStyle(arrowElement).width);\n let arrowOffset = parseFloat(window.getComputedStyle(arrowElement).left);\n let popperWidth = parseFloat(window.getComputedStyle(data.instance.popper).width);\n let popperOffset = parseFloat(window.getComputedStyle(data.instance.popper).left);\n let popperBorderWidth = parseFloat(stepElement.css('borderTopWidth'));\n let popperBorderRadiusWidth = parseFloat(stepElement.css('borderTopLeftRadius')) * 2;\n let arrowPos = arrowOffset + (arrowWidth / 2);\n let maxPos = popperWidth + popperOffset - popperBorderWidth - popperBorderRadiusWidth;\n let minPos = popperOffset + popperBorderWidth + popperBorderRadiusWidth;\n if (arrowPos >= maxPos || arrowPos <= minPos) {\n let newArrowPos = 0;\n if (arrowPos > (popperWidth / 2)) {\n newArrowPos = maxPos - arrowWidth;\n } else {\n newArrowPos = minPos + arrowWidth;\n }\n $(arrowElement).css('left', newArrowPos);\n }\n }\n };\n\n const recalculateStepPosition = function(data) {\n const placement = data.placement.split('-')[0];\n const isVertical = ['left', 'right'].indexOf(placement) !== -1;\n const popperElement = $(data.instance.popper);\n const targetElement = $(data.instance.reference);\n const arrowElement = popperElement.find('[data-role=\"arrow\"]');\n const stepElement = popperElement.find('[data-role=\"flexitour-step\"]');\n const viewportHeight = $(window).height();\n const viewportWidth = $(window).width();\n const arrowHeight = parseFloat(arrowElement.outerHeight(true));\n const popperHeight = parseFloat(popperElement.outerHeight(true));\n const targetHeight = parseFloat(targetElement.outerHeight(true));\n const arrowWidth = parseFloat(arrowElement.outerWidth(true));\n const popperWidth = parseFloat(popperElement.outerWidth(true));\n const targetWidth = parseFloat(targetElement.outerWidth(true));\n let maxHeight;\n\n if (thisT.recalculatedNo > 1) {\n // The current screen is too small, and cannot fit with the original placement.\n // We should set the placement to auto so the PopperJS can calculate the perfect placement.\n thisT.currentStepPopper.options.placement = isVertical ? 'auto-left' : 'auto-bottom';\n }\n if (thisT.recalculatedNo > 2) {\n // Return here to prevent recursive calling.\n return;\n }\n\n if (isVertical) {\n // Find the best place to put the tour: Left of right.\n const leftSpace = targetElement.offset().left > 0 ? targetElement.offset().left : 0;\n const rightSpace = viewportWidth - leftSpace - targetWidth;\n const remainingSpace = leftSpace >= rightSpace ? leftSpace : rightSpace;\n maxHeight = viewportHeight - MINSPACING * 2;\n if (remainingSpace < (popperWidth + arrowWidth)) {\n const maxWidth = remainingSpace - MINSPACING - arrowWidth;\n if (maxWidth > 0) {\n popperElement.css({\n 'max-width': maxWidth + 'px',\n });\n // Not enough space, flag true to make Popper to recalculate the position.\n thisT.possitionNeedToBeRecalculated = true;\n }\n } else if (maxHeight < popperHeight) {\n // Check if the Popper's height can fit the viewport height or not.\n // If not, set the correct max-height value for the Popper element.\n popperElement.css({\n 'max-height': maxHeight + 'px',\n });\n }\n } else {\n // Find the best place to put the tour: Top of bottom.\n const topSpace = targetElement.offset().top > 0 ? targetElement.offset().top : 0;\n const bottomSpace = viewportHeight - topSpace - targetHeight;\n const remainingSpace = topSpace >= bottomSpace ? topSpace : bottomSpace;\n maxHeight = remainingSpace - MINSPACING - arrowHeight;\n if (remainingSpace < (popperHeight + arrowHeight)) {\n // Not enough space, flag true to make Popper to recalculate the position.\n thisT.possitionNeedToBeRecalculated = true;\n }\n }\n\n // Check if the Popper's height can fit the viewport height or not.\n // If not, set the correct max-height value for the body.\n const currentStepBody = stepElement.find('[data-placeholder=\"body\"]').first();\n const headerEle = stepElement.find('.modal-header').first();\n const footerEle = stepElement.find('.modal-footer').first();\n const headerHeight = headerEle.outerHeight(true) ?? 0;\n const footerHeight = footerEle.outerHeight(true) ?? 0;\n maxHeight = maxHeight - headerHeight - footerHeight;\n if (maxHeight > 0) {\n headerEle.removeClass('minimal');\n footerEle.removeClass('minimal');\n currentStepBody.css({\n 'max-height': maxHeight + 'px',\n 'overflow': 'auto',\n });\n } else {\n headerEle.addClass('minimal');\n footerEle.addClass('minimal');\n }\n // Call the Popper update method to update the position.\n thisT.currentStepPopper.update();\n };\n\n let background = $('[data-flexitour=\"step-background\"]');\n if (background.length) {\n target = background;\n }\n this.currentStepPopper = new Popper(target, content[0], config);\n\n return this;\n }\n\n /**\n * For left/right placement, checks that there is room for the step at current window size.\n *\n * If there is not enough room, changes placement to 'top'.\n *\n * @method recalculatePlacement\n * @param {Object} stepConfig The step configuration of the step\n * @return {String} The placement after recalculate\n */\n recalculatePlacement(stepConfig) {\n const buffer = 10;\n const arrowWidth = 16;\n let target = this.getStepTarget(stepConfig);\n let widthContent = this.currentStepNode.width() + arrowWidth;\n let targetOffsetLeft = target.offset().left - buffer;\n let targetOffsetRight = target.offset().left + target.width() + buffer;\n let placement = stepConfig.placement;\n\n if (['left', 'right'].indexOf(placement) !== -1) {\n if ((targetOffsetLeft < (widthContent + buffer)) &&\n ((targetOffsetRight + widthContent + buffer) > document.documentElement.clientWidth)) {\n placement = 'top';\n }\n }\n return placement;\n }\n\n /**\n * Add the backdrop.\n *\n * @method positionBackdrop\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n positionBackdrop(stepConfig) {\n if (stepConfig.backdrop) {\n this.currentStepConfig.hasBackdrop = true;\n let backdrop = $('
');\n\n if (stepConfig.zIndex) {\n if (stepConfig.attachPoint === 'append') {\n stepConfig.attachTo.append(backdrop);\n } else {\n backdrop.insertAfter(stepConfig.attachTo);\n }\n } else {\n $('body').append(backdrop);\n }\n\n if (this.isStepActuallyVisible(stepConfig)) {\n // The step has a visible target.\n // Punch a hole through the backdrop.\n let background = $('[data-flexitour=\"step-background\"]');\n if (!background.length) {\n background = $('
');\n }\n\n let targetNode = this.getStepTarget(stepConfig);\n\n let buffer = 10;\n\n let colorNode = targetNode;\n if (buffer) {\n colorNode = $('body');\n }\n\n let drawertop = 0;\n if (targetNode.parents('[data-usertour=\"scroller\"]').length) {\n const scrollerElement = targetNode.parents('[data-usertour=\"scroller\"]');\n const navigationBuffer = scrollerElement.offset().top;\n if (scrollerElement.scrollTop() >= navigationBuffer) {\n drawertop = scrollerElement.scrollTop() - navigationBuffer;\n background.css({\n position: 'fixed'\n });\n }\n }\n\n background.css({\n width: targetNode.outerWidth() + buffer + buffer,\n height: targetNode.outerHeight() + buffer + buffer,\n left: targetNode.offset().left - buffer,\n top: targetNode.offset().top + drawertop - buffer,\n backgroundColor: this.calculateInherittedBackgroundColor(colorNode),\n });\n\n if (targetNode.offset().left < buffer) {\n background.css({\n width: targetNode.outerWidth() + targetNode.offset().left + buffer,\n left: targetNode.offset().left,\n });\n }\n\n if ((targetNode.offset().top + drawertop) < buffer) {\n background.css({\n height: targetNode.outerHeight() + targetNode.offset().top + buffer,\n top: targetNode.offset().top,\n });\n }\n\n let targetRadius = targetNode.css('borderRadius');\n if (targetRadius && targetRadius !== $('body').css('borderRadius')) {\n background.css('borderRadius', targetRadius);\n }\n\n let targetPosition = this.calculatePosition(targetNode);\n if (targetPosition === 'absolute') {\n background.css('position', 'fixed');\n }\n\n let fader = background.clone();\n fader.css({\n backgroundColor: backdrop.css('backgroundColor'),\n opacity: backdrop.css('opacity'),\n });\n fader.attr('data-flexitour', 'step-background-fader');\n\n if (targetNode.parents('[data-region=\"fixed-drawer\"]').length || !stepConfig.zIndex) {\n let targetClone = targetNode.clone();\n background.append(targetClone.first());\n }\n\n if (stepConfig.zIndex) {\n if (stepConfig.attachPoint === 'append') {\n stepConfig.attachTo.append(background);\n } else {\n fader.insertAfter(stepConfig.attachTo);\n background.insertAfter(stepConfig.attachTo);\n }\n } else {\n $('body').append(fader);\n $('body').append(background);\n }\n\n // Add the backdrop data to the actual target.\n // This is the part which actually does the work.\n targetNode.attr('data-flexitour', 'step-backdrop');\n\n if (stepConfig.zIndex) {\n backdrop.css('zIndex', stepConfig.zIndex);\n background.css('zIndex', stepConfig.zIndex + 1);\n targetNode.css('zIndex', stepConfig.zIndex + 2);\n }\n\n fader.fadeOut('2000', function() {\n $(this).remove();\n });\n }\n }\n return this;\n }\n\n /**\n * Calculate the inheritted z-index.\n *\n * @method calculateZIndex\n * @param {jQuery} elem The element to calculate z-index for\n * @return {Number} Calculated z-index\n */\n calculateZIndex(elem) {\n elem = $(elem);\n while (elem.length && elem[0] !== document) {\n // Ignore z-index if position is set to a value where z-index is ignored by the browser\n // This makes behavior of this function consistent across browsers\n // WebKit always returns auto if the element is positioned.\n let position = elem.css(\"position\");\n if (position === \"absolute\" || position === \"fixed\") {\n // IE returns 0 when zIndex is not specified\n // other browsers return a string\n // we ignore the case of nested elements with an explicit value of 0\n //
\n let value = parseInt(elem.css(\"zIndex\"), 10);\n if (!isNaN(value) && value !== 0) {\n return value;\n }\n }\n elem = elem.parent();\n }\n\n return 0;\n }\n\n /**\n * Calculate the inheritted background colour.\n *\n * @method calculateInherittedBackgroundColor\n * @param {jQuery} elem The element to calculate colour for\n * @return {String} Calculated background colour\n */\n calculateInherittedBackgroundColor(elem) {\n // Use a fake node to compare each element against.\n let fakeNode = $('
').hide();\n $('body').append(fakeNode);\n let fakeElemColor = fakeNode.css('backgroundColor');\n fakeNode.remove();\n\n elem = $(elem);\n while (elem.length && elem[0] !== document) {\n let color = elem.css('backgroundColor');\n if (color !== fakeElemColor) {\n return color;\n }\n elem = elem.parent();\n }\n\n return null;\n }\n\n /**\n * Calculate the inheritted position.\n *\n * @method calculatePosition\n * @param {jQuery} elem The element to calculate position for\n * @return {String} Calculated position\n */\n calculatePosition(elem) {\n elem = $(elem);\n while (elem.length && elem[0] !== document) {\n let position = elem.css('position');\n if (position !== 'static') {\n return position;\n }\n elem = elem.parent();\n }\n\n return null;\n }\n\n /**\n * Perform accessibility changes for step shown.\n *\n * This will add aria-hidden=\"true\" to all siblings and parent siblings.\n *\n * @method accessibilityShow\n */\n accessibilityShow() {\n let stateHolder = 'data-has-hidden';\n let attrName = 'aria-hidden';\n let hideFunction = function(child) {\n let flexitourRole = child.data('flexitour');\n if (flexitourRole) {\n switch (flexitourRole) {\n case 'container':\n case 'target':\n return;\n }\n }\n\n let hidden = child.attr(attrName);\n if (!hidden) {\n child.attr(stateHolder, true);\n Aria.hide(child);\n }\n };\n\n this.currentStepNode.siblings().each(function(index, node) {\n hideFunction($(node));\n });\n this.currentStepNode.parentsUntil('body').siblings().each(function(index, node) {\n hideFunction($(node));\n });\n }\n\n /**\n * Perform accessibility changes for step hidden.\n *\n * This will remove any newly added aria-hidden=\"true\".\n *\n * @method accessibilityHide\n */\n accessibilityHide() {\n let stateHolder = 'data-has-hidden';\n let showFunction = function(child) {\n let hidden = child.attr(stateHolder);\n if (typeof hidden !== 'undefined') {\n child.removeAttr(stateHolder);\n Aria.unhide(child);\n }\n };\n\n $('[' + stateHolder + ']').each(function(index, node) {\n showFunction($(node));\n });\n }\n};\n\nexport default Tour;\n"],"names":["constructor","config","init","eventHandlers","reset","originalConfiguration","configure","apply","this","arguments","possitionNeedToBeRecalculated","recalculatedNo","storage","window","sessionStorage","storageKey","tourName","e","hide","resetStepListeners","steps","currentStepNumber","eventName","forEach","handler","addEventHandler","resetStepDefaults","template","templateContent","checkMinimumRequirements","Error","length","loadOriginalConfiguration","stepDefaults","setStepDefaults","extend","element","placement","delay","moveOnClick","moveAfterTime","orphan","direction","getCurrentStepNumber","parseInt","setCurrentStepNumber","stepNumber","setItem","code","DOMException","QUOTA_EXCEEDED_ERR","removeItem","getNextStepNumber","nextStepNumber","isStepPotentiallyVisible","getStepConfig","getPreviousStepNumber","previousStepNumber","isLastStep","stepConfig","isStepActuallyVisible","getPotentiallyVisibleSteps","position","result","stepId","stepid","isCSSAllowed","target","getStepTarget","is","testCSSElement","document","createElement","classList","add","body","appendChild","isAllowed","getComputedStyle","display","remove","next","gotoStep","previous","endTour","_gotoStep","delayed","setTimeout","bind","fn","dispatchEvent","eventTypes","stepRender","defaultPrevented","renderStep","stepRendered","normalizeStepConfig","$","reflex","moveAfterClick","content","attachTo","attachPoint","first","detail","cancelable","tour","push","processStepListeners","listeners","node","currentStepNode","args","proxy","handleKeyDown","targetNode","parents","listener","on","off","currentStepConfig","getTemplateContent","find","html","title","nextBtn","endBtn","removeClass","addClass","prop","then","value","catch","attr","displaystepnumbers","stepsPotentiallyVisible","totalStepsPotentiallyVisible","total","addStepToPage","clone","animationTarget","stop","data","zIndex","calculateZIndex","css","positionBackdrop","append","top","left","animate","scrollTop","calculateScrollTop","promise","positionStep","revealStep","isOrphan","currentStepPopper","Popper","removeOnDestroy","arrowElement","modifiers","enabled","applyStyle","onLoad","onCreate","images","calculateStepPositionInPage","fadeIn","announceStep","focus","bodyRegion","headerRegion","accessibilityShow","tabbableSelector","keyCode","hasBackdrop","currentIndex","nextIndex","nextNode","focusRelevant","activeElement","stepTarget","tabbableNodes","dialogContainer","filter","index","has","each","shiftKey","closest","last","preventDefault","call","startTour","startAt","storageStartValue","getItem","storageStartAt","tourStart","tourRunning","tourStarted","restartTour","tourEnd","previousTarget","tourEnded","transition","stepHide","destroy","removeAttr","fadeTime","fadeOut","currentStepElement","accessibilityHide","stepHidden","show","getStepContainer","viewportHeight","height","scrollParent","offset","Math","max","min","ceil","stepHeight","viewportWidth","width","stepWidth","MINSPACING","maxHeight","outerHeight","flipBehavior","thisT","recalculatePlacement","flip","behaviour","arrow","recalculateArrowPosition","recalculateStepPosition","onUpdate","split","isVertical","indexOf","instance","popper","querySelector","stepElement","arrowHeight","parseFloat","arrowOffset","popperHeight","popperOffset","popperBorderWidth","popperBorderRadiusWidth","arrowPos","maxPos","minPos","newArrowPos","arrowWidth","popperWidth","popperElement","targetElement","reference","targetHeight","outerWidth","targetWidth","options","leftSpace","rightSpace","remainingSpace","maxWidth","topSpace","bottomSpace","currentStepBody","headerEle","footerEle","update","background","widthContent","targetOffsetLeft","targetOffsetRight","documentElement","clientWidth","backdrop","insertAfter","buffer","colorNode","drawertop","scrollerElement","navigationBuffer","backgroundColor","calculateInherittedBackgroundColor","targetRadius","calculatePosition","fader","opacity","targetClone","elem","isNaN","parent","fakeNode","fakeElemColor","color","hideFunction","child","flexitourRole","Aria","siblings","parentsUntil","unhide"],"mappings":"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"} \ No newline at end of file diff --git a/admin/tool/usertours/amd/src/managesteps.js b/admin/tool/usertours/amd/src/managesteps.js index 77c9ed95de0..d5883f72b5a 100644 --- a/admin/tool/usertours/amd/src/managesteps.js +++ b/admin/tool/usertours/amd/src/managesteps.js @@ -5,7 +5,7 @@ * @copyright 2016 Andrew Nicols */ import {prefetchStrings} from 'core/prefetch'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {confirm as confirmModal} from 'core/notification'; /** diff --git a/admin/tool/usertours/amd/src/managetours.js b/admin/tool/usertours/amd/src/managetours.js index 7bafc42456f..4ce97a4883d 100644 --- a/admin/tool/usertours/amd/src/managetours.js +++ b/admin/tool/usertours/amd/src/managetours.js @@ -5,7 +5,7 @@ * @copyright 2016 Andrew Nicols */ import {prefetchStrings} from 'core/prefetch'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {confirm as confirmModal} from 'core/notification'; /** diff --git a/admin/tool/usertours/amd/src/tour.js b/admin/tool/usertours/amd/src/tour.js index b7cb5815e58..cf80a6cecf6 100644 --- a/admin/tool/usertours/amd/src/tour.js +++ b/admin/tool/usertours/amd/src/tour.js @@ -34,7 +34,7 @@ import * as Aria from 'core/aria'; import Popper from 'core/popper'; import {dispatchEvent} from 'core/event_dispatcher'; import {eventTypes} from './events'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {prefetchStrings} from 'core/prefetch'; import {notifyFilterContentUpdated} from 'core/event'; diff --git a/badges/amd/build/backpackactions.min.js b/badges/amd/build/backpackactions.min.js index 2aac63c12e3..0527ba5ac05 100644 --- a/badges/amd/build/backpackactions.min.js +++ b/badges/amd/build/backpackactions.min.js @@ -5,6 +5,6 @@ define("core_badges/backpackactions",["exports","jquery","core_badges/selectors" * @module core_badges/backpackactions * @copyright 2020 Sara Arjona * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_selectors=_interopRequireDefault(_selectors),_pending=_interopRequireDefault(_pending),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_config=_interopRequireDefault(_config);_exports.init=()=>{const pendingPromise=new _pending.default,root=(0,_jquery.default)(_selectors.default.elements.main);registerListenerEvents(root),pendingPromise.resolve()};const registerListenerEvents=root=>{root.on("click",_selectors.default.actions.deletebackpack,(async e=>{e.preventDefault();const link=(0,_jquery.default)(e.currentTarget),modal=await buildModal(link);displayModal(modal,link)}))},buildModal=async link=>{const backpackurl=link.closest(_selectors.default.elements.backpackurl).attr("data-backpackurl");return _modal_factory.default.create({title:await(0,_str.get_string)("delexternalbackpack","core_badges"),body:await(0,_str.get_string)("delexternalbackpackconfirm","core_badges",backpackurl),type:_modal_factory.default.types.SAVE_CANCEL})},displayModal=async(modal,link)=>{modal.setSaveButtonText(await(0,_str.get_string)("delete","core")),modal.getRoot().on(_modal_events.default.save,(function(){window.location.href=link.attr("href")+"&sesskey="+_config.default.sesskey+"&confirm=1"})),modal.getRoot().on(_modal_events.default.hidden,(function(){modal.destroy()})),modal.show()}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_selectors=_interopRequireDefault(_selectors),_pending=_interopRequireDefault(_pending),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_config=_interopRequireDefault(_config);_exports.init=()=>{const pendingPromise=new _pending.default,root=(0,_jquery.default)(_selectors.default.elements.main);registerListenerEvents(root),pendingPromise.resolve()};const registerListenerEvents=root=>{root.on("click",_selectors.default.actions.deletebackpack,(async e=>{e.preventDefault();const link=(0,_jquery.default)(e.currentTarget),modal=await buildModal(link);displayModal(modal,link)}))},buildModal=async link=>{const backpackurl=link.closest(_selectors.default.elements.backpackurl).attr("data-backpackurl");return _modal_factory.default.create({title:await(0,_str.getString)("delexternalbackpack","core_badges"),body:await(0,_str.getString)("delexternalbackpackconfirm","core_badges",backpackurl),type:_modal_factory.default.types.SAVE_CANCEL})},displayModal=async(modal,link)=>{modal.setSaveButtonText(await(0,_str.getString)("delete","core")),modal.getRoot().on(_modal_events.default.save,(function(){window.location.href=link.attr("href")+"&sesskey="+_config.default.sesskey+"&confirm=1"})),modal.getRoot().on(_modal_events.default.hidden,(function(){modal.destroy()})),modal.show()}})); //# sourceMappingURL=backpackactions.min.js.map \ No newline at end of file diff --git a/badges/amd/build/backpackactions.min.js.map b/badges/amd/build/backpackactions.min.js.map index d409d5dce56..6c4df9db156 100644 --- a/badges/amd/build/backpackactions.min.js.map +++ b/badges/amd/build/backpackactions.min.js.map @@ -1 +1 @@ -{"version":3,"file":"backpackactions.min.js","sources":["../src/backpackactions.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 * Action methods related to backpacks.\n *\n * @module core_badges/backpackactions\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport selectors from 'core_badges/selectors';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Config from 'core/config';\n\n/**\n * Set up the actions.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n\n const root = $(selectors.elements.main);\n registerListenerEvents(root);\n\n pendingPromise.resolve();\n};\n\n/**\n * Register backpack related event listeners.\n *\n * @method registerListenerEvents\n * @param {Object} root The root element.\n */\nconst registerListenerEvents = (root) => {\n\n root.on('click', selectors.actions.deletebackpack, async(e) => {\n e.preventDefault();\n\n const link = $(e.currentTarget);\n const modal = await buildModal(link);\n\n displayModal(modal, link);\n });\n};\n\nconst buildModal = async(link) => {\n\n const backpackurl = link.closest(selectors.elements.backpackurl).attr('data-backpackurl');\n\n return ModalFactory.create({\n title: await getString('delexternalbackpack', 'core_badges'),\n body: await getString('delexternalbackpackconfirm', 'core_badges', backpackurl),\n type: ModalFactory.types.SAVE_CANCEL,\n });\n\n};\n\nconst displayModal = async(modal, link) => {\n modal.setSaveButtonText(await getString('delete', 'core'));\n\n modal.getRoot().on(ModalEvents.save, function() {\n window.location.href = link.attr('href') + '&sesskey=' + Config.sesskey + '&confirm=1';\n });\n\n modal.getRoot().on(ModalEvents.hidden, function() {\n modal.destroy();\n });\n\n modal.show();\n};\n"],"names":["pendingPromise","Pending","root","selectors","elements","main","registerListenerEvents","resolve","on","actions","deletebackpack","async","e","preventDefault","link","currentTarget","modal","buildModal","displayModal","backpackurl","closest","attr","ModalFactory","create","title","body","type","types","SAVE_CANCEL","setSaveButtonText","getRoot","ModalEvents","save","window","location","href","Config","sesskey","hidden","destroy","show"],"mappings":";;;;;;;kXAoCoB,WACVA,eAAiB,IAAIC,iBAErBC,MAAO,mBAAEC,mBAAUC,SAASC,MAClCC,uBAAuBJ,MAEvBF,eAAeO,iBASbD,uBAA0BJ,OAE5BA,KAAKM,GAAG,QAASL,mBAAUM,QAAQC,gBAAgBC,MAAAA,IAC/CC,EAAEC,uBAEIC,MAAO,mBAAEF,EAAEG,eACXC,YAAcC,WAAWH,MAE/BI,aAAaF,MAAOF,UAItBG,WAAaN,MAAAA,aAETQ,YAAcL,KAAKM,QAAQjB,mBAAUC,SAASe,aAAaE,KAAK,2BAE/DC,uBAAaC,OAAO,CACvBC,YAAa,mBAAU,sBAAuB,eAC9CC,WAAY,mBAAU,6BAA8B,cAAeN,aACnEO,KAAMJ,uBAAaK,MAAMC,eAK3BV,aAAeP,MAAMK,MAAOF,QAC9BE,MAAMa,wBAAwB,mBAAU,SAAU,SAElDb,MAAMc,UAAUtB,GAAGuB,sBAAYC,MAAM,WACjCC,OAAOC,SAASC,KAAOrB,KAAKO,KAAK,QAAU,YAAce,gBAAOC,QAAU,gBAG9ErB,MAAMc,UAAUtB,GAAGuB,sBAAYO,QAAQ,WACnCtB,MAAMuB,aAGVvB,MAAMwB"} \ No newline at end of file +{"version":3,"file":"backpackactions.min.js","sources":["../src/backpackactions.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 * Action methods related to backpacks.\n *\n * @module core_badges/backpackactions\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport selectors from 'core_badges/selectors';\nimport {getString} from 'core/str';\nimport Pending from 'core/pending';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Config from 'core/config';\n\n/**\n * Set up the actions.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n\n const root = $(selectors.elements.main);\n registerListenerEvents(root);\n\n pendingPromise.resolve();\n};\n\n/**\n * Register backpack related event listeners.\n *\n * @method registerListenerEvents\n * @param {Object} root The root element.\n */\nconst registerListenerEvents = (root) => {\n\n root.on('click', selectors.actions.deletebackpack, async(e) => {\n e.preventDefault();\n\n const link = $(e.currentTarget);\n const modal = await buildModal(link);\n\n displayModal(modal, link);\n });\n};\n\nconst buildModal = async(link) => {\n\n const backpackurl = link.closest(selectors.elements.backpackurl).attr('data-backpackurl');\n\n return ModalFactory.create({\n title: await getString('delexternalbackpack', 'core_badges'),\n body: await getString('delexternalbackpackconfirm', 'core_badges', backpackurl),\n type: ModalFactory.types.SAVE_CANCEL,\n });\n\n};\n\nconst displayModal = async(modal, link) => {\n modal.setSaveButtonText(await getString('delete', 'core'));\n\n modal.getRoot().on(ModalEvents.save, function() {\n window.location.href = link.attr('href') + '&sesskey=' + Config.sesskey + '&confirm=1';\n });\n\n modal.getRoot().on(ModalEvents.hidden, function() {\n modal.destroy();\n });\n\n modal.show();\n};\n"],"names":["pendingPromise","Pending","root","selectors","elements","main","registerListenerEvents","resolve","on","actions","deletebackpack","async","e","preventDefault","link","currentTarget","modal","buildModal","displayModal","backpackurl","closest","attr","ModalFactory","create","title","body","type","types","SAVE_CANCEL","setSaveButtonText","getRoot","ModalEvents","save","window","location","href","Config","sesskey","hidden","destroy","show"],"mappings":";;;;;;;kXAoCoB,WACVA,eAAiB,IAAIC,iBAErBC,MAAO,mBAAEC,mBAAUC,SAASC,MAClCC,uBAAuBJ,MAEvBF,eAAeO,iBASbD,uBAA0BJ,OAE5BA,KAAKM,GAAG,QAASL,mBAAUM,QAAQC,gBAAgBC,MAAAA,IAC/CC,EAAEC,uBAEIC,MAAO,mBAAEF,EAAEG,eACXC,YAAcC,WAAWH,MAE/BI,aAAaF,MAAOF,UAItBG,WAAaN,MAAAA,aAETQ,YAAcL,KAAKM,QAAQjB,mBAAUC,SAASe,aAAaE,KAAK,2BAE/DC,uBAAaC,OAAO,CACvBC,YAAa,kBAAU,sBAAuB,eAC9CC,WAAY,kBAAU,6BAA8B,cAAeN,aACnEO,KAAMJ,uBAAaK,MAAMC,eAK3BV,aAAeP,MAAMK,MAAOF,QAC9BE,MAAMa,wBAAwB,kBAAU,SAAU,SAElDb,MAAMc,UAAUtB,GAAGuB,sBAAYC,MAAM,WACjCC,OAAOC,SAASC,KAAOrB,KAAKO,KAAK,QAAU,YAAce,gBAAOC,QAAU,gBAG9ErB,MAAMc,UAAUtB,GAAGuB,sBAAYO,QAAQ,WACnCtB,MAAMuB,aAGVvB,MAAMwB"} \ No newline at end of file diff --git a/badges/amd/src/backpackactions.js b/badges/amd/src/backpackactions.js index 7cb97d5cc7a..03daf30b869 100644 --- a/badges/amd/src/backpackactions.js +++ b/badges/amd/src/backpackactions.js @@ -23,7 +23,7 @@ import $ from 'jquery'; import selectors from 'core_badges/selectors'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Pending from 'core/pending'; import ModalFactory from 'core/modal_factory'; import ModalEvents from 'core/modal_events'; diff --git a/blocks/amd/build/add_modal.min.js b/blocks/amd/build/add_modal.min.js index 3f2d8e54cf2..ac5b2410399 100644 --- a/blocks/amd/build/add_modal.min.js +++ b/blocks/amd/build/add_modal.min.js @@ -5,6 +5,6 @@ define("core_block/add_modal",["exports","core/modal_factory","core/templates"," * @module core_block/add_modal * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modal_factory=_interopRequireDefault(_modal_factory),_templates=_interopRequireDefault(_templates),_ajax=_interopRequireDefault(_ajax),_modalform=_interopRequireDefault(_modalform);const SELECTORS_ADD_BLOCK='[data-key="addblock"]',SELECTORS_SHOW_BLOCK_FORM='[data-action="showaddblockform"][data-blockname][data-blockform]';let listenerEventsRegistered=!1;const registerListenerEvents=(addBlockUrl,pagehash)=>{let addBlockModal=null;document.addEventListener("click",(e=>{const showAddBlockForm=e.target.closest(SELECTORS_SHOW_BLOCK_FORM);if(showAddBlockForm){e.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.get_string)("addblock","core_block",showAddBlockForm.getAttribute("data-blocktitle"))},args:{blockname:showAddBlockForm.getAttribute("data-blockname"),pagehash:pagehash,blockregion:showAddBlockForm.getAttribute("data-blockregion")},formClass:showAddBlockForm.getAttribute("data-blockform"),returnFocus:showAddBlockForm});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(()=>{addBlockModal.destroy(),window.location.reload()})),modalForm.show()}const addBlock=e.target.closest(SELECTORS_ADD_BLOCK);if(addBlock){e.preventDefault();let addBlockModalUrl=null!=addBlockUrl?addBlockUrl:addBlock.dataset.url;buildAddBlockModal().then((modal=>{addBlockModal=modal;const modalBody=renderBlocks(addBlockModalUrl,pagehash,addBlock.getAttribute("data-blockregion"));return modal.setBody(modalBody),modal.show(),modalBody})).catch((()=>{addBlockModal.destroy()}))}}))},buildAddBlockModal=()=>_modal_factory.default.create({type:_modal_factory.default.types.CANCEL,title:(0,_str.get_string)("addblock")}),renderBlocks=async(addBlockUrl,pagehash,region)=>{const blocks=await getAddableBlocks(pagehash);return _templates.default.render("core/add_block_body",{blocks:blocks,url:addBlockUrl,blockregion:region,pagehash:pagehash})},getAddableBlocks=async pagehash=>{const request={methodname:"core_block_fetch_addable_blocks",args:{pagecontextid:0,pagetype:"",pagelayout:"",subpage:"",pagehash:pagehash}};return _ajax.default.call([request])[0]};_exports.init=function(){let addBlockUrl=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,pagehash=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";listenerEventsRegistered||(registerListenerEvents(addBlockUrl,pagehash),listenerEventsRegistered=!0)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modal_factory=_interopRequireDefault(_modal_factory),_templates=_interopRequireDefault(_templates),_ajax=_interopRequireDefault(_ajax),_modalform=_interopRequireDefault(_modalform);const SELECTORS_ADD_BLOCK='[data-key="addblock"]',SELECTORS_SHOW_BLOCK_FORM='[data-action="showaddblockform"][data-blockname][data-blockform]';let listenerEventsRegistered=!1;const registerListenerEvents=(addBlockUrl,pagehash)=>{let addBlockModal=null;document.addEventListener("click",(e=>{const showAddBlockForm=e.target.closest(SELECTORS_SHOW_BLOCK_FORM);if(showAddBlockForm){e.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("addblock","core_block",showAddBlockForm.getAttribute("data-blocktitle"))},args:{blockname:showAddBlockForm.getAttribute("data-blockname"),pagehash:pagehash,blockregion:showAddBlockForm.getAttribute("data-blockregion")},formClass:showAddBlockForm.getAttribute("data-blockform"),returnFocus:showAddBlockForm});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(()=>{addBlockModal.destroy(),window.location.reload()})),modalForm.show()}const addBlock=e.target.closest(SELECTORS_ADD_BLOCK);if(addBlock){e.preventDefault();let addBlockModalUrl=null!=addBlockUrl?addBlockUrl:addBlock.dataset.url;buildAddBlockModal().then((modal=>{addBlockModal=modal;const modalBody=renderBlocks(addBlockModalUrl,pagehash,addBlock.getAttribute("data-blockregion"));return modal.setBody(modalBody),modal.show(),modalBody})).catch((()=>{addBlockModal.destroy()}))}}))},buildAddBlockModal=()=>_modal_factory.default.create({type:_modal_factory.default.types.CANCEL,title:(0,_str.getString)("addblock")}),renderBlocks=async(addBlockUrl,pagehash,region)=>{const blocks=await getAddableBlocks(pagehash);return _templates.default.render("core/add_block_body",{blocks:blocks,url:addBlockUrl,blockregion:region,pagehash:pagehash})},getAddableBlocks=async pagehash=>{const request={methodname:"core_block_fetch_addable_blocks",args:{pagecontextid:0,pagetype:"",pagelayout:"",subpage:"",pagehash:pagehash}};return _ajax.default.call([request])[0]};_exports.init=function(){let addBlockUrl=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,pagehash=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";listenerEventsRegistered||(registerListenerEvents(addBlockUrl,pagehash),listenerEventsRegistered=!0)}})); //# sourceMappingURL=add_modal.min.js.map \ No newline at end of file diff --git a/blocks/amd/build/add_modal.min.js.map b/blocks/amd/build/add_modal.min.js.map index 32e068ba07a..8ad213dd9f5 100644 --- a/blocks/amd/build/add_modal.min.js.map +++ b/blocks/amd/build/add_modal.min.js.map @@ -1 +1 @@ -{"version":3,"file":"add_modal.min.js","sources":["../src/add_modal.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 * Show an add block modal instead of doing it on a separate page.\n *\n * @module core_block/add_modal\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalFactory from 'core/modal_factory';\nimport Templates from 'core/templates';\nimport {get_string as getString} from 'core/str';\nimport Ajax from 'core/ajax';\nimport ModalForm from \"core_form/modalform\";\n\nconst SELECTORS = {\n ADD_BLOCK: '[data-key=\"addblock\"]',\n SHOW_BLOCK_FORM: '[data-action=\"showaddblockform\"][data-blockname][data-blockform]'\n};\n\n// Ensure we only add our listeners once.\nlet listenerEventsRegistered = false;\n\n/**\n * Register related event listeners.\n *\n * @method registerListenerEvents\n * @param {String|null} addBlockUrl The add block URL\n * @param {String} pagehash\n */\nconst registerListenerEvents = (addBlockUrl, pagehash) => {\n let addBlockModal = null;\n document.addEventListener('click', e => {\n\n const showAddBlockForm = e.target.closest(SELECTORS.SHOW_BLOCK_FORM);\n if (showAddBlockForm) {\n e.preventDefault();\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('addblock', 'core_block',\n showAddBlockForm.getAttribute('data-blocktitle')),\n },\n args: {blockname: showAddBlockForm.getAttribute('data-blockname'), pagehash,\n blockregion: showAddBlockForm.getAttribute('data-blockregion')},\n formClass: showAddBlockForm.getAttribute('data-blockform'),\n returnFocus: showAddBlockForm,\n });\n\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, () => {\n addBlockModal.destroy();\n window.location.reload();\n });\n\n modalForm.show();\n }\n\n const addBlock = e.target.closest(SELECTORS.ADD_BLOCK);\n if (addBlock) {\n e.preventDefault();\n\n let addBlockModalUrl = addBlockUrl ?? addBlock.dataset.url;\n\n buildAddBlockModal()\n .then(modal => {\n addBlockModal = modal;\n const modalBody = renderBlocks(addBlockModalUrl, pagehash,\n addBlock.getAttribute('data-blockregion'));\n modal.setBody(modalBody);\n modal.show();\n\n return modalBody;\n })\n .catch(() => {\n addBlockModal.destroy();\n });\n }\n });\n};\n\n/**\n * Method that creates the 'add block' modal.\n *\n * @method buildAddBlockModal\n * @returns {Promise} The modal promise (modal's body will be rendered later).\n */\nconst buildAddBlockModal = () => {\n return ModalFactory.create({\n type: ModalFactory.types.CANCEL,\n title: getString('addblock')\n });\n};\n\n/**\n * Method that renders the list of available blocks.\n *\n * @method renderBlocks\n * @param {String} addBlockUrl The add block URL\n * @param {String} pagehash\n * @param {String} region\n * @return {Promise}\n */\nconst renderBlocks = async(addBlockUrl, pagehash, region) => {\n // Fetch all addable blocks in the given page.\n const blocks = await getAddableBlocks(pagehash);\n\n return Templates.render('core/add_block_body', {\n blocks: blocks,\n url: addBlockUrl,\n blockregion: region,\n pagehash\n });\n};\n\n/**\n * Method that fetches all addable blocks in a given page.\n *\n * @method getAddableBlocks\n * @param {String} pagehash\n * @return {Promise}\n */\nconst getAddableBlocks = async(pagehash) => {\n const request = {\n methodname: 'core_block_fetch_addable_blocks',\n args: {\n pagecontextid: 0,\n pagetype: '',\n pagelayout: '',\n subpage: '',\n pagehash: pagehash,\n },\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Set up the actions.\n *\n * @method init\n * @param {String} addBlockUrl The add block URL\n * @param {String} pagehash\n */\nexport const init = (addBlockUrl = null, pagehash = '') => {\n if (!listenerEventsRegistered) {\n registerListenerEvents(addBlockUrl, pagehash);\n listenerEventsRegistered = true;\n }\n};\n"],"names":["SELECTORS","listenerEventsRegistered","registerListenerEvents","addBlockUrl","pagehash","addBlockModal","document","addEventListener","e","showAddBlockForm","target","closest","preventDefault","modalForm","ModalForm","modalConfig","title","getAttribute","args","blockname","blockregion","formClass","returnFocus","events","FORM_SUBMITTED","destroy","window","location","reload","show","addBlock","addBlockModalUrl","dataset","url","buildAddBlockModal","then","modal","modalBody","renderBlocks","setBody","catch","ModalFactory","create","type","types","CANCEL","async","region","blocks","getAddableBlocks","Templates","render","request","methodname","pagecontextid","pagetype","pagelayout","subpage","Ajax","call"],"mappings":";;;;;;;8QA6BMA,oBACS,wBADTA,0BAEe,uEAIjBC,0BAA2B,QASzBC,uBAAyB,CAACC,YAAaC,gBACrCC,cAAgB,KACpBC,SAASC,iBAAiB,SAASC,UAEzBC,iBAAmBD,EAAEE,OAAOC,QAAQX,8BACtCS,iBAAkB,CAClBD,EAAEI,uBAEIC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,mBAAU,WAAY,aACzBP,iBAAiBQ,aAAa,qBAEtCC,KAAM,CAACC,UAAWV,iBAAiBQ,aAAa,kBAAmBb,SAAAA,SAC/DgB,YAAaX,iBAAiBQ,aAAa,qBAC/CI,UAAWZ,iBAAiBQ,aAAa,kBACzCK,YAAab,mBAGjBI,UAAUN,iBAAiBM,UAAUU,OAAOC,gBAAgB,KACxDnB,cAAcoB,UACdC,OAAOC,SAASC,YAGpBf,UAAUgB,aAGRC,SAAWtB,EAAEE,OAAOC,QAAQX,wBAC9B8B,SAAU,CACVtB,EAAEI,qBAEEmB,iBAAmB5B,MAAAA,YAAAA,YAAe2B,SAASE,QAAQC,IAEvDC,qBACKC,MAAKC,QACF/B,cAAgB+B,YACVC,UAAYC,aAAaP,iBAAkB3B,SAC7C0B,SAASb,aAAa,4BAC1BmB,MAAMG,QAAQF,WACdD,MAAMP,OAECQ,aAEVG,OAAM,KACHnC,cAAcoB,kBAY5BS,mBAAqB,IAChBO,uBAAaC,OAAO,CACvBC,KAAMF,uBAAaG,MAAMC,OACzB7B,OAAO,mBAAU,cAanBsB,aAAeQ,MAAM3C,YAAaC,SAAU2C,gBAExCC,aAAeC,iBAAiB7C,iBAE/B8C,mBAAUC,OAAO,sBAAuB,CAC3CH,OAAQA,OACRf,IAAK9B,YACLiB,YAAa2B,OACb3C,SAAAA,YAWF6C,iBAAmBH,MAAAA,iBACfM,QAAU,CACZC,WAAY,kCACZnC,KAAM,CACFoC,cAAe,EACfC,SAAU,GACVC,WAAY,GACZC,QAAS,GACTrD,SAAUA,kBAIXsD,cAAKC,KAAK,CAACP,UAAU,kBAUZ,eAACjD,mEAAc,KAAMC,gEAAW,GAC3CH,2BACDC,uBAAuBC,YAAaC,UACpCH,0BAA2B"} \ No newline at end of file +{"version":3,"file":"add_modal.min.js","sources":["../src/add_modal.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 * Show an add block modal instead of doing it on a separate page.\n *\n * @module core_block/add_modal\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalFactory from 'core/modal_factory';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport Ajax from 'core/ajax';\nimport ModalForm from \"core_form/modalform\";\n\nconst SELECTORS = {\n ADD_BLOCK: '[data-key=\"addblock\"]',\n SHOW_BLOCK_FORM: '[data-action=\"showaddblockform\"][data-blockname][data-blockform]'\n};\n\n// Ensure we only add our listeners once.\nlet listenerEventsRegistered = false;\n\n/**\n * Register related event listeners.\n *\n * @method registerListenerEvents\n * @param {String|null} addBlockUrl The add block URL\n * @param {String} pagehash\n */\nconst registerListenerEvents = (addBlockUrl, pagehash) => {\n let addBlockModal = null;\n document.addEventListener('click', e => {\n\n const showAddBlockForm = e.target.closest(SELECTORS.SHOW_BLOCK_FORM);\n if (showAddBlockForm) {\n e.preventDefault();\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('addblock', 'core_block',\n showAddBlockForm.getAttribute('data-blocktitle')),\n },\n args: {blockname: showAddBlockForm.getAttribute('data-blockname'), pagehash,\n blockregion: showAddBlockForm.getAttribute('data-blockregion')},\n formClass: showAddBlockForm.getAttribute('data-blockform'),\n returnFocus: showAddBlockForm,\n });\n\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, () => {\n addBlockModal.destroy();\n window.location.reload();\n });\n\n modalForm.show();\n }\n\n const addBlock = e.target.closest(SELECTORS.ADD_BLOCK);\n if (addBlock) {\n e.preventDefault();\n\n let addBlockModalUrl = addBlockUrl ?? addBlock.dataset.url;\n\n buildAddBlockModal()\n .then(modal => {\n addBlockModal = modal;\n const modalBody = renderBlocks(addBlockModalUrl, pagehash,\n addBlock.getAttribute('data-blockregion'));\n modal.setBody(modalBody);\n modal.show();\n\n return modalBody;\n })\n .catch(() => {\n addBlockModal.destroy();\n });\n }\n });\n};\n\n/**\n * Method that creates the 'add block' modal.\n *\n * @method buildAddBlockModal\n * @returns {Promise} The modal promise (modal's body will be rendered later).\n */\nconst buildAddBlockModal = () => {\n return ModalFactory.create({\n type: ModalFactory.types.CANCEL,\n title: getString('addblock')\n });\n};\n\n/**\n * Method that renders the list of available blocks.\n *\n * @method renderBlocks\n * @param {String} addBlockUrl The add block URL\n * @param {String} pagehash\n * @param {String} region\n * @return {Promise}\n */\nconst renderBlocks = async(addBlockUrl, pagehash, region) => {\n // Fetch all addable blocks in the given page.\n const blocks = await getAddableBlocks(pagehash);\n\n return Templates.render('core/add_block_body', {\n blocks: blocks,\n url: addBlockUrl,\n blockregion: region,\n pagehash\n });\n};\n\n/**\n * Method that fetches all addable blocks in a given page.\n *\n * @method getAddableBlocks\n * @param {String} pagehash\n * @return {Promise}\n */\nconst getAddableBlocks = async(pagehash) => {\n const request = {\n methodname: 'core_block_fetch_addable_blocks',\n args: {\n pagecontextid: 0,\n pagetype: '',\n pagelayout: '',\n subpage: '',\n pagehash: pagehash,\n },\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Set up the actions.\n *\n * @method init\n * @param {String} addBlockUrl The add block URL\n * @param {String} pagehash\n */\nexport const init = (addBlockUrl = null, pagehash = '') => {\n if (!listenerEventsRegistered) {\n registerListenerEvents(addBlockUrl, pagehash);\n listenerEventsRegistered = true;\n }\n};\n"],"names":["SELECTORS","listenerEventsRegistered","registerListenerEvents","addBlockUrl","pagehash","addBlockModal","document","addEventListener","e","showAddBlockForm","target","closest","preventDefault","modalForm","ModalForm","modalConfig","title","getAttribute","args","blockname","blockregion","formClass","returnFocus","events","FORM_SUBMITTED","destroy","window","location","reload","show","addBlock","addBlockModalUrl","dataset","url","buildAddBlockModal","then","modal","modalBody","renderBlocks","setBody","catch","ModalFactory","create","type","types","CANCEL","async","region","blocks","getAddableBlocks","Templates","render","request","methodname","pagecontextid","pagetype","pagelayout","subpage","Ajax","call"],"mappings":";;;;;;;8QA6BMA,oBACS,wBADTA,0BAEe,uEAIjBC,0BAA2B,QASzBC,uBAAyB,CAACC,YAAaC,gBACrCC,cAAgB,KACpBC,SAASC,iBAAiB,SAASC,UAEzBC,iBAAmBD,EAAEE,OAAOC,QAAQX,8BACtCS,iBAAkB,CAClBD,EAAEI,uBAEIC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,WAAY,aACzBP,iBAAiBQ,aAAa,qBAEtCC,KAAM,CAACC,UAAWV,iBAAiBQ,aAAa,kBAAmBb,SAAAA,SAC/DgB,YAAaX,iBAAiBQ,aAAa,qBAC/CI,UAAWZ,iBAAiBQ,aAAa,kBACzCK,YAAab,mBAGjBI,UAAUN,iBAAiBM,UAAUU,OAAOC,gBAAgB,KACxDnB,cAAcoB,UACdC,OAAOC,SAASC,YAGpBf,UAAUgB,aAGRC,SAAWtB,EAAEE,OAAOC,QAAQX,wBAC9B8B,SAAU,CACVtB,EAAEI,qBAEEmB,iBAAmB5B,MAAAA,YAAAA,YAAe2B,SAASE,QAAQC,IAEvDC,qBACKC,MAAKC,QACF/B,cAAgB+B,YACVC,UAAYC,aAAaP,iBAAkB3B,SAC7C0B,SAASb,aAAa,4BAC1BmB,MAAMG,QAAQF,WACdD,MAAMP,OAECQ,aAEVG,OAAM,KACHnC,cAAcoB,kBAY5BS,mBAAqB,IAChBO,uBAAaC,OAAO,CACvBC,KAAMF,uBAAaG,MAAMC,OACzB7B,OAAO,kBAAU,cAanBsB,aAAeQ,MAAM3C,YAAaC,SAAU2C,gBAExCC,aAAeC,iBAAiB7C,iBAE/B8C,mBAAUC,OAAO,sBAAuB,CAC3CH,OAAQA,OACRf,IAAK9B,YACLiB,YAAa2B,OACb3C,SAAAA,YAWF6C,iBAAmBH,MAAAA,iBACfM,QAAU,CACZC,WAAY,kCACZnC,KAAM,CACFoC,cAAe,EACfC,SAAU,GACVC,WAAY,GACZC,QAAS,GACTrD,SAAUA,kBAIXsD,cAAKC,KAAK,CAACP,UAAU,kBAUZ,eAACjD,mEAAc,KAAMC,gEAAW,GAC3CH,2BACDC,uBAAuBC,YAAaC,UACpCH,0BAA2B"} \ No newline at end of file diff --git a/blocks/amd/src/add_modal.js b/blocks/amd/src/add_modal.js index a1d0f573ec8..74732917433 100644 --- a/blocks/amd/src/add_modal.js +++ b/blocks/amd/src/add_modal.js @@ -23,7 +23,7 @@ import ModalFactory from 'core/modal_factory'; import Templates from 'core/templates'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Ajax from 'core/ajax'; import ModalForm from "core_form/modalform"; diff --git a/blocks/online_users/amd/build/change_user_visibility.min.js b/blocks/online_users/amd/build/change_user_visibility.min.js index ebb6923be12..511d4f46f63 100644 --- a/blocks/online_users/amd/build/change_user_visibility.min.js +++ b/blocks/online_users/amd/build/change_user_visibility.min.js @@ -6,6 +6,6 @@ define("block_online_users/change_user_visibility",["exports","core/str","core/n * @module block_online_users/change_user_visibility * @copyright 2018 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};const SELECTORS_CHANGE_VISIBILITY_LINK="#change-user-visibility",SELECTORS_CHANGE_VISIBILITY_ICON="#change-user-visibility .icon",oppositeAction=action=>"show"==action?"hide":"show",changeVisibilityLinkAttr=action=>getTitle(action).then((title=>{const link=document.querySelector(SELECTORS_CHANGE_VISIBILITY_LINK);return link.dataset.action=action,link.title=title,link})),changeVisibilityIconAttr=action=>getTitle(action).then((title=>{const icon=document.querySelector(SELECTORS_CHANGE_VISIBILITY_ICON);return icon.setAttribute("title",title),icon.setAttribute("aria-label",title),icon.closest("img")?(icon.setAttribute("src",M.util.image_url("t/".concat(action))),icon.setAttribute("alt",title)):(icon.classList.add(getIconClass(action)),icon.classList.remove(getIconClass(oppositeAction(action)))),title})),getIconClass=action=>"show"==action?"fa-eye-slash":"fa-eye",getTitle=action=>(0,_str.get_string)("online_status:".concat(action),"block_online_users");_exports.init=()=>{document.addEventListener("click",(e=>{const link=e.target.closest(SELECTORS_CHANGE_VISIBILITY_LINK);var action,userid;link&&(e.preventDefault(),action=link.dataset.action,userid=link.dataset.userid,(0,_repository.setUserPreference)("block_online_users_uservisibility","show"==action?1:0,userid).then((data=>{if(data.saved){const newAction=oppositeAction(action);changeVisibilityLinkAttr(newAction),changeVisibilityIconAttr(newAction)}return data})).catch(_notification.default.exception))}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};const SELECTORS_CHANGE_VISIBILITY_LINK="#change-user-visibility",SELECTORS_CHANGE_VISIBILITY_ICON="#change-user-visibility .icon",oppositeAction=action=>"show"==action?"hide":"show",changeVisibilityLinkAttr=action=>getTitle(action).then((title=>{const link=document.querySelector(SELECTORS_CHANGE_VISIBILITY_LINK);return link.dataset.action=action,link.title=title,link})),changeVisibilityIconAttr=action=>getTitle(action).then((title=>{const icon=document.querySelector(SELECTORS_CHANGE_VISIBILITY_ICON);return icon.setAttribute("title",title),icon.setAttribute("aria-label",title),icon.closest("img")?(icon.setAttribute("src",M.util.image_url("t/".concat(action))),icon.setAttribute("alt",title)):(icon.classList.add(getIconClass(action)),icon.classList.remove(getIconClass(oppositeAction(action)))),title})),getIconClass=action=>"show"==action?"fa-eye-slash":"fa-eye",getTitle=action=>(0,_str.getString)("online_status:".concat(action),"block_online_users");_exports.init=()=>{document.addEventListener("click",(e=>{const link=e.target.closest(SELECTORS_CHANGE_VISIBILITY_LINK);var action,userid;link&&(e.preventDefault(),action=link.dataset.action,userid=link.dataset.userid,(0,_repository.setUserPreference)("block_online_users_uservisibility","show"==action?1:0,userid).then((data=>{if(data.saved){const newAction=oppositeAction(action);changeVisibilityLinkAttr(newAction),changeVisibilityIconAttr(newAction)}return data})).catch(_notification.default.exception))}))}})); //# sourceMappingURL=change_user_visibility.min.js.map \ No newline at end of file diff --git a/blocks/online_users/amd/build/change_user_visibility.min.js.map b/blocks/online_users/amd/build/change_user_visibility.min.js.map index 2f58f211bef..5f2278e25f3 100644 --- a/blocks/online_users/amd/build/change_user_visibility.min.js.map +++ b/blocks/online_users/amd/build/change_user_visibility.min.js.map @@ -1 +1 @@ -{"version":3,"file":"change_user_visibility.min.js","sources":["../src/change_user_visibility.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 * A javascript module that handles the change of the user's visibility in the\n * online users block.\n *\n * @module block_online_users/change_user_visibility\n * @copyright 2018 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString} from 'core/str';\nimport Notification from 'core/notification';\nimport {setUserPreference} from 'core_user/repository';\n\n/**\n * Selectors.\n *\n * @access private\n * @type {Object}\n */\nconst SELECTORS = {\n CHANGE_VISIBILITY_LINK: '#change-user-visibility',\n CHANGE_VISIBILITY_ICON: '#change-user-visibility .icon',\n};\n\n/**\n * Change user visibility in the online users block.\n *\n * @method changeVisibility\n * @param {String} action\n * @param {String} userid\n * @returns {Promise}\n * @private\n */\nconst changeVisibility = (action, userid) => setUserPreference(\n 'block_online_users_uservisibility',\n action == \"show\" ? 1 : 0,\n userid,\n)\n.then((data) => {\n if (data.saved) {\n const newAction = oppositeAction(action);\n changeVisibilityLinkAttr(newAction);\n changeVisibilityIconAttr(newAction);\n }\n return data;\n}).catch(Notification.exception);\n\n/**\n * Get the opposite action.\n *\n * @method oppositeAction\n * @param {String} action\n * @return {String}\n * @private\n */\nconst oppositeAction = (action) => action == 'show' ? 'hide' : 'show';\n\n/**\n * Change the attribute values of the user visibility link in the online users block.\n *\n * @method changeVisibilityLinkAttr\n * @param {String} action\n * @returns {Promise}\n * @private\n */\nconst changeVisibilityLinkAttr = (action) => getTitle(action)\n .then((title) => {\n const link = document.querySelector(SELECTORS.CHANGE_VISIBILITY_LINK);\n link.dataset.action = action;\n link.title = title;\n return link;\n });\n\n/**\n * Change the attribute values of the user visibility icon in the online users block.\n *\n * @method changeVisibilityIconAttr\n * @param {String} action\n * @returns {Promise}\n * @private\n */\nconst changeVisibilityIconAttr = (action) => getTitle(action)\n .then((title) => {\n const icon = document.querySelector(SELECTORS.CHANGE_VISIBILITY_ICON);\n\n // Add the proper title to the icon.\n icon.setAttribute('title', title);\n icon.setAttribute('aria-label', title);\n\n if (icon.closest(\"img\")) {\n // If the icon is an image.\n icon.setAttribute('src', M.util.image_url(`t/${action}`));\n icon.setAttribute('alt', title);\n } else {\n // Add the new icon class and remove the old one.\n icon.classList.add(getIconClass(action));\n icon.classList.remove(getIconClass(oppositeAction(action)));\n }\n return title;\n });\n\n/**\n * Get the proper class for the user visibility icon in the online users block.\n *\n * @method getIconClass\n * @param {String} action\n * @return {String}\n * @private\n */\nconst getIconClass = (action) => (action == 'show') ? 'fa-eye-slash' : 'fa-eye';\n\n/**\n * Get the title description of the user visibility link in the online users block.\n *\n * @method getTitle\n * @param {String} action\n * @return {object} jQuery promise\n * @private\n */\nconst getTitle = (action) => getString(`online_status:${action}`, 'block_online_users');\n\n/**\n * Initialise change user visibility function.\n *\n * @method init\n */\nexport const init = () => {\n document.addEventListener('click', (e) => {\n const link = e.target.closest(SELECTORS.CHANGE_VISIBILITY_LINK);\n if (!link) {\n return;\n }\n e.preventDefault();\n changeVisibility(\n link.dataset.action,\n link.dataset.userid,\n );\n });\n};\n"],"names":["SELECTORS","oppositeAction","action","changeVisibilityLinkAttr","getTitle","then","title","link","document","querySelector","dataset","changeVisibilityIconAttr","icon","setAttribute","closest","M","util","image_url","classList","add","getIconClass","remove","addEventListener","e","target","userid","preventDefault","data","saved","newAction","catch","Notification","exception"],"mappings":";;;;;;;;4JAkCMA,iCACsB,0BADtBA,iCAEsB,gCAkCtBC,eAAkBC,QAAqB,QAAVA,OAAmB,OAAS,OAUzDC,yBAA4BD,QAAWE,SAASF,QACjDG,MAAMC,cACGC,KAAOC,SAASC,cAAcT,yCACpCO,KAAKG,QAAQR,OAASA,OACtBK,KAAKD,MAAQA,MACNC,QAWTI,yBAA4BT,QAAWE,SAASF,QACjDG,MAAMC,cACGM,KAAOJ,SAASC,cAAcT,yCAGpCY,KAAKC,aAAa,QAASP,OAC3BM,KAAKC,aAAa,aAAcP,OAE5BM,KAAKE,QAAQ,QAEbF,KAAKC,aAAa,MAAOE,EAAEC,KAAKC,sBAAef,UAC/CU,KAAKC,aAAa,MAAOP,SAGzBM,KAAKM,UAAUC,IAAIC,aAAalB,SAChCU,KAAKM,UAAUG,OAAOD,aAAanB,eAAeC,WAE/CI,SAWTc,aAAgBlB,QAAsB,QAAVA,OAAoB,eAAiB,SAUjEE,SAAYF,SAAW,2CAA2BA,QAAU,oCAO9C,KAChBM,SAASc,iBAAiB,SAAUC,UAC1BhB,KAAOgB,EAAEC,OAAOV,QAAQd,kCA/Fb,IAACE,OAAQuB,OAgGrBlB,OAGLgB,EAAEG,iBAnGgBxB,OAqGdK,KAAKG,QAAQR,OArGSuB,OAsGtBlB,KAAKG,QAAQe,QAtGoB,iCACzC,oCACU,QAAVvB,OAAmB,EAAI,EACvBuB,QAEHpB,MAAMsB,UACCA,KAAKC,MAAO,OACNC,UAAY5B,eAAeC,QACjCC,yBAAyB0B,WACzBlB,yBAAyBkB,kBAEtBF,QACRG,MAAMC,sBAAaC"} \ No newline at end of file +{"version":3,"file":"change_user_visibility.min.js","sources":["../src/change_user_visibility.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 * A javascript module that handles the change of the user's visibility in the\n * online users block.\n *\n * @module block_online_users/change_user_visibility\n * @copyright 2018 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getString} from 'core/str';\nimport Notification from 'core/notification';\nimport {setUserPreference} from 'core_user/repository';\n\n/**\n * Selectors.\n *\n * @access private\n * @type {Object}\n */\nconst SELECTORS = {\n CHANGE_VISIBILITY_LINK: '#change-user-visibility',\n CHANGE_VISIBILITY_ICON: '#change-user-visibility .icon',\n};\n\n/**\n * Change user visibility in the online users block.\n *\n * @method changeVisibility\n * @param {String} action\n * @param {String} userid\n * @returns {Promise}\n * @private\n */\nconst changeVisibility = (action, userid) => setUserPreference(\n 'block_online_users_uservisibility',\n action == \"show\" ? 1 : 0,\n userid,\n)\n.then((data) => {\n if (data.saved) {\n const newAction = oppositeAction(action);\n changeVisibilityLinkAttr(newAction);\n changeVisibilityIconAttr(newAction);\n }\n return data;\n}).catch(Notification.exception);\n\n/**\n * Get the opposite action.\n *\n * @method oppositeAction\n * @param {String} action\n * @return {String}\n * @private\n */\nconst oppositeAction = (action) => action == 'show' ? 'hide' : 'show';\n\n/**\n * Change the attribute values of the user visibility link in the online users block.\n *\n * @method changeVisibilityLinkAttr\n * @param {String} action\n * @returns {Promise}\n * @private\n */\nconst changeVisibilityLinkAttr = (action) => getTitle(action)\n .then((title) => {\n const link = document.querySelector(SELECTORS.CHANGE_VISIBILITY_LINK);\n link.dataset.action = action;\n link.title = title;\n return link;\n });\n\n/**\n * Change the attribute values of the user visibility icon in the online users block.\n *\n * @method changeVisibilityIconAttr\n * @param {String} action\n * @returns {Promise}\n * @private\n */\nconst changeVisibilityIconAttr = (action) => getTitle(action)\n .then((title) => {\n const icon = document.querySelector(SELECTORS.CHANGE_VISIBILITY_ICON);\n\n // Add the proper title to the icon.\n icon.setAttribute('title', title);\n icon.setAttribute('aria-label', title);\n\n if (icon.closest(\"img\")) {\n // If the icon is an image.\n icon.setAttribute('src', M.util.image_url(`t/${action}`));\n icon.setAttribute('alt', title);\n } else {\n // Add the new icon class and remove the old one.\n icon.classList.add(getIconClass(action));\n icon.classList.remove(getIconClass(oppositeAction(action)));\n }\n return title;\n });\n\n/**\n * Get the proper class for the user visibility icon in the online users block.\n *\n * @method getIconClass\n * @param {String} action\n * @return {String}\n * @private\n */\nconst getIconClass = (action) => (action == 'show') ? 'fa-eye-slash' : 'fa-eye';\n\n/**\n * Get the title description of the user visibility link in the online users block.\n *\n * @method getTitle\n * @param {String} action\n * @return {object} jQuery promise\n * @private\n */\nconst getTitle = (action) => getString(`online_status:${action}`, 'block_online_users');\n\n/**\n * Initialise change user visibility function.\n *\n * @method init\n */\nexport const init = () => {\n document.addEventListener('click', (e) => {\n const link = e.target.closest(SELECTORS.CHANGE_VISIBILITY_LINK);\n if (!link) {\n return;\n }\n e.preventDefault();\n changeVisibility(\n link.dataset.action,\n link.dataset.userid,\n );\n });\n};\n"],"names":["SELECTORS","oppositeAction","action","changeVisibilityLinkAttr","getTitle","then","title","link","document","querySelector","dataset","changeVisibilityIconAttr","icon","setAttribute","closest","M","util","image_url","classList","add","getIconClass","remove","addEventListener","e","target","userid","preventDefault","data","saved","newAction","catch","Notification","exception"],"mappings":";;;;;;;;4JAkCMA,iCACsB,0BADtBA,iCAEsB,gCAkCtBC,eAAkBC,QAAqB,QAAVA,OAAmB,OAAS,OAUzDC,yBAA4BD,QAAWE,SAASF,QACjDG,MAAMC,cACGC,KAAOC,SAASC,cAAcT,yCACpCO,KAAKG,QAAQR,OAASA,OACtBK,KAAKD,MAAQA,MACNC,QAWTI,yBAA4BT,QAAWE,SAASF,QACjDG,MAAMC,cACGM,KAAOJ,SAASC,cAAcT,yCAGpCY,KAAKC,aAAa,QAASP,OAC3BM,KAAKC,aAAa,aAAcP,OAE5BM,KAAKE,QAAQ,QAEbF,KAAKC,aAAa,MAAOE,EAAEC,KAAKC,sBAAef,UAC/CU,KAAKC,aAAa,MAAOP,SAGzBM,KAAKM,UAAUC,IAAIC,aAAalB,SAChCU,KAAKM,UAAUG,OAAOD,aAAanB,eAAeC,WAE/CI,SAWTc,aAAgBlB,QAAsB,QAAVA,OAAoB,eAAiB,SAUjEE,SAAYF,SAAW,0CAA2BA,QAAU,oCAO9C,KAChBM,SAASc,iBAAiB,SAAUC,UAC1BhB,KAAOgB,EAAEC,OAAOV,QAAQd,kCA/Fb,IAACE,OAAQuB,OAgGrBlB,OAGLgB,EAAEG,iBAnGgBxB,OAqGdK,KAAKG,QAAQR,OArGSuB,OAsGtBlB,KAAKG,QAAQe,QAtGoB,iCACzC,oCACU,QAAVvB,OAAmB,EAAI,EACvBuB,QAEHpB,MAAMsB,UACCA,KAAKC,MAAO,OACNC,UAAY5B,eAAeC,QACjCC,yBAAyB0B,WACzBlB,yBAAyBkB,kBAEtBF,QACRG,MAAMC,sBAAaC"} \ No newline at end of file diff --git a/blocks/online_users/amd/src/change_user_visibility.js b/blocks/online_users/amd/src/change_user_visibility.js index 23de8f2ee78..b9af2483de9 100644 --- a/blocks/online_users/amd/src/change_user_visibility.js +++ b/blocks/online_users/amd/src/change_user_visibility.js @@ -22,7 +22,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Notification from 'core/notification'; import {setUserPreference} from 'core_user/repository'; diff --git a/calendar/amd/build/manage_subscriptions.min.js b/calendar/amd/build/manage_subscriptions.min.js index 9005567c8df..27c40627c0e 100644 --- a/calendar/amd/build/manage_subscriptions.min.js +++ b/calendar/amd/build/manage_subscriptions.min.js @@ -6,6 +6,6 @@ define("core_calendar/manage_subscriptions",["exports","core_calendar/selectors" * @copyright 2021 Huong Nguyen * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 4.0 - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,CalendarSelectors=_interopRequireWildcard(CalendarSelectors),CalendarRepository=_interopRequireWildcard(CalendarRepository),Modal=_interopRequireWildcard(Modal),ModalEvents=_interopRequireWildcard(ModalEvents),_prefetch=(obj=_prefetch)&&obj.__esModule?obj:{default:obj};const getSubscriptionName=element=>element.closest("tr").dataset.subname,registerEventListeners=()=>{document.addEventListener("click",(e=>{const deleteAction=e.target.closest(CalendarSelectors.actions.deleteSubscription);if(deleteAction){e.preventDefault();((element,messageCode)=>{const subscriptionName=getSubscriptionName(element);return Modal.create({type:Modal.types.SAVE_CANCEL,title:(0,_str.get_string)("confirmation","admin"),body:(0,_str.get_string)(messageCode,"calendar",subscriptionName),buttons:{save:(0,_str.get_string)("yes")}}).then((modal=>(modal.getRoot().on(ModalEvents.hidden,(()=>{element.focus()})),modal.show(),modal)))})(deleteAction,"confirmsubscriptiondelete").then((modal=>(modal.getRoot().on(ModalEvents.save,(()=>{const subscriptionId=parseInt(deleteAction.closest("tr").dataset.subid);CalendarRepository.deleteSubscription(subscriptionId).then((data=>{const response=(async(element,data)=>{const subscriptionName=getSubscriptionName(element),message=data.status?await(0,_str.get_string)("subscriptionremoved","calendar",subscriptionName):data.warnings[0].message,type=data.status?"info":"error";return(0,_notification.addNotification)({message:message,type:type})})(deleteAction,data);return response.then((()=>{const subscriptionRow=(subscriptionId=>document.querySelector('tr[data-subid="'.concat(subscriptionId,'"]')))(subscriptionId);return subscriptionRow.remove()}))})).catch(_notification.exception)})),modal))).catch(_notification.exception)}})),document.addEventListener(_events.eventTypes.elementUpdated,(e=>{"core_calendar"==e.target.getAttribute("data-component")&&(0,_notification.fetchNotifications)()}))};_exports.init=()=>{_prefetch.default.prefetchStrings("moodle",["yes"]),_prefetch.default.prefetchStrings("core_admin",["confirmation"]),_prefetch.default.prefetchStrings("core_calendar",["confirmsubscriptiondelete","subscriptionremoved"]),registerEventListeners()}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,CalendarSelectors=_interopRequireWildcard(CalendarSelectors),CalendarRepository=_interopRequireWildcard(CalendarRepository),Modal=_interopRequireWildcard(Modal),ModalEvents=_interopRequireWildcard(ModalEvents),_prefetch=(obj=_prefetch)&&obj.__esModule?obj:{default:obj};const getSubscriptionName=element=>element.closest("tr").dataset.subname,registerEventListeners=()=>{document.addEventListener("click",(e=>{const deleteAction=e.target.closest(CalendarSelectors.actions.deleteSubscription);if(deleteAction){e.preventDefault();((element,messageCode)=>{const subscriptionName=getSubscriptionName(element);return Modal.create({type:Modal.types.SAVE_CANCEL,title:(0,_str.getString)("confirmation","admin"),body:(0,_str.getString)(messageCode,"calendar",subscriptionName),buttons:{save:(0,_str.getString)("yes")}}).then((modal=>(modal.getRoot().on(ModalEvents.hidden,(()=>{element.focus()})),modal.show(),modal)))})(deleteAction,"confirmsubscriptiondelete").then((modal=>(modal.getRoot().on(ModalEvents.save,(()=>{const subscriptionId=parseInt(deleteAction.closest("tr").dataset.subid);CalendarRepository.deleteSubscription(subscriptionId).then((data=>{const response=(async(element,data)=>{const subscriptionName=getSubscriptionName(element),message=data.status?await(0,_str.getString)("subscriptionremoved","calendar",subscriptionName):data.warnings[0].message,type=data.status?"info":"error";return(0,_notification.addNotification)({message:message,type:type})})(deleteAction,data);return response.then((()=>{const subscriptionRow=(subscriptionId=>document.querySelector('tr[data-subid="'.concat(subscriptionId,'"]')))(subscriptionId);return subscriptionRow.remove()}))})).catch(_notification.exception)})),modal))).catch(_notification.exception)}})),document.addEventListener(_events.eventTypes.elementUpdated,(e=>{"core_calendar"==e.target.getAttribute("data-component")&&(0,_notification.fetchNotifications)()}))};_exports.init=()=>{_prefetch.default.prefetchStrings("moodle",["yes"]),_prefetch.default.prefetchStrings("core_admin",["confirmation"]),_prefetch.default.prefetchStrings("core_calendar",["confirmsubscriptiondelete","subscriptionremoved"]),registerEventListeners()}})); //# sourceMappingURL=manage_subscriptions.min.js.map \ No newline at end of file diff --git a/calendar/amd/build/manage_subscriptions.min.js.map b/calendar/amd/build/manage_subscriptions.min.js.map index 7a9f6905260..21c9c397fc0 100644 --- a/calendar/amd/build/manage_subscriptions.min.js.map +++ b/calendar/amd/build/manage_subscriptions.min.js.map @@ -1 +1 @@ -{"version":3,"file":"manage_subscriptions.min.js","sources":["../src/manage_subscriptions.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 * A module to handle Delete/Update operations of the manage subscription page.\n *\n * @module core_calendar/manage_subscriptions\n * @copyright 2021 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n */\n\nimport * as CalendarSelectors from 'core_calendar/selectors';\nimport * as CalendarRepository from 'core_calendar/repository';\nimport * as Modal from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {exception as displayException, addNotification, fetchNotifications} from 'core/notification';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {eventTypes} from 'core/local/inplace_editable/events';\n\n/**\n * Get subscription id for given element.\n *\n * @param {HTMLElement} element update/delete link\n * @return {Number}\n */\nconst getSubscriptionId = element => {\n return parseInt(element.closest('tr').dataset.subid);\n};\n\n/**\n * Get subscription name for given element.\n *\n * @param {HTMLElement} element update/delete link\n * @return {String}\n */\nconst getSubscriptionName = element => {\n return element.closest('tr').dataset.subname;\n};\n\n/**\n * Get subscription table row for subscription id.\n *\n * @param {string} subscriptionId Subscription id\n * @return {Element}\n */\nconst getSubscriptionRow = subscriptionId => {\n return document.querySelector(`tr[data-subid=\"${subscriptionId}\"]`);\n};\n\n/**\n * Create modal.\n *\n * @param {HTMLElement} element\n * @param {string} messageCode Message code.\n * @return {promise} Promise for modal\n */\nconst createModal = (element, messageCode) => {\n const subscriptionName = getSubscriptionName(element);\n return Modal.create({\n type: Modal.types.SAVE_CANCEL,\n title: getString('confirmation', 'admin'),\n body: getString(messageCode, 'calendar', subscriptionName),\n buttons: {\n save: getString('yes')\n },\n }).then(modal => {\n modal.getRoot().on(ModalEvents.hidden, () => {\n element.focus();\n });\n modal.show();\n return modal;\n });\n};\n\n/**\n * Response handler for delete action.\n *\n * @param {HTMLElement} element\n * @param {Object} data\n * @return {Promise}\n */\nconst responseHandlerForDelete = async(element, data) => {\n const subscriptionName = getSubscriptionName(element);\n const message = data.status ? await getString('subscriptionremoved', 'calendar', subscriptionName) : data.warnings[0].message;\n const type = data.status ? 'info' : 'error';\n return addNotification({message, type});\n};\n\n/**\n * Register events for update/delete links.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', e => {\n const deleteAction = e.target.closest(CalendarSelectors.actions.deleteSubscription);\n if (deleteAction) {\n e.preventDefault();\n const modalPromise = createModal(deleteAction, 'confirmsubscriptiondelete');\n modalPromise.then(modal => {\n modal.getRoot().on(ModalEvents.save, () => {\n const subscriptionId = getSubscriptionId(deleteAction);\n CalendarRepository.deleteSubscription(subscriptionId).then(data => {\n const response = responseHandlerForDelete(deleteAction, data);\n return response.then(() => {\n const subscriptionRow = getSubscriptionRow(subscriptionId);\n return subscriptionRow.remove();\n });\n }).catch(displayException);\n });\n\n return modal;\n }).catch(displayException);\n }\n });\n\n document.addEventListener(eventTypes.elementUpdated, e => {\n const inplaceEditable = e.target;\n if (inplaceEditable.getAttribute('data-component') == 'core_calendar') {\n fetchNotifications();\n }\n });\n};\n\n/**\n * Initialises.\n */\nexport const init = () => {\n Prefetch.prefetchStrings('moodle', ['yes']);\n Prefetch.prefetchStrings('core_admin', ['confirmation']);\n Prefetch.prefetchStrings('core_calendar', ['confirmsubscriptiondelete', 'subscriptionremoved']);\n registerEventListeners();\n};\n"],"names":["getSubscriptionName","element","closest","dataset","subname","registerEventListeners","document","addEventListener","e","deleteAction","target","CalendarSelectors","actions","deleteSubscription","preventDefault","messageCode","subscriptionName","Modal","create","type","types","SAVE_CANCEL","title","body","buttons","save","then","modal","getRoot","on","ModalEvents","hidden","focus","show","createModal","subscriptionId","parseInt","subid","CalendarRepository","data","response","async","message","status","warnings","responseHandlerForDelete","subscriptionRow","querySelector","getSubscriptionRow","remove","catch","displayException","eventTypes","elementUpdated","getAttribute","prefetchStrings"],"mappings":";;;;;;;;sWAiDMA,oBAAsBC,SACjBA,QAAQC,QAAQ,MAAMC,QAAQC,QAuDnCC,uBAAyB,KAC3BC,SAASC,iBAAiB,SAASC,UACzBC,aAAeD,EAAEE,OAAOR,QAAQS,kBAAkBC,QAAQC,uBAC5DJ,aAAc,CACdD,EAAEM,iBAvCM,EAACb,QAASc,qBACpBC,iBAAmBhB,oBAAoBC,gBACtCgB,MAAMC,OAAO,CAChBC,KAAMF,MAAMG,MAAMC,YAClBC,OAAO,mBAAU,eAAgB,SACjCC,MAAM,mBAAUR,YAAa,WAAYC,kBACzCQ,QAAS,CACLC,MAAM,mBAAU,UAErBC,MAAKC,QACJA,MAAMC,UAAUC,GAAGC,YAAYC,QAAQ,KACnC9B,QAAQ+B,WAEZL,MAAMM,OACCN,UA0BkBO,CAAYzB,aAAc,6BAClCiB,MAAKC,QACdA,MAAMC,UAAUC,GAAGC,YAAYL,MAAM,WAC3BU,eAzEfC,SAyEkD3B,aAzEjCP,QAAQ,MAAMC,QAAQkC,OA0E9BC,mBAAmBzB,mBAAmBsB,gBAAgBT,MAAKa,aACjDC,SApBGC,OAAMxC,QAASsC,cACtCvB,iBAAmBhB,oBAAoBC,SACvCyC,QAAUH,KAAKI,aAAe,mBAAU,sBAAuB,WAAY3B,kBAAoBuB,KAAKK,SAAS,GAAGF,QAChHvB,KAAOoB,KAAKI,OAAS,OAAS,eAC7B,iCAAgB,CAACD,QAAAA,QAASvB,KAAAA,QAgBI0B,CAAyBpC,aAAc8B,aACjDC,SAASd,MAAK,WACXoB,gBA1DPX,CAAAA,gBAChB7B,SAASyC,uCAAgCZ,sBAyDAa,CAAmBb,uBACpCW,gBAAgBG,eAE5BC,MAAMC,4BAGNxB,SACRuB,MAAMC,6BAIjB7C,SAASC,iBAAiB6C,mBAAWC,gBAAgB7C,IAEK,iBAD9BA,EAAEE,OACN4C,aAAa,4EASrB,uBACPC,gBAAgB,SAAU,CAAC,0BAC3BA,gBAAgB,aAAc,CAAC,mCAC/BA,gBAAgB,gBAAiB,CAAC,4BAA6B,wBACxElD"} \ No newline at end of file +{"version":3,"file":"manage_subscriptions.min.js","sources":["../src/manage_subscriptions.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 * A module to handle Delete/Update operations of the manage subscription page.\n *\n * @module core_calendar/manage_subscriptions\n * @copyright 2021 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n */\n\nimport * as CalendarSelectors from 'core_calendar/selectors';\nimport * as CalendarRepository from 'core_calendar/repository';\nimport * as Modal from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {exception as displayException, addNotification, fetchNotifications} from 'core/notification';\nimport Prefetch from 'core/prefetch';\nimport {getString} from 'core/str';\nimport {eventTypes} from 'core/local/inplace_editable/events';\n\n/**\n * Get subscription id for given element.\n *\n * @param {HTMLElement} element update/delete link\n * @return {Number}\n */\nconst getSubscriptionId = element => {\n return parseInt(element.closest('tr').dataset.subid);\n};\n\n/**\n * Get subscription name for given element.\n *\n * @param {HTMLElement} element update/delete link\n * @return {String}\n */\nconst getSubscriptionName = element => {\n return element.closest('tr').dataset.subname;\n};\n\n/**\n * Get subscription table row for subscription id.\n *\n * @param {string} subscriptionId Subscription id\n * @return {Element}\n */\nconst getSubscriptionRow = subscriptionId => {\n return document.querySelector(`tr[data-subid=\"${subscriptionId}\"]`);\n};\n\n/**\n * Create modal.\n *\n * @param {HTMLElement} element\n * @param {string} messageCode Message code.\n * @return {promise} Promise for modal\n */\nconst createModal = (element, messageCode) => {\n const subscriptionName = getSubscriptionName(element);\n return Modal.create({\n type: Modal.types.SAVE_CANCEL,\n title: getString('confirmation', 'admin'),\n body: getString(messageCode, 'calendar', subscriptionName),\n buttons: {\n save: getString('yes')\n },\n }).then(modal => {\n modal.getRoot().on(ModalEvents.hidden, () => {\n element.focus();\n });\n modal.show();\n return modal;\n });\n};\n\n/**\n * Response handler for delete action.\n *\n * @param {HTMLElement} element\n * @param {Object} data\n * @return {Promise}\n */\nconst responseHandlerForDelete = async(element, data) => {\n const subscriptionName = getSubscriptionName(element);\n const message = data.status ? await getString('subscriptionremoved', 'calendar', subscriptionName) : data.warnings[0].message;\n const type = data.status ? 'info' : 'error';\n return addNotification({message, type});\n};\n\n/**\n * Register events for update/delete links.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', e => {\n const deleteAction = e.target.closest(CalendarSelectors.actions.deleteSubscription);\n if (deleteAction) {\n e.preventDefault();\n const modalPromise = createModal(deleteAction, 'confirmsubscriptiondelete');\n modalPromise.then(modal => {\n modal.getRoot().on(ModalEvents.save, () => {\n const subscriptionId = getSubscriptionId(deleteAction);\n CalendarRepository.deleteSubscription(subscriptionId).then(data => {\n const response = responseHandlerForDelete(deleteAction, data);\n return response.then(() => {\n const subscriptionRow = getSubscriptionRow(subscriptionId);\n return subscriptionRow.remove();\n });\n }).catch(displayException);\n });\n\n return modal;\n }).catch(displayException);\n }\n });\n\n document.addEventListener(eventTypes.elementUpdated, e => {\n const inplaceEditable = e.target;\n if (inplaceEditable.getAttribute('data-component') == 'core_calendar') {\n fetchNotifications();\n }\n });\n};\n\n/**\n * Initialises.\n */\nexport const init = () => {\n Prefetch.prefetchStrings('moodle', ['yes']);\n Prefetch.prefetchStrings('core_admin', ['confirmation']);\n Prefetch.prefetchStrings('core_calendar', ['confirmsubscriptiondelete', 'subscriptionremoved']);\n registerEventListeners();\n};\n"],"names":["getSubscriptionName","element","closest","dataset","subname","registerEventListeners","document","addEventListener","e","deleteAction","target","CalendarSelectors","actions","deleteSubscription","preventDefault","messageCode","subscriptionName","Modal","create","type","types","SAVE_CANCEL","title","body","buttons","save","then","modal","getRoot","on","ModalEvents","hidden","focus","show","createModal","subscriptionId","parseInt","subid","CalendarRepository","data","response","async","message","status","warnings","responseHandlerForDelete","subscriptionRow","querySelector","getSubscriptionRow","remove","catch","displayException","eventTypes","elementUpdated","getAttribute","prefetchStrings"],"mappings":";;;;;;;;sWAiDMA,oBAAsBC,SACjBA,QAAQC,QAAQ,MAAMC,QAAQC,QAuDnCC,uBAAyB,KAC3BC,SAASC,iBAAiB,SAASC,UACzBC,aAAeD,EAAEE,OAAOR,QAAQS,kBAAkBC,QAAQC,uBAC5DJ,aAAc,CACdD,EAAEM,iBAvCM,EAACb,QAASc,qBACpBC,iBAAmBhB,oBAAoBC,gBACtCgB,MAAMC,OAAO,CAChBC,KAAMF,MAAMG,MAAMC,YAClBC,OAAO,kBAAU,eAAgB,SACjCC,MAAM,kBAAUR,YAAa,WAAYC,kBACzCQ,QAAS,CACLC,MAAM,kBAAU,UAErBC,MAAKC,QACJA,MAAMC,UAAUC,GAAGC,YAAYC,QAAQ,KACnC9B,QAAQ+B,WAEZL,MAAMM,OACCN,UA0BkBO,CAAYzB,aAAc,6BAClCiB,MAAKC,QACdA,MAAMC,UAAUC,GAAGC,YAAYL,MAAM,WAC3BU,eAzEfC,SAyEkD3B,aAzEjCP,QAAQ,MAAMC,QAAQkC,OA0E9BC,mBAAmBzB,mBAAmBsB,gBAAgBT,MAAKa,aACjDC,SApBGC,OAAMxC,QAASsC,cACtCvB,iBAAmBhB,oBAAoBC,SACvCyC,QAAUH,KAAKI,aAAe,kBAAU,sBAAuB,WAAY3B,kBAAoBuB,KAAKK,SAAS,GAAGF,QAChHvB,KAAOoB,KAAKI,OAAS,OAAS,eAC7B,iCAAgB,CAACD,QAAAA,QAASvB,KAAAA,QAgBI0B,CAAyBpC,aAAc8B,aACjDC,SAASd,MAAK,WACXoB,gBA1DPX,CAAAA,gBAChB7B,SAASyC,uCAAgCZ,sBAyDAa,CAAmBb,uBACpCW,gBAAgBG,eAE5BC,MAAMC,4BAGNxB,SACRuB,MAAMC,6BAIjB7C,SAASC,iBAAiB6C,mBAAWC,gBAAgB7C,IAEK,iBAD9BA,EAAEE,OACN4C,aAAa,4EASrB,uBACPC,gBAAgB,SAAU,CAAC,0BAC3BA,gBAAgB,aAAc,CAAC,mCAC/BA,gBAAgB,gBAAiB,CAAC,4BAA6B,wBACxElD"} \ No newline at end of file diff --git a/calendar/amd/build/view_manager.min.js b/calendar/amd/build/view_manager.min.js index 797eb83e456..b496055ee51 100644 --- a/calendar/amd/build/view_manager.min.js +++ b/calendar/amd/build/view_manager.min.js @@ -5,6 +5,6 @@ define("core_calendar/view_manager",["exports","jquery","core/templates","core/n * @module core_calendar/view_manager * @copyright 2017 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.updateUrl=_exports.reloadCurrentUpcoming=_exports.reloadCurrentMonth=_exports.reloadCurrentDay=_exports.registerEventListenersForMonthDetailed=_exports.refreshMonthContent=_exports.refreshDayContent=_exports.init=_exports.foldDayEvents=_exports.changeMonth=_exports.changeDay=void 0,_jquery=_interopRequireDefault(_jquery),_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),CalendarRepository=_interopRequireWildcard(CalendarRepository),_events=_interopRequireDefault(_events),CalendarSelectors=_interopRequireWildcard(CalendarSelectors),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_summary_modal=_interopRequireDefault(_summary_modal),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_pending=_interopRequireDefault(_pending);const foldDayEvents=()=>{const days=(0,_jquery.default)(CalendarSelectors.elements.monthDetailed).find(CalendarSelectors.day);0!==days.length&&days.each((function(){const dayContainer=(0,_jquery.default)(this),eventsSelector="".concat(CalendarSelectors.elements.dateContent," ul li[data-event-eventtype]"),filteredEventsSelector="".concat(CalendarSelectors.elements.dateContent,' ul li[data-event-filtered="true"]'),moreEventsSelector="".concat(CalendarSelectors.elements.dateContent,' [data-action="view-more-events"]'),events=dayContainer.find(eventsSelector);if(0===events.length)return;const numberOfFiltered=dayContainer.find(filteredEventsSelector).length,numberOfEvents=events.length-numberOfFiltered;let count=1;events.each((function(){const event=(0,_jquery.default)(this);"true"!==event.attr("data-event-filtered")?count>5-(5===numberOfEvents?0:1)?(event.attr("data-event-folded","true"),event.hide()):(event.attr("data-event-folded","false"),event.show(),count++):event.attr("data-event-folded","false")}));const moreEventsLink=dayContainer.find(moreEventsSelector);if(numberOfEvents>5){const numberOfHiddenEvents=numberOfEvents-5+1;moreEventsLink.show(),(0,_str.get_string)("moreevents","calendar",numberOfHiddenEvents).then((str=>{const link=moreEventsLink.find("strong a");return moreEventsLink.attr("data-event-folded","false"),link.text(str),str})).fail()}else moreEventsLink.hide()}))};_exports.foldDayEvents=foldDayEvents;const registerEventListenersForMonthDetailed=pendingId=>{const events="".concat(_events.default.viewUpdated);(0,_jquery.default)("body").on(events,(function(e){foldDayEvents()})),foldDayEvents(),(0,_jquery.default)("body").on(_events.default.filterChanged,(function(e,data){const root=(0,_jquery.default)(CalendarSelectors.elements.monthDetailed),pending=new _pending.default(pendingId),target=root.find(CalendarSelectors.eventType[data.type]),transitionPromise=_jquery.default.Deferred();data.hidden?transitionPromise.then((function(){return target.attr("data-event-filtered","true"),target.hide().promise()})).fail():transitionPromise.then((function(){return target.attr("data-event-filtered","false"),target.show().promise()})).fail(),transitionPromise.then((function(){foldDayEvents()})).always(pending.resolve).fail(),transitionPromise.resolve()}))};_exports.registerEventListenersForMonthDetailed=registerEventListenersForMonthDetailed;const refreshMonthContent=function(root,year,month,courseId,categoryId){let target=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,template=arguments.length>6&&void 0!==arguments[6]?arguments[6]:"",day=arguments.length>7&&void 0!==arguments[7]?arguments[7]:1;startLoading(root),target=target||root.find(CalendarSelectors.wrapper),template=template||root.attr("data-template"),M.util.js_pending([root.get("id"),year,month,courseId].join("-"));const includenavigation=root.data("includenavigation"),mini=root.data("mini"),viewMode=target.data("view");return CalendarRepository.getCalendarMonthData(year,month,courseId,categoryId,includenavigation,mini,day,viewMode).then((context=>_templates.default.render(template,context))).then(((html,js)=>_templates.default.replaceNode(target,html,js))).then((()=>{document.querySelector("body").dispatchEvent(new CustomEvent(_events.default.viewUpdated))})).always((()=>(M.util.js_complete([root.get("id"),year,month,courseId].join("-")),stopLoading(root)))).fail(_notification.default.exception)};_exports.refreshMonthContent=refreshMonthContent;const changeMonth=function(root,url,year,month,courseId,categoryId){let day=arguments.length>6&&void 0!==arguments[6]?arguments[6]:1;return refreshMonthContent(root,year,month,courseId,categoryId,null,"",day).then((function(){url.length&&"#"!==url&&updateUrl(url);for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];return args})).then((function(){(0,_jquery.default)("body").trigger(_events.default.monthChanged,[year,month,courseId,categoryId]);for(var _len2=arguments.length,args=new Array(_len2),_key2=0;_key2<_len2;_key2++)args[_key2]=arguments[_key2];return args}))};_exports.changeMonth=changeMonth;_exports.reloadCurrentMonth=function(root){let courseId=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,categoryId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;const year=root.find(CalendarSelectors.wrapper).data("year"),month=root.find(CalendarSelectors.wrapper).data("month"),day=root.find(CalendarSelectors.wrapper).data("day");return courseId=courseId||root.find(CalendarSelectors.wrapper).data("courseid"),categoryId=categoryId||root.find(CalendarSelectors.wrapper).data("categoryid"),refreshMonthContent(root,year,month,courseId,categoryId,null,"",day).then((function(){(0,_jquery.default)("body").trigger(_events.default.courseChanged,[year,month,courseId,categoryId]);for(var _len3=arguments.length,args=new Array(_len3),_key3=0;_key3<_len3;_key3++)args[_key3]=arguments[_key3];return args}))};const refreshDayContent=function(root,year,month,day,courseId,categoryId){let target=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,template=arguments.length>7&&void 0!==arguments[7]?arguments[7]:"";startLoading(root),target&&0!=target.length||(target=root.find(CalendarSelectors.wrapper)),template=template||root.attr("data-template"),M.util.js_pending([root.get("id"),year,month,day,courseId,categoryId].join("-"));const includenavigation=root.data("includenavigation");return CalendarRepository.getCalendarDayData(year,month,day,courseId,categoryId,includenavigation).then((context=>(context.viewingday=!0,context.showviewselector=!0,_templates.default.render(template,context)))).then(((html,js)=>_templates.default.replaceNode(target,html,js))).then((()=>{document.querySelector("body").dispatchEvent(new CustomEvent(_events.default.viewUpdated))})).always((()=>(M.util.js_complete([root.get("id"),year,month,day,courseId,categoryId].join("-")),stopLoading(root)))).fail(_notification.default.exception)};_exports.refreshDayContent=refreshDayContent;_exports.reloadCurrentDay=function(root){let courseId=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,categoryId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;const wrapper=root.find(CalendarSelectors.wrapper),year=wrapper.data("year"),month=wrapper.data("month"),day=wrapper.data("day");return courseId=courseId||root.find(CalendarSelectors.wrapper).data("courseid"),categoryId=categoryId||root.find(CalendarSelectors.wrapper).data("categoryid"),refreshDayContent(root,year,month,day,courseId,categoryId)};const changeDay=(root,url,year,month,day,courseId,categoryId)=>refreshDayContent(root,year,month,day,courseId,categoryId).then((function(){url.length&&"#"!==url&&updateUrl(url);for(var _len4=arguments.length,args=new Array(_len4),_key4=0;_key4<_len4;_key4++)args[_key4]=arguments[_key4];return args})).then((function(){(0,_jquery.default)("body").trigger(_events.default.dayChanged,[year,month,courseId,categoryId]);for(var _len5=arguments.length,args=new Array(_len5),_key5=0;_key5<_len5;_key5++)args[_key5]=arguments[_key5];return args}));_exports.changeDay=changeDay;const updateUrl=url=>{document.getElementById(CalendarSelectors.fullCalendarView)&&window.history.pushState({},"",url)};_exports.updateUrl=updateUrl;const startLoading=root=>{root.find(CalendarSelectors.containers.loadingIcon).removeClass("hidden")},stopLoading=root=>{root.find(CalendarSelectors.containers.loadingIcon).addClass("hidden")},reloadCurrentUpcoming=function(root){let courseId=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,categoryId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,target=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,template=arguments.length>4&&void 0!==arguments[4]?arguments[4]:"";return startLoading(root),target=target||root.find(CalendarSelectors.wrapper),template=template||root.attr("data-template"),courseId=courseId||root.find(CalendarSelectors.wrapper).data("courseid"),categoryId=categoryId||root.find(CalendarSelectors.wrapper).data("categoryid"),CalendarRepository.getCalendarUpcomingData(courseId,categoryId).then((context=>(context.viewingupcoming=!0,context.showviewselector=!0,_templates.default.render(template,context)))).then(((html,js)=>_templates.default.replaceNode(target,html,js))).then((()=>{document.querySelector("body").dispatchEvent(new CustomEvent(_events.default.viewUpdated))})).always((function(){return stopLoading(root)})).fail(_notification.default.exception)};_exports.reloadCurrentUpcoming=reloadCurrentUpcoming;const renderEventSummaryModal=eventId=>{const pendingPromise=new _pending.default("core_calendar/view_manager:renderEventSummaryModal");return CalendarRepository.getEventById(eventId).then((getEventResponse=>{if(!getEventResponse.event)throw new Error("Error encountered while trying to fetch calendar event with ID: "+eventId);return getEventResponse.event})).then((eventData=>{const modalParams={title:eventData.name,type:_summary_modal.default.TYPE,body:_templates.default.render("core_calendar/event_summary_body",eventData),templateContext:{canedit:eventData.canedit,candelete:eventData.candelete,headerclasses:(eventType=eventData.normalisedeventtype,"calendar_event_"+eventType),isactionevent:eventData.isactionevent,url:eventData.url,action:eventData.action}};var eventType;return _modal_factory.default.create(modalParams)})).then((modal=>(modal.getRoot().on(_modal_events.default.hidden,(function(){modal.destroy()})),modal.show(),modal))).then((modal=>(pendingPromise.resolve(),modal))).catch(_notification.default.exception)};_exports.init=(root,view)=>{(0,_prefetch.prefetchStrings)("calendar",["moreevents"]),foldDayEvents(),(root=>{(root=(0,_jquery.default)(root)).on("click",CalendarSelectors.links.eventLink,(e=>{const target=e.target;let eventLink=null,eventId=null;const pendingPromise=new _pending.default("core_calendar/view_manager:eventLink:click");eventLink=target.matches(CalendarSelectors.actions.viewEvent)?target:target.closest(CalendarSelectors.actions.viewEvent),eventId=eventLink?eventLink.dataset.eventId:target.querySelector(CalendarSelectors.actions.viewEvent).dataset.eventId,eventId?(e.preventDefault(),e.stopPropagation(),renderEventSummaryModal(eventId).then(pendingPromise.resolve).catch()):pendingPromise.resolve()})),root.on("click",CalendarSelectors.links.navLink,(e=>{const wrapper=root.find(CalendarSelectors.wrapper),view=wrapper.data("view"),courseId=wrapper.data("courseid"),categoryId=wrapper.data("categoryid"),link=e.currentTarget;"month"===view||"monthblock"===view?(changeMonth(root,link.href,link.dataset.year,link.dataset.month,courseId,categoryId,link.dataset.day),e.preventDefault()):"day"===view&&(changeDay(root,link.href,link.dataset.year,link.dataset.month,link.dataset.day,courseId,categoryId),e.preventDefault())}));const viewSelector=root.find(CalendarSelectors.viewSelector);_custom_interaction_events.default.define(viewSelector,[_custom_interaction_events.default.events.activate]),viewSelector.on(_custom_interaction_events.default.events.activate,(e=>{e.preventDefault();const option=e.target;if(option.classList.contains("active"))return;const view=option.dataset.view,year=option.dataset.year,month=option.dataset.month,day=option.dataset.day,courseId=option.dataset.courseid,categoryId=option.dataset.categoryid;"month"==view?refreshMonthContent(root,year,month,courseId,categoryId,root,"core_calendar/calendar_month",day).then((()=>{updateUrl("?view=month")})).fail(_notification.default.exception):"day"==view?refreshDayContent(root,year,month,day,courseId,categoryId,root,"core_calendar/calendar_day").then((()=>{updateUrl("?view=day")})).fail(_notification.default.exception):"upcoming"==view&&reloadCurrentUpcoming(root,courseId,categoryId,root,"core_calendar/calendar_upcoming").then((()=>{updateUrl("?view=upcoming")})).fail(_notification.default.exception)}))})(root);const calendarTable=root.find(CalendarSelectors.elements.monthDetailed);if(calendarTable.length){"month-detailed-".concat(calendarTable.id,"-filterChanged");registerEventListenersForMonthDetailed(calendarTable)}}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.updateUrl=_exports.reloadCurrentUpcoming=_exports.reloadCurrentMonth=_exports.reloadCurrentDay=_exports.registerEventListenersForMonthDetailed=_exports.refreshMonthContent=_exports.refreshDayContent=_exports.init=_exports.foldDayEvents=_exports.changeMonth=_exports.changeDay=void 0,_jquery=_interopRequireDefault(_jquery),_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),CalendarRepository=_interopRequireWildcard(CalendarRepository),_events=_interopRequireDefault(_events),CalendarSelectors=_interopRequireWildcard(CalendarSelectors),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_summary_modal=_interopRequireDefault(_summary_modal),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_pending=_interopRequireDefault(_pending);const foldDayEvents=()=>{const days=(0,_jquery.default)(CalendarSelectors.elements.monthDetailed).find(CalendarSelectors.day);0!==days.length&&days.each((function(){const dayContainer=(0,_jquery.default)(this),eventsSelector="".concat(CalendarSelectors.elements.dateContent," ul li[data-event-eventtype]"),filteredEventsSelector="".concat(CalendarSelectors.elements.dateContent,' ul li[data-event-filtered="true"]'),moreEventsSelector="".concat(CalendarSelectors.elements.dateContent,' [data-action="view-more-events"]'),events=dayContainer.find(eventsSelector);if(0===events.length)return;const numberOfFiltered=dayContainer.find(filteredEventsSelector).length,numberOfEvents=events.length-numberOfFiltered;let count=1;events.each((function(){const event=(0,_jquery.default)(this);"true"!==event.attr("data-event-filtered")?count>5-(5===numberOfEvents?0:1)?(event.attr("data-event-folded","true"),event.hide()):(event.attr("data-event-folded","false"),event.show(),count++):event.attr("data-event-folded","false")}));const moreEventsLink=dayContainer.find(moreEventsSelector);if(numberOfEvents>5){const numberOfHiddenEvents=numberOfEvents-5+1;moreEventsLink.show(),(0,_str.getString)("moreevents","calendar",numberOfHiddenEvents).then((str=>{const link=moreEventsLink.find("strong a");return moreEventsLink.attr("data-event-folded","false"),link.text(str),str})).fail()}else moreEventsLink.hide()}))};_exports.foldDayEvents=foldDayEvents;const registerEventListenersForMonthDetailed=pendingId=>{const events="".concat(_events.default.viewUpdated);(0,_jquery.default)("body").on(events,(function(e){foldDayEvents()})),foldDayEvents(),(0,_jquery.default)("body").on(_events.default.filterChanged,(function(e,data){const root=(0,_jquery.default)(CalendarSelectors.elements.monthDetailed),pending=new _pending.default(pendingId),target=root.find(CalendarSelectors.eventType[data.type]),transitionPromise=_jquery.default.Deferred();data.hidden?transitionPromise.then((function(){return target.attr("data-event-filtered","true"),target.hide().promise()})).fail():transitionPromise.then((function(){return target.attr("data-event-filtered","false"),target.show().promise()})).fail(),transitionPromise.then((function(){foldDayEvents()})).always(pending.resolve).fail(),transitionPromise.resolve()}))};_exports.registerEventListenersForMonthDetailed=registerEventListenersForMonthDetailed;const refreshMonthContent=function(root,year,month,courseId,categoryId){let target=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,template=arguments.length>6&&void 0!==arguments[6]?arguments[6]:"",day=arguments.length>7&&void 0!==arguments[7]?arguments[7]:1;startLoading(root),target=target||root.find(CalendarSelectors.wrapper),template=template||root.attr("data-template"),M.util.js_pending([root.get("id"),year,month,courseId].join("-"));const includenavigation=root.data("includenavigation"),mini=root.data("mini"),viewMode=target.data("view");return CalendarRepository.getCalendarMonthData(year,month,courseId,categoryId,includenavigation,mini,day,viewMode).then((context=>_templates.default.render(template,context))).then(((html,js)=>_templates.default.replaceNode(target,html,js))).then((()=>{document.querySelector("body").dispatchEvent(new CustomEvent(_events.default.viewUpdated))})).always((()=>(M.util.js_complete([root.get("id"),year,month,courseId].join("-")),stopLoading(root)))).fail(_notification.default.exception)};_exports.refreshMonthContent=refreshMonthContent;const changeMonth=function(root,url,year,month,courseId,categoryId){let day=arguments.length>6&&void 0!==arguments[6]?arguments[6]:1;return refreshMonthContent(root,year,month,courseId,categoryId,null,"",day).then((function(){url.length&&"#"!==url&&updateUrl(url);for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];return args})).then((function(){(0,_jquery.default)("body").trigger(_events.default.monthChanged,[year,month,courseId,categoryId]);for(var _len2=arguments.length,args=new Array(_len2),_key2=0;_key2<_len2;_key2++)args[_key2]=arguments[_key2];return args}))};_exports.changeMonth=changeMonth;_exports.reloadCurrentMonth=function(root){let courseId=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,categoryId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;const year=root.find(CalendarSelectors.wrapper).data("year"),month=root.find(CalendarSelectors.wrapper).data("month"),day=root.find(CalendarSelectors.wrapper).data("day");return courseId=courseId||root.find(CalendarSelectors.wrapper).data("courseid"),categoryId=categoryId||root.find(CalendarSelectors.wrapper).data("categoryid"),refreshMonthContent(root,year,month,courseId,categoryId,null,"",day).then((function(){(0,_jquery.default)("body").trigger(_events.default.courseChanged,[year,month,courseId,categoryId]);for(var _len3=arguments.length,args=new Array(_len3),_key3=0;_key3<_len3;_key3++)args[_key3]=arguments[_key3];return args}))};const refreshDayContent=function(root,year,month,day,courseId,categoryId){let target=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,template=arguments.length>7&&void 0!==arguments[7]?arguments[7]:"";startLoading(root),target&&0!=target.length||(target=root.find(CalendarSelectors.wrapper)),template=template||root.attr("data-template"),M.util.js_pending([root.get("id"),year,month,day,courseId,categoryId].join("-"));const includenavigation=root.data("includenavigation");return CalendarRepository.getCalendarDayData(year,month,day,courseId,categoryId,includenavigation).then((context=>(context.viewingday=!0,context.showviewselector=!0,_templates.default.render(template,context)))).then(((html,js)=>_templates.default.replaceNode(target,html,js))).then((()=>{document.querySelector("body").dispatchEvent(new CustomEvent(_events.default.viewUpdated))})).always((()=>(M.util.js_complete([root.get("id"),year,month,day,courseId,categoryId].join("-")),stopLoading(root)))).fail(_notification.default.exception)};_exports.refreshDayContent=refreshDayContent;_exports.reloadCurrentDay=function(root){let courseId=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,categoryId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;const wrapper=root.find(CalendarSelectors.wrapper),year=wrapper.data("year"),month=wrapper.data("month"),day=wrapper.data("day");return courseId=courseId||root.find(CalendarSelectors.wrapper).data("courseid"),categoryId=categoryId||root.find(CalendarSelectors.wrapper).data("categoryid"),refreshDayContent(root,year,month,day,courseId,categoryId)};const changeDay=(root,url,year,month,day,courseId,categoryId)=>refreshDayContent(root,year,month,day,courseId,categoryId).then((function(){url.length&&"#"!==url&&updateUrl(url);for(var _len4=arguments.length,args=new Array(_len4),_key4=0;_key4<_len4;_key4++)args[_key4]=arguments[_key4];return args})).then((function(){(0,_jquery.default)("body").trigger(_events.default.dayChanged,[year,month,courseId,categoryId]);for(var _len5=arguments.length,args=new Array(_len5),_key5=0;_key5<_len5;_key5++)args[_key5]=arguments[_key5];return args}));_exports.changeDay=changeDay;const updateUrl=url=>{document.getElementById(CalendarSelectors.fullCalendarView)&&window.history.pushState({},"",url)};_exports.updateUrl=updateUrl;const startLoading=root=>{root.find(CalendarSelectors.containers.loadingIcon).removeClass("hidden")},stopLoading=root=>{root.find(CalendarSelectors.containers.loadingIcon).addClass("hidden")},reloadCurrentUpcoming=function(root){let courseId=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,categoryId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,target=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,template=arguments.length>4&&void 0!==arguments[4]?arguments[4]:"";return startLoading(root),target=target||root.find(CalendarSelectors.wrapper),template=template||root.attr("data-template"),courseId=courseId||root.find(CalendarSelectors.wrapper).data("courseid"),categoryId=categoryId||root.find(CalendarSelectors.wrapper).data("categoryid"),CalendarRepository.getCalendarUpcomingData(courseId,categoryId).then((context=>(context.viewingupcoming=!0,context.showviewselector=!0,_templates.default.render(template,context)))).then(((html,js)=>_templates.default.replaceNode(target,html,js))).then((()=>{document.querySelector("body").dispatchEvent(new CustomEvent(_events.default.viewUpdated))})).always((function(){return stopLoading(root)})).fail(_notification.default.exception)};_exports.reloadCurrentUpcoming=reloadCurrentUpcoming;const renderEventSummaryModal=eventId=>{const pendingPromise=new _pending.default("core_calendar/view_manager:renderEventSummaryModal");return CalendarRepository.getEventById(eventId).then((getEventResponse=>{if(!getEventResponse.event)throw new Error("Error encountered while trying to fetch calendar event with ID: "+eventId);return getEventResponse.event})).then((eventData=>{const modalParams={title:eventData.name,type:_summary_modal.default.TYPE,body:_templates.default.render("core_calendar/event_summary_body",eventData),templateContext:{canedit:eventData.canedit,candelete:eventData.candelete,headerclasses:(eventType=eventData.normalisedeventtype,"calendar_event_"+eventType),isactionevent:eventData.isactionevent,url:eventData.url,action:eventData.action}};var eventType;return _modal_factory.default.create(modalParams)})).then((modal=>(modal.getRoot().on(_modal_events.default.hidden,(function(){modal.destroy()})),modal.show(),modal))).then((modal=>(pendingPromise.resolve(),modal))).catch(_notification.default.exception)};_exports.init=(root,view)=>{(0,_prefetch.prefetchStrings)("calendar",["moreevents"]),foldDayEvents(),(root=>{(root=(0,_jquery.default)(root)).on("click",CalendarSelectors.links.eventLink,(e=>{const target=e.target;let eventLink=null,eventId=null;const pendingPromise=new _pending.default("core_calendar/view_manager:eventLink:click");eventLink=target.matches(CalendarSelectors.actions.viewEvent)?target:target.closest(CalendarSelectors.actions.viewEvent),eventId=eventLink?eventLink.dataset.eventId:target.querySelector(CalendarSelectors.actions.viewEvent).dataset.eventId,eventId?(e.preventDefault(),e.stopPropagation(),renderEventSummaryModal(eventId).then(pendingPromise.resolve).catch()):pendingPromise.resolve()})),root.on("click",CalendarSelectors.links.navLink,(e=>{const wrapper=root.find(CalendarSelectors.wrapper),view=wrapper.data("view"),courseId=wrapper.data("courseid"),categoryId=wrapper.data("categoryid"),link=e.currentTarget;"month"===view||"monthblock"===view?(changeMonth(root,link.href,link.dataset.year,link.dataset.month,courseId,categoryId,link.dataset.day),e.preventDefault()):"day"===view&&(changeDay(root,link.href,link.dataset.year,link.dataset.month,link.dataset.day,courseId,categoryId),e.preventDefault())}));const viewSelector=root.find(CalendarSelectors.viewSelector);_custom_interaction_events.default.define(viewSelector,[_custom_interaction_events.default.events.activate]),viewSelector.on(_custom_interaction_events.default.events.activate,(e=>{e.preventDefault();const option=e.target;if(option.classList.contains("active"))return;const view=option.dataset.view,year=option.dataset.year,month=option.dataset.month,day=option.dataset.day,courseId=option.dataset.courseid,categoryId=option.dataset.categoryid;"month"==view?refreshMonthContent(root,year,month,courseId,categoryId,root,"core_calendar/calendar_month",day).then((()=>{updateUrl("?view=month")})).fail(_notification.default.exception):"day"==view?refreshDayContent(root,year,month,day,courseId,categoryId,root,"core_calendar/calendar_day").then((()=>{updateUrl("?view=day")})).fail(_notification.default.exception):"upcoming"==view&&reloadCurrentUpcoming(root,courseId,categoryId,root,"core_calendar/calendar_upcoming").then((()=>{updateUrl("?view=upcoming")})).fail(_notification.default.exception)}))})(root);const calendarTable=root.find(CalendarSelectors.elements.monthDetailed);if(calendarTable.length){"month-detailed-".concat(calendarTable.id,"-filterChanged");registerEventListenersForMonthDetailed(calendarTable)}}})); //# sourceMappingURL=view_manager.min.js.map \ No newline at end of file diff --git a/calendar/amd/build/view_manager.min.js.map b/calendar/amd/build/view_manager.min.js.map index 87f2fd128be..7dfc4bbe4a2 100644 --- a/calendar/amd/build/view_manager.min.js.map +++ b/calendar/amd/build/view_manager.min.js.map @@ -1 +1 @@ -{"version":3,"file":"view_manager.min.js","sources":["../src/view_manager.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 * A javascript module to handler calendar view changes.\n *\n * @module core_calendar/view_manager\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport * as CalendarRepository from 'core_calendar/repository';\nimport CalendarEvents from 'core_calendar/events';\nimport * as CalendarSelectors from 'core_calendar/selectors';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport SummaryModal from 'core_calendar/summary_modal';\nimport CustomEvents from 'core/custom_interaction_events';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\n\n/**\n * Limit number of events per day\n *\n */\nconst LIMIT_DAY_EVENTS = 5;\n\n/**\n * Hide day events if more than 5.\n *\n */\nexport const foldDayEvents = () => {\n const root = $(CalendarSelectors.elements.monthDetailed);\n const days = root.find(CalendarSelectors.day);\n if (days.length === 0) {\n return;\n }\n days.each(function() {\n const dayContainer = $(this);\n const eventsSelector = `${CalendarSelectors.elements.dateContent} ul li[data-event-eventtype]`;\n const filteredEventsSelector = `${CalendarSelectors.elements.dateContent} ul li[data-event-filtered=\"true\"]`;\n const moreEventsSelector = `${CalendarSelectors.elements.dateContent} [data-action=\"view-more-events\"]`;\n const events = dayContainer.find(eventsSelector);\n if (events.length === 0) {\n return;\n }\n\n const filteredEvents = dayContainer.find(filteredEventsSelector);\n const numberOfFiltered = filteredEvents.length;\n const numberOfEvents = events.length - numberOfFiltered;\n\n let count = 1;\n events.each(function() {\n const event = $(this);\n const isNotFiltered = event.attr('data-event-filtered') !== 'true';\n const offset = (numberOfEvents === LIMIT_DAY_EVENTS) ? 0 : 1;\n if (isNotFiltered) {\n if (count > LIMIT_DAY_EVENTS - offset) {\n event.attr('data-event-folded', 'true');\n event.hide();\n } else {\n event.attr('data-event-folded', 'false');\n event.show();\n count++;\n }\n } else {\n // It's being filtered out.\n event.attr('data-event-folded', 'false');\n }\n });\n\n const moreEventsLink = dayContainer.find(moreEventsSelector);\n if (numberOfEvents > LIMIT_DAY_EVENTS) {\n const numberOfHiddenEvents = numberOfEvents - LIMIT_DAY_EVENTS + 1;\n moreEventsLink.show();\n getString('moreevents', 'calendar', numberOfHiddenEvents).then(str => {\n const link = moreEventsLink.find('strong a');\n moreEventsLink.attr('data-event-folded', 'false');\n link.text(str);\n return str;\n }).fail();\n } else {\n moreEventsLink.hide();\n }\n });\n};\n\n/**\n * Register and handle month calendar events.\n *\n * @param {string} pendingId pending id.\n */\nexport const registerEventListenersForMonthDetailed = (pendingId) => {\n const events = `${CalendarEvents.viewUpdated}`;\n $('body').on(events, function(e) {\n foldDayEvents(e);\n });\n foldDayEvents();\n $('body').on(CalendarEvents.filterChanged, function(e, data) {\n const root = $(CalendarSelectors.elements.monthDetailed);\n const pending = new Pending(pendingId);\n const target = root.find(CalendarSelectors.eventType[data.type]);\n const transitionPromise = $.Deferred();\n if (data.hidden) {\n transitionPromise.then(function() {\n target.attr('data-event-filtered', 'true');\n return target.hide().promise();\n }).fail();\n } else {\n transitionPromise.then(function() {\n target.attr('data-event-filtered', 'false');\n return target.show().promise();\n }).fail();\n }\n\n transitionPromise.then(function() {\n foldDayEvents();\n return;\n })\n .always(pending.resolve)\n .fail();\n\n transitionPromise.resolve();\n });\n};\n\n/**\n * Register event listeners for the module.\n *\n * @param {object} root The root element.\n */\nconst registerEventListeners = (root) => {\n root = $(root);\n\n // Bind click events to event links.\n root.on('click', CalendarSelectors.links.eventLink, (e) => {\n const target = e.target;\n let eventLink = null;\n let eventId = null;\n const pendingPromise = new Pending('core_calendar/view_manager:eventLink:click');\n\n if (target.matches(CalendarSelectors.actions.viewEvent)) {\n eventLink = target;\n } else {\n eventLink = target.closest(CalendarSelectors.actions.viewEvent);\n }\n\n if (eventLink) {\n eventId = eventLink.dataset.eventId;\n } else {\n eventId = target.querySelector(CalendarSelectors.actions.viewEvent).dataset.eventId;\n }\n\n if (eventId) {\n // A link was found. Show the modal.\n\n e.preventDefault();\n // We've handled the event so stop it from bubbling\n // and causing the day click handler to fire.\n e.stopPropagation();\n\n renderEventSummaryModal(eventId)\n .then(pendingPromise.resolve)\n .catch();\n } else {\n pendingPromise.resolve();\n }\n });\n\n root.on('click', CalendarSelectors.links.navLink, (e) => {\n const wrapper = root.find(CalendarSelectors.wrapper);\n const view = wrapper.data('view');\n const courseId = wrapper.data('courseid');\n const categoryId = wrapper.data('categoryid');\n const link = e.currentTarget;\n\n if (view === 'month' || view === 'monthblock') {\n changeMonth(root, link.href, link.dataset.year, link.dataset.month, courseId, categoryId, link.dataset.day);\n e.preventDefault();\n } else if (view === 'day') {\n changeDay(root, link.href, link.dataset.year, link.dataset.month, link.dataset.day, courseId, categoryId);\n e.preventDefault();\n }\n });\n\n const viewSelector = root.find(CalendarSelectors.viewSelector);\n CustomEvents.define(viewSelector, [CustomEvents.events.activate]);\n viewSelector.on(\n CustomEvents.events.activate,\n (e) => {\n e.preventDefault();\n\n const option = e.target;\n if (option.classList.contains('active')) {\n return;\n }\n\n const view = option.dataset.view,\n year = option.dataset.year,\n month = option.dataset.month,\n day = option.dataset.day,\n courseId = option.dataset.courseid,\n categoryId = option.dataset.categoryid;\n\n if (view == 'month') {\n refreshMonthContent(root, year, month, courseId, categoryId, root, 'core_calendar/calendar_month', day)\n .then(() => {\n updateUrl('?view=month');\n }).fail(Notification.exception);\n } else if (view == 'day') {\n refreshDayContent(root, year, month, day, courseId, categoryId, root, 'core_calendar/calendar_day')\n .then(() => {\n updateUrl('?view=day');\n }).fail(Notification.exception);\n } else if (view == 'upcoming') {\n reloadCurrentUpcoming(root, courseId, categoryId, root, 'core_calendar/calendar_upcoming')\n .then(() => {\n updateUrl('?view=upcoming');\n }).fail(Notification.exception);\n }\n }\n );\n};\n\n/**\n * Refresh the month content.\n *\n * @param {object} root The root element.\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n * @param {number} day Day (optional)\n * @return {promise}\n */\nexport const refreshMonthContent = (root, year, month, courseId, categoryId, target = null, template = '', day = 1) => {\n startLoading(root);\n\n target = target || root.find(CalendarSelectors.wrapper);\n template = template || root.attr('data-template');\n M.util.js_pending([root.get('id'), year, month, courseId].join('-'));\n const includenavigation = root.data('includenavigation');\n const mini = root.data('mini');\n const viewMode = target.data('view');\n return CalendarRepository.getCalendarMonthData(year, month, courseId, categoryId, includenavigation, mini, day, viewMode)\n .then(context => {\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(() => {\n M.util.js_complete([root.get('id'), year, month, courseId].join('-'));\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Handle changes to the current calendar view.\n *\n * @param {object} root The container element\n * @param {string} url The calendar url to be shown\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {number} day Day (optional)\n * @return {promise}\n */\nexport const changeMonth = (root, url, year, month, courseId, categoryId, day = 1) => {\n return refreshMonthContent(root, year, month, courseId, categoryId, null, '', day)\n .then((...args) => {\n if (url.length && url !== '#') {\n updateUrl(url);\n }\n return args;\n })\n .then((...args) => {\n $('body').trigger(CalendarEvents.monthChanged, [year, month, courseId, categoryId]);\n return args;\n });\n};\n\n/**\n * Reload the current month view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @return {promise}\n */\nexport const reloadCurrentMonth = (root, courseId = 0, categoryId = 0) => {\n const year = root.find(CalendarSelectors.wrapper).data('year');\n const month = root.find(CalendarSelectors.wrapper).data('month');\n const day = root.find(CalendarSelectors.wrapper).data('day');\n\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return refreshMonthContent(root, year, month, courseId, categoryId, null, '', day).\n then((...args) => {\n $('body').trigger(CalendarEvents.courseChanged, [year, month, courseId, categoryId]);\n return args;\n });\n};\n\n\n/**\n * Refresh the day content.\n *\n * @param {object} root The root element.\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} day Day\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n *\n * @return {promise}\n */\nexport const refreshDayContent = (root, year, month, day, courseId, categoryId, target = null, template = '') => {\n startLoading(root);\n\n if (!target || target.length == 0){\n target = root.find(CalendarSelectors.wrapper);\n }\n template = template || root.attr('data-template');\n M.util.js_pending([root.get('id'), year, month, day, courseId, categoryId].join('-'));\n const includenavigation = root.data('includenavigation');\n return CalendarRepository.getCalendarDayData(year, month, day, courseId, categoryId, includenavigation)\n .then((context) => {\n context.viewingday = true;\n context.showviewselector = true;\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(() => {\n M.util.js_complete([root.get('id'), year, month, day, courseId, categoryId].join('-'));\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Reload the current day view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @return {promise}\n */\nexport const reloadCurrentDay = (root, courseId = 0, categoryId = 0) => {\n const wrapper = root.find(CalendarSelectors.wrapper);\n const year = wrapper.data('year');\n const month = wrapper.data('month');\n const day = wrapper.data('day');\n\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return refreshDayContent(root, year, month, day, courseId, categoryId);\n};\n\n/**\n * Handle changes to the current calendar view.\n *\n * @param {object} root The root element.\n * @param {String} url The calendar url to be shown\n * @param {Number} year Year\n * @param {Number} month Month\n * @param {Number} day Day\n * @param {Number} courseId The id of the course whose events are shown\n * @param {Number} categoryId The id of the category whose events are shown\n * @return {promise}\n */\nexport const changeDay = (root, url, year, month, day, courseId, categoryId) => {\n return refreshDayContent(root, year, month, day, courseId, categoryId)\n .then((...args) => {\n if (url.length && url !== '#') {\n updateUrl(url);\n }\n return args;\n })\n .then((...args) => {\n $('body').trigger(CalendarEvents.dayChanged, [year, month, courseId, categoryId]);\n return args;\n });\n};\n\n/**\n * Update calendar URL.\n *\n * @param {String} url The calendar url to be updated.\n */\nexport const updateUrl = (url) => {\n const viewingFullCalendar = document.getElementById(CalendarSelectors.fullCalendarView);\n\n // We want to update the url only if the user is viewing the full calendar.\n if (viewingFullCalendar) {\n window.history.pushState({}, '', url);\n }\n};\n\n/**\n * Set the element state to loading.\n *\n * @param {object} root The container element\n * @method startLoading\n */\nconst startLoading = (root) => {\n const loadingIconContainer = root.find(CalendarSelectors.containers.loadingIcon);\n\n loadingIconContainer.removeClass('hidden');\n};\n\n/**\n * Remove the loading state from the element.\n *\n * @param {object} root The container element\n * @method stopLoading\n */\nconst stopLoading = (root) => {\n const loadingIconContainer = root.find(CalendarSelectors.containers.loadingIcon);\n\n loadingIconContainer.addClass('hidden');\n};\n\n/**\n * Reload the current month view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n * @return {promise}\n */\nexport const reloadCurrentUpcoming = (root, courseId = 0, categoryId = 0, target = null, template = '') => {\n startLoading(root);\n\n target = target || root.find(CalendarSelectors.wrapper);\n template = template || root.attr('data-template');\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return CalendarRepository.getCalendarUpcomingData(courseId, categoryId)\n .then((context) => {\n context.viewingupcoming = true;\n context.showviewselector = true;\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(function() {\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Get the CSS class to apply for the given event type.\n *\n * @param {string} eventType The calendar event type\n * @return {string}\n */\nconst getEventTypeClassFromType = (eventType) => {\n return 'calendar_event_' + eventType;\n};\n\n/**\n * Render the event summary modal.\n *\n * @param {Number} eventId The calendar event id.\n * @returns {Promise}\n */\nconst renderEventSummaryModal = (eventId) => {\n const pendingPromise = new Pending('core_calendar/view_manager:renderEventSummaryModal');\n\n // Calendar repository promise.\n return CalendarRepository.getEventById(eventId)\n .then((getEventResponse) => {\n if (!getEventResponse.event) {\n throw new Error('Error encountered while trying to fetch calendar event with ID: ' + eventId);\n }\n\n return getEventResponse.event;\n })\n .then(eventData => {\n // Build the modal parameters from the event data.\n const modalParams = {\n title: eventData.name,\n type: SummaryModal.TYPE,\n body: Templates.render('core_calendar/event_summary_body', eventData),\n templateContext: {\n canedit: eventData.canedit,\n candelete: eventData.candelete,\n headerclasses: getEventTypeClassFromType(eventData.normalisedeventtype),\n isactionevent: eventData.isactionevent,\n url: eventData.url,\n action: eventData.action\n }\n };\n\n // Create the modal.\n return ModalFactory.create(modalParams);\n })\n .then(modal => {\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n // Finally, render the modal!\n modal.show();\n\n return modal;\n })\n .then(modal => {\n pendingPromise.resolve();\n\n return modal;\n })\n .catch(Notification.exception);\n};\n\nexport const init = (root, view) => {\n prefetchStrings('calendar', ['moreevents']);\n foldDayEvents();\n registerEventListeners(root, view);\n const calendarTable = root.find(CalendarSelectors.elements.monthDetailed);\n if (calendarTable.length) {\n const pendingId = `month-detailed-${calendarTable.id}-filterChanged`;\n registerEventListenersForMonthDetailed(calendarTable, pendingId);\n }\n};\n"],"names":["foldDayEvents","days","CalendarSelectors","elements","monthDetailed","find","day","length","each","dayContainer","this","eventsSelector","dateContent","filteredEventsSelector","moreEventsSelector","events","numberOfFiltered","numberOfEvents","count","event","attr","hide","show","moreEventsLink","numberOfHiddenEvents","then","str","link","text","fail","registerEventListenersForMonthDetailed","pendingId","CalendarEvents","viewUpdated","on","e","filterChanged","data","root","pending","Pending","target","eventType","type","transitionPromise","$","Deferred","hidden","promise","always","resolve","refreshMonthContent","year","month","courseId","categoryId","template","startLoading","wrapper","M","util","js_pending","get","join","includenavigation","mini","viewMode","CalendarRepository","getCalendarMonthData","context","Templates","render","html","js","replaceNode","document","querySelector","dispatchEvent","CustomEvent","js_complete","stopLoading","Notification","exception","changeMonth","url","updateUrl","args","trigger","monthChanged","courseChanged","refreshDayContent","getCalendarDayData","viewingday","showviewselector","changeDay","dayChanged","getElementById","fullCalendarView","window","history","pushState","containers","loadingIcon","removeClass","addClass","reloadCurrentUpcoming","getCalendarUpcomingData","viewingupcoming","renderEventSummaryModal","eventId","pendingPromise","getEventById","getEventResponse","Error","eventData","modalParams","title","name","SummaryModal","TYPE","body","templateContext","canedit","candelete","headerclasses","normalisedeventtype","isactionevent","action","ModalFactory","create","modal","getRoot","ModalEvents","destroy","catch","view","links","eventLink","matches","actions","viewEvent","closest","dataset","preventDefault","stopPropagation","navLink","currentTarget","href","viewSelector","define","CustomEvents","activate","option","classList","contains","courseid","categoryid","registerEventListeners","calendarTable","id"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"view_manager.min.js","sources":["../src/view_manager.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 * A javascript module to handler calendar view changes.\n *\n * @module core_calendar/view_manager\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport * as CalendarRepository from 'core_calendar/repository';\nimport CalendarEvents from 'core_calendar/events';\nimport * as CalendarSelectors from 'core_calendar/selectors';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport SummaryModal from 'core_calendar/summary_modal';\nimport CustomEvents from 'core/custom_interaction_events';\nimport {getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\n\n/**\n * Limit number of events per day\n *\n */\nconst LIMIT_DAY_EVENTS = 5;\n\n/**\n * Hide day events if more than 5.\n *\n */\nexport const foldDayEvents = () => {\n const root = $(CalendarSelectors.elements.monthDetailed);\n const days = root.find(CalendarSelectors.day);\n if (days.length === 0) {\n return;\n }\n days.each(function() {\n const dayContainer = $(this);\n const eventsSelector = `${CalendarSelectors.elements.dateContent} ul li[data-event-eventtype]`;\n const filteredEventsSelector = `${CalendarSelectors.elements.dateContent} ul li[data-event-filtered=\"true\"]`;\n const moreEventsSelector = `${CalendarSelectors.elements.dateContent} [data-action=\"view-more-events\"]`;\n const events = dayContainer.find(eventsSelector);\n if (events.length === 0) {\n return;\n }\n\n const filteredEvents = dayContainer.find(filteredEventsSelector);\n const numberOfFiltered = filteredEvents.length;\n const numberOfEvents = events.length - numberOfFiltered;\n\n let count = 1;\n events.each(function() {\n const event = $(this);\n const isNotFiltered = event.attr('data-event-filtered') !== 'true';\n const offset = (numberOfEvents === LIMIT_DAY_EVENTS) ? 0 : 1;\n if (isNotFiltered) {\n if (count > LIMIT_DAY_EVENTS - offset) {\n event.attr('data-event-folded', 'true');\n event.hide();\n } else {\n event.attr('data-event-folded', 'false');\n event.show();\n count++;\n }\n } else {\n // It's being filtered out.\n event.attr('data-event-folded', 'false');\n }\n });\n\n const moreEventsLink = dayContainer.find(moreEventsSelector);\n if (numberOfEvents > LIMIT_DAY_EVENTS) {\n const numberOfHiddenEvents = numberOfEvents - LIMIT_DAY_EVENTS + 1;\n moreEventsLink.show();\n getString('moreevents', 'calendar', numberOfHiddenEvents).then(str => {\n const link = moreEventsLink.find('strong a');\n moreEventsLink.attr('data-event-folded', 'false');\n link.text(str);\n return str;\n }).fail();\n } else {\n moreEventsLink.hide();\n }\n });\n};\n\n/**\n * Register and handle month calendar events.\n *\n * @param {string} pendingId pending id.\n */\nexport const registerEventListenersForMonthDetailed = (pendingId) => {\n const events = `${CalendarEvents.viewUpdated}`;\n $('body').on(events, function(e) {\n foldDayEvents(e);\n });\n foldDayEvents();\n $('body').on(CalendarEvents.filterChanged, function(e, data) {\n const root = $(CalendarSelectors.elements.monthDetailed);\n const pending = new Pending(pendingId);\n const target = root.find(CalendarSelectors.eventType[data.type]);\n const transitionPromise = $.Deferred();\n if (data.hidden) {\n transitionPromise.then(function() {\n target.attr('data-event-filtered', 'true');\n return target.hide().promise();\n }).fail();\n } else {\n transitionPromise.then(function() {\n target.attr('data-event-filtered', 'false');\n return target.show().promise();\n }).fail();\n }\n\n transitionPromise.then(function() {\n foldDayEvents();\n return;\n })\n .always(pending.resolve)\n .fail();\n\n transitionPromise.resolve();\n });\n};\n\n/**\n * Register event listeners for the module.\n *\n * @param {object} root The root element.\n */\nconst registerEventListeners = (root) => {\n root = $(root);\n\n // Bind click events to event links.\n root.on('click', CalendarSelectors.links.eventLink, (e) => {\n const target = e.target;\n let eventLink = null;\n let eventId = null;\n const pendingPromise = new Pending('core_calendar/view_manager:eventLink:click');\n\n if (target.matches(CalendarSelectors.actions.viewEvent)) {\n eventLink = target;\n } else {\n eventLink = target.closest(CalendarSelectors.actions.viewEvent);\n }\n\n if (eventLink) {\n eventId = eventLink.dataset.eventId;\n } else {\n eventId = target.querySelector(CalendarSelectors.actions.viewEvent).dataset.eventId;\n }\n\n if (eventId) {\n // A link was found. Show the modal.\n\n e.preventDefault();\n // We've handled the event so stop it from bubbling\n // and causing the day click handler to fire.\n e.stopPropagation();\n\n renderEventSummaryModal(eventId)\n .then(pendingPromise.resolve)\n .catch();\n } else {\n pendingPromise.resolve();\n }\n });\n\n root.on('click', CalendarSelectors.links.navLink, (e) => {\n const wrapper = root.find(CalendarSelectors.wrapper);\n const view = wrapper.data('view');\n const courseId = wrapper.data('courseid');\n const categoryId = wrapper.data('categoryid');\n const link = e.currentTarget;\n\n if (view === 'month' || view === 'monthblock') {\n changeMonth(root, link.href, link.dataset.year, link.dataset.month, courseId, categoryId, link.dataset.day);\n e.preventDefault();\n } else if (view === 'day') {\n changeDay(root, link.href, link.dataset.year, link.dataset.month, link.dataset.day, courseId, categoryId);\n e.preventDefault();\n }\n });\n\n const viewSelector = root.find(CalendarSelectors.viewSelector);\n CustomEvents.define(viewSelector, [CustomEvents.events.activate]);\n viewSelector.on(\n CustomEvents.events.activate,\n (e) => {\n e.preventDefault();\n\n const option = e.target;\n if (option.classList.contains('active')) {\n return;\n }\n\n const view = option.dataset.view,\n year = option.dataset.year,\n month = option.dataset.month,\n day = option.dataset.day,\n courseId = option.dataset.courseid,\n categoryId = option.dataset.categoryid;\n\n if (view == 'month') {\n refreshMonthContent(root, year, month, courseId, categoryId, root, 'core_calendar/calendar_month', day)\n .then(() => {\n updateUrl('?view=month');\n }).fail(Notification.exception);\n } else if (view == 'day') {\n refreshDayContent(root, year, month, day, courseId, categoryId, root, 'core_calendar/calendar_day')\n .then(() => {\n updateUrl('?view=day');\n }).fail(Notification.exception);\n } else if (view == 'upcoming') {\n reloadCurrentUpcoming(root, courseId, categoryId, root, 'core_calendar/calendar_upcoming')\n .then(() => {\n updateUrl('?view=upcoming');\n }).fail(Notification.exception);\n }\n }\n );\n};\n\n/**\n * Refresh the month content.\n *\n * @param {object} root The root element.\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n * @param {number} day Day (optional)\n * @return {promise}\n */\nexport const refreshMonthContent = (root, year, month, courseId, categoryId, target = null, template = '', day = 1) => {\n startLoading(root);\n\n target = target || root.find(CalendarSelectors.wrapper);\n template = template || root.attr('data-template');\n M.util.js_pending([root.get('id'), year, month, courseId].join('-'));\n const includenavigation = root.data('includenavigation');\n const mini = root.data('mini');\n const viewMode = target.data('view');\n return CalendarRepository.getCalendarMonthData(year, month, courseId, categoryId, includenavigation, mini, day, viewMode)\n .then(context => {\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(() => {\n M.util.js_complete([root.get('id'), year, month, courseId].join('-'));\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Handle changes to the current calendar view.\n *\n * @param {object} root The container element\n * @param {string} url The calendar url to be shown\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {number} day Day (optional)\n * @return {promise}\n */\nexport const changeMonth = (root, url, year, month, courseId, categoryId, day = 1) => {\n return refreshMonthContent(root, year, month, courseId, categoryId, null, '', day)\n .then((...args) => {\n if (url.length && url !== '#') {\n updateUrl(url);\n }\n return args;\n })\n .then((...args) => {\n $('body').trigger(CalendarEvents.monthChanged, [year, month, courseId, categoryId]);\n return args;\n });\n};\n\n/**\n * Reload the current month view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @return {promise}\n */\nexport const reloadCurrentMonth = (root, courseId = 0, categoryId = 0) => {\n const year = root.find(CalendarSelectors.wrapper).data('year');\n const month = root.find(CalendarSelectors.wrapper).data('month');\n const day = root.find(CalendarSelectors.wrapper).data('day');\n\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return refreshMonthContent(root, year, month, courseId, categoryId, null, '', day).\n then((...args) => {\n $('body').trigger(CalendarEvents.courseChanged, [year, month, courseId, categoryId]);\n return args;\n });\n};\n\n\n/**\n * Refresh the day content.\n *\n * @param {object} root The root element.\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} day Day\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n *\n * @return {promise}\n */\nexport const refreshDayContent = (root, year, month, day, courseId, categoryId, target = null, template = '') => {\n startLoading(root);\n\n if (!target || target.length == 0){\n target = root.find(CalendarSelectors.wrapper);\n }\n template = template || root.attr('data-template');\n M.util.js_pending([root.get('id'), year, month, day, courseId, categoryId].join('-'));\n const includenavigation = root.data('includenavigation');\n return CalendarRepository.getCalendarDayData(year, month, day, courseId, categoryId, includenavigation)\n .then((context) => {\n context.viewingday = true;\n context.showviewselector = true;\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(() => {\n M.util.js_complete([root.get('id'), year, month, day, courseId, categoryId].join('-'));\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Reload the current day view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @return {promise}\n */\nexport const reloadCurrentDay = (root, courseId = 0, categoryId = 0) => {\n const wrapper = root.find(CalendarSelectors.wrapper);\n const year = wrapper.data('year');\n const month = wrapper.data('month');\n const day = wrapper.data('day');\n\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return refreshDayContent(root, year, month, day, courseId, categoryId);\n};\n\n/**\n * Handle changes to the current calendar view.\n *\n * @param {object} root The root element.\n * @param {String} url The calendar url to be shown\n * @param {Number} year Year\n * @param {Number} month Month\n * @param {Number} day Day\n * @param {Number} courseId The id of the course whose events are shown\n * @param {Number} categoryId The id of the category whose events are shown\n * @return {promise}\n */\nexport const changeDay = (root, url, year, month, day, courseId, categoryId) => {\n return refreshDayContent(root, year, month, day, courseId, categoryId)\n .then((...args) => {\n if (url.length && url !== '#') {\n updateUrl(url);\n }\n return args;\n })\n .then((...args) => {\n $('body').trigger(CalendarEvents.dayChanged, [year, month, courseId, categoryId]);\n return args;\n });\n};\n\n/**\n * Update calendar URL.\n *\n * @param {String} url The calendar url to be updated.\n */\nexport const updateUrl = (url) => {\n const viewingFullCalendar = document.getElementById(CalendarSelectors.fullCalendarView);\n\n // We want to update the url only if the user is viewing the full calendar.\n if (viewingFullCalendar) {\n window.history.pushState({}, '', url);\n }\n};\n\n/**\n * Set the element state to loading.\n *\n * @param {object} root The container element\n * @method startLoading\n */\nconst startLoading = (root) => {\n const loadingIconContainer = root.find(CalendarSelectors.containers.loadingIcon);\n\n loadingIconContainer.removeClass('hidden');\n};\n\n/**\n * Remove the loading state from the element.\n *\n * @param {object} root The container element\n * @method stopLoading\n */\nconst stopLoading = (root) => {\n const loadingIconContainer = root.find(CalendarSelectors.containers.loadingIcon);\n\n loadingIconContainer.addClass('hidden');\n};\n\n/**\n * Reload the current month view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n * @return {promise}\n */\nexport const reloadCurrentUpcoming = (root, courseId = 0, categoryId = 0, target = null, template = '') => {\n startLoading(root);\n\n target = target || root.find(CalendarSelectors.wrapper);\n template = template || root.attr('data-template');\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return CalendarRepository.getCalendarUpcomingData(courseId, categoryId)\n .then((context) => {\n context.viewingupcoming = true;\n context.showviewselector = true;\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(function() {\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Get the CSS class to apply for the given event type.\n *\n * @param {string} eventType The calendar event type\n * @return {string}\n */\nconst getEventTypeClassFromType = (eventType) => {\n return 'calendar_event_' + eventType;\n};\n\n/**\n * Render the event summary modal.\n *\n * @param {Number} eventId The calendar event id.\n * @returns {Promise}\n */\nconst renderEventSummaryModal = (eventId) => {\n const pendingPromise = new Pending('core_calendar/view_manager:renderEventSummaryModal');\n\n // Calendar repository promise.\n return CalendarRepository.getEventById(eventId)\n .then((getEventResponse) => {\n if (!getEventResponse.event) {\n throw new Error('Error encountered while trying to fetch calendar event with ID: ' + eventId);\n }\n\n return getEventResponse.event;\n })\n .then(eventData => {\n // Build the modal parameters from the event data.\n const modalParams = {\n title: eventData.name,\n type: SummaryModal.TYPE,\n body: Templates.render('core_calendar/event_summary_body', eventData),\n templateContext: {\n canedit: eventData.canedit,\n candelete: eventData.candelete,\n headerclasses: getEventTypeClassFromType(eventData.normalisedeventtype),\n isactionevent: eventData.isactionevent,\n url: eventData.url,\n action: eventData.action\n }\n };\n\n // Create the modal.\n return ModalFactory.create(modalParams);\n })\n .then(modal => {\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n // Finally, render the modal!\n modal.show();\n\n return modal;\n })\n .then(modal => {\n pendingPromise.resolve();\n\n return modal;\n })\n .catch(Notification.exception);\n};\n\nexport const init = (root, view) => {\n prefetchStrings('calendar', ['moreevents']);\n foldDayEvents();\n registerEventListeners(root, view);\n const calendarTable = root.find(CalendarSelectors.elements.monthDetailed);\n if (calendarTable.length) {\n const pendingId = `month-detailed-${calendarTable.id}-filterChanged`;\n registerEventListenersForMonthDetailed(calendarTable, pendingId);\n }\n};\n"],"names":["foldDayEvents","days","CalendarSelectors","elements","monthDetailed","find","day","length","each","dayContainer","this","eventsSelector","dateContent","filteredEventsSelector","moreEventsSelector","events","numberOfFiltered","numberOfEvents","count","event","attr","hide","show","moreEventsLink","numberOfHiddenEvents","then","str","link","text","fail","registerEventListenersForMonthDetailed","pendingId","CalendarEvents","viewUpdated","on","e","filterChanged","data","root","pending","Pending","target","eventType","type","transitionPromise","$","Deferred","hidden","promise","always","resolve","refreshMonthContent","year","month","courseId","categoryId","template","startLoading","wrapper","M","util","js_pending","get","join","includenavigation","mini","viewMode","CalendarRepository","getCalendarMonthData","context","Templates","render","html","js","replaceNode","document","querySelector","dispatchEvent","CustomEvent","js_complete","stopLoading","Notification","exception","changeMonth","url","updateUrl","args","trigger","monthChanged","courseChanged","refreshDayContent","getCalendarDayData","viewingday","showviewselector","changeDay","dayChanged","getElementById","fullCalendarView","window","history","pushState","containers","loadingIcon","removeClass","addClass","reloadCurrentUpcoming","getCalendarUpcomingData","viewingupcoming","renderEventSummaryModal","eventId","pendingPromise","getEventById","getEventResponse","Error","eventData","modalParams","title","name","SummaryModal","TYPE","body","templateContext","canedit","candelete","headerclasses","normalisedeventtype","isactionevent","action","ModalFactory","create","modal","getRoot","ModalEvents","destroy","catch","view","links","eventLink","matches","actions","viewEvent","closest","dataset","preventDefault","stopPropagation","navLink","currentTarget","href","viewSelector","define","CustomEvents","activate","option","classList","contains","courseid","categoryid","registerEventListeners","calendarTable","id"],"mappings":"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"} \ No newline at end of file diff --git a/calendar/amd/src/manage_subscriptions.js b/calendar/amd/src/manage_subscriptions.js index e9343564cb5..674c3023e81 100644 --- a/calendar/amd/src/manage_subscriptions.js +++ b/calendar/amd/src/manage_subscriptions.js @@ -28,7 +28,7 @@ import * as Modal from 'core/modal_factory'; import * as ModalEvents from 'core/modal_events'; import {exception as displayException, addNotification, fetchNotifications} from 'core/notification'; import Prefetch from 'core/prefetch'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {eventTypes} from 'core/local/inplace_editable/events'; /** diff --git a/calendar/amd/src/view_manager.js b/calendar/amd/src/view_manager.js index 041c7d09778..a8ce1ea6e63 100644 --- a/calendar/amd/src/view_manager.js +++ b/calendar/amd/src/view_manager.js @@ -31,7 +31,7 @@ import ModalFactory from 'core/modal_factory'; import ModalEvents from 'core/modal_events'; import SummaryModal from 'core_calendar/summary_modal'; import CustomEvents from 'core/custom_interaction_events'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Pending from 'core/pending'; import {prefetchStrings} from 'core/prefetch'; diff --git a/comment/amd/build/admin.min.js b/comment/amd/build/admin.min.js index 58a23e5ec91..af501863efb 100644 --- a/comment/amd/build/admin.min.js +++ b/comment/amd/build/admin.min.js @@ -1,3 +1,3 @@ -define("core_comment/admin",["exports","core/event_dispatcher","core/notification","core/pending","core/prefetch","core/str","core_comment/repository","core_reportbuilder/local/events","core_reportbuilder/local/selectors"],(function(_exports,_event_dispatcher,_notification,_pending,_prefetch,_str,_repository,reportEvents,reportSelectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);const Selectors_commentDelete='[data-action="comment-delete"]',Selectors_commentDeleteChecked='[data-togglegroup="report-select-all"][data-toggle="slave"]:checked',Selectors_commentDeleteSelected='[data-action="comment-delete-selected"]';_exports.init=()=>{(0,_prefetch.prefetchStrings)("core_admin",["confirmdeletecomments"]),(0,_prefetch.prefetchStrings)("core",["delete","deleteselected"]),document.addEventListener("click",(event=>{const commentDelete=event.target.closest(Selectors_commentDelete);if(commentDelete){event.preventDefault();const triggerElement=commentDelete.closest(".dropdown").querySelector(".dropdown-toggle");_notification.default.saveCancelPromise((0,_str.get_string)("delete","core"),(0,_str.get_string)("confirmdeletecomments","core_admin"),(0,_str.get_string)("delete","core"),{triggerElement:triggerElement}).then((()=>{const pendingPromise=new _pending.default("core_comment/comment:delete"),reportElement=event.target.closest(reportSelectors.regions.report);return(0,_repository.deleteComment)(commentDelete.dataset.commentId).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))}const commentDeleteSelected=event.target.closest(Selectors_commentDeleteSelected);if(commentDeleteSelected){event.preventDefault();const reportElement=document.querySelector(reportSelectors.regions.report),commentDeleteChecked=reportElement.querySelectorAll(Selectors_commentDeleteChecked);if(0===commentDeleteChecked.length)return;_notification.default.saveCancelPromise((0,_str.get_string)("deleteselected","core"),(0,_str.get_string)("confirmdeletecomments","core_admin"),(0,_str.get_string)("delete","core"),{triggerElement:commentDeleteSelected}).then((()=>{const pendingPromise=new _pending.default("core_comment/comments:delete"),deleteCommentIds=[...commentDeleteChecked].map((check=>check.value));return(0,_repository.deleteComments)(deleteCommentIds).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))}}))}})); +define("core_comment/admin",["exports","core/event_dispatcher","core/notification","core/pending","core/prefetch","core/str","core_comment/repository","core_reportbuilder/local/events","core_reportbuilder/local/selectors"],(function(_exports,_event_dispatcher,_notification,_pending,_prefetch,_str,_repository,reportEvents,reportSelectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);const Selectors_commentDelete='[data-action="comment-delete"]',Selectors_commentDeleteChecked='[data-togglegroup="report-select-all"][data-toggle="slave"]:checked',Selectors_commentDeleteSelected='[data-action="comment-delete-selected"]';_exports.init=()=>{(0,_prefetch.prefetchStrings)("core_admin",["confirmdeletecomments"]),(0,_prefetch.prefetchStrings)("core",["delete","deleteselected"]),document.addEventListener("click",(event=>{const commentDelete=event.target.closest(Selectors_commentDelete);if(commentDelete){event.preventDefault();const triggerElement=commentDelete.closest(".dropdown").querySelector(".dropdown-toggle");_notification.default.saveCancelPromise((0,_str.getString)("delete","core"),(0,_str.getString)("confirmdeletecomments","core_admin"),(0,_str.getString)("delete","core"),{triggerElement:triggerElement}).then((()=>{const pendingPromise=new _pending.default("core_comment/comment:delete"),reportElement=event.target.closest(reportSelectors.regions.report);return(0,_repository.deleteComment)(commentDelete.dataset.commentId).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))}const commentDeleteSelected=event.target.closest(Selectors_commentDeleteSelected);if(commentDeleteSelected){event.preventDefault();const reportElement=document.querySelector(reportSelectors.regions.report),commentDeleteChecked=reportElement.querySelectorAll(Selectors_commentDeleteChecked);if(0===commentDeleteChecked.length)return;_notification.default.saveCancelPromise((0,_str.getString)("deleteselected","core"),(0,_str.getString)("confirmdeletecomments","core_admin"),(0,_str.getString)("delete","core"),{triggerElement:commentDeleteSelected}).then((()=>{const pendingPromise=new _pending.default("core_comment/comments:delete"),deleteCommentIds=[...commentDeleteChecked].map((check=>check.value));return(0,_repository.deleteComments)(deleteCommentIds).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))}}))}})); //# sourceMappingURL=admin.min.js.map \ No newline at end of file diff --git a/comment/amd/build/admin.min.js.map b/comment/amd/build/admin.min.js.map index b81deb29784..b510c7c8681 100644 --- a/comment/amd/build/admin.min.js.map +++ b/comment/amd/build/admin.min.js.map @@ -1 +1 @@ -{"version":3,"file":"admin.min.js","sources":["../src/admin.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 * Comments admin management\n *\n * @module core_comment/admin\n * @copyright 2022 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {deleteComment, deleteComments} from 'core_comment/repository';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\n\nconst Selectors = {\n commentDelete: '[data-action=\"comment-delete\"]',\n commentDeleteChecked: '[data-togglegroup=\"report-select-all\"][data-toggle=\"slave\"]:checked',\n commentDeleteSelected: '[data-action=\"comment-delete-selected\"]',\n};\n\n/**\n * Initialise module\n */\nexport const init = () => {\n prefetchStrings('core_admin', [\n 'confirmdeletecomments',\n ]);\n\n prefetchStrings('core', [\n 'delete',\n 'deleteselected'\n ]);\n\n document.addEventListener('click', event => {\n const commentDelete = event.target.closest(Selectors.commentDelete);\n if (commentDelete) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = commentDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n Notification.saveCancelPromise(\n getString('delete', 'core'),\n getString('confirmdeletecomments', 'core_admin'),\n getString('delete', 'core'),\n {triggerElement}\n ).then(() => {\n const pendingPromise = new Pending('core_comment/comment:delete');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n\n return deleteComment(commentDelete.dataset.commentId)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n\n const commentDeleteSelected = event.target.closest(Selectors.commentDeleteSelected);\n if (commentDeleteSelected) {\n event.preventDefault();\n\n const reportElement = document.querySelector(reportSelectors.regions.report);\n const commentDeleteChecked = reportElement.querySelectorAll(Selectors.commentDeleteChecked);\n if (commentDeleteChecked.length === 0) {\n return;\n }\n\n Notification.saveCancelPromise(\n getString('deleteselected', 'core'),\n getString('confirmdeletecomments', 'core_admin'),\n getString('delete', 'core'),\n {triggerElement: commentDeleteSelected}\n ).then(() => {\n const pendingPromise = new Pending('core_comment/comments:delete');\n const deleteCommentIds = [...commentDeleteChecked].map(check => check.value);\n\n return deleteComments(deleteCommentIds)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n};\n"],"names":["Selectors","document","addEventListener","event","commentDelete","target","closest","preventDefault","triggerElement","querySelector","saveCancelPromise","then","pendingPromise","Pending","reportElement","reportSelectors","regions","report","dataset","commentId","reportEvents","tableReload","preservePagination","resolve","catch","Notification","exception","commentDeleteSelected","commentDeleteChecked","querySelectorAll","length","deleteCommentIds","map","check","value"],"mappings":"0nDAkCMA,wBACa,iCADbA,+BAEoB,sEAFpBA,gCAGqB,wDAMP,mCACA,aAAc,CAC1B,wDAGY,OAAQ,CACpB,SACA,mBAGJC,SAASC,iBAAiB,SAASC,cACzBC,cAAgBD,MAAME,OAAOC,QAAQN,4BACvCI,cAAe,CACfD,MAAMI,uBAGAC,eAAiBJ,cAAcE,QAAQ,aAAaG,cAAc,0CAC3DC,mBACT,mBAAU,SAAU,SACpB,mBAAU,wBAAyB,eACnC,mBAAU,SAAU,QACpB,CAACF,eAAAA,iBACHG,MAAK,WACGC,eAAiB,IAAIC,iBAAQ,+BAC7BC,cAAgBX,MAAME,OAAOC,QAAQS,gBAAgBC,QAAQC,eAE5D,6BAAcb,cAAcc,QAAQC,WACtCR,MAAK,yCACYS,aAAaC,YAAa,CAACC,oBAAoB,GAAOR,eAC7DF,eAAeW,aAEzBC,MAAMC,sBAAaC,cACzBF,OAAM,eAKPG,sBAAwBxB,MAAME,OAAOC,QAAQN,oCAC/C2B,sBAAuB,CACvBxB,MAAMI,uBAEAO,cAAgBb,SAASQ,cAAcM,gBAAgBC,QAAQC,QAC/DW,qBAAuBd,cAAce,iBAAiB7B,mCACxB,IAAhC4B,qBAAqBE,oCAIZpB,mBACT,mBAAU,iBAAkB,SAC5B,mBAAU,wBAAyB,eACnC,mBAAU,SAAU,QACpB,CAACF,eAAgBmB,wBACnBhB,MAAK,WACGC,eAAiB,IAAIC,iBAAQ,gCAC7BkB,iBAAmB,IAAIH,sBAAsBI,KAAIC,OAASA,MAAMC,eAE/D,8BAAeH,kBACjBpB,MAAK,yCACYS,aAAaC,YAAa,CAACC,oBAAoB,GAAOR,eAC7DF,eAAeW,aAEzBC,MAAMC,sBAAaC,cACzBF,OAAM"} \ No newline at end of file +{"version":3,"file":"admin.min.js","sources":["../src/admin.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 * Comments admin management\n *\n * @module core_comment/admin\n * @copyright 2022 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport {deleteComment, deleteComments} from 'core_comment/repository';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\n\nconst Selectors = {\n commentDelete: '[data-action=\"comment-delete\"]',\n commentDeleteChecked: '[data-togglegroup=\"report-select-all\"][data-toggle=\"slave\"]:checked',\n commentDeleteSelected: '[data-action=\"comment-delete-selected\"]',\n};\n\n/**\n * Initialise module\n */\nexport const init = () => {\n prefetchStrings('core_admin', [\n 'confirmdeletecomments',\n ]);\n\n prefetchStrings('core', [\n 'delete',\n 'deleteselected'\n ]);\n\n document.addEventListener('click', event => {\n const commentDelete = event.target.closest(Selectors.commentDelete);\n if (commentDelete) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = commentDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n Notification.saveCancelPromise(\n getString('delete', 'core'),\n getString('confirmdeletecomments', 'core_admin'),\n getString('delete', 'core'),\n {triggerElement}\n ).then(() => {\n const pendingPromise = new Pending('core_comment/comment:delete');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n\n return deleteComment(commentDelete.dataset.commentId)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n\n const commentDeleteSelected = event.target.closest(Selectors.commentDeleteSelected);\n if (commentDeleteSelected) {\n event.preventDefault();\n\n const reportElement = document.querySelector(reportSelectors.regions.report);\n const commentDeleteChecked = reportElement.querySelectorAll(Selectors.commentDeleteChecked);\n if (commentDeleteChecked.length === 0) {\n return;\n }\n\n Notification.saveCancelPromise(\n getString('deleteselected', 'core'),\n getString('confirmdeletecomments', 'core_admin'),\n getString('delete', 'core'),\n {triggerElement: commentDeleteSelected}\n ).then(() => {\n const pendingPromise = new Pending('core_comment/comments:delete');\n const deleteCommentIds = [...commentDeleteChecked].map(check => check.value);\n\n return deleteComments(deleteCommentIds)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n};\n"],"names":["Selectors","document","addEventListener","event","commentDelete","target","closest","preventDefault","triggerElement","querySelector","saveCancelPromise","then","pendingPromise","Pending","reportElement","reportSelectors","regions","report","dataset","commentId","reportEvents","tableReload","preservePagination","resolve","catch","Notification","exception","commentDeleteSelected","commentDeleteChecked","querySelectorAll","length","deleteCommentIds","map","check","value"],"mappings":"0nDAkCMA,wBACa,iCADbA,+BAEoB,sEAFpBA,gCAGqB,wDAMP,mCACA,aAAc,CAC1B,wDAGY,OAAQ,CACpB,SACA,mBAGJC,SAASC,iBAAiB,SAASC,cACzBC,cAAgBD,MAAME,OAAOC,QAAQN,4BACvCI,cAAe,CACfD,MAAMI,uBAGAC,eAAiBJ,cAAcE,QAAQ,aAAaG,cAAc,0CAC3DC,mBACT,kBAAU,SAAU,SACpB,kBAAU,wBAAyB,eACnC,kBAAU,SAAU,QACpB,CAACF,eAAAA,iBACHG,MAAK,WACGC,eAAiB,IAAIC,iBAAQ,+BAC7BC,cAAgBX,MAAME,OAAOC,QAAQS,gBAAgBC,QAAQC,eAE5D,6BAAcb,cAAcc,QAAQC,WACtCR,MAAK,yCACYS,aAAaC,YAAa,CAACC,oBAAoB,GAAOR,eAC7DF,eAAeW,aAEzBC,MAAMC,sBAAaC,cACzBF,OAAM,eAKPG,sBAAwBxB,MAAME,OAAOC,QAAQN,oCAC/C2B,sBAAuB,CACvBxB,MAAMI,uBAEAO,cAAgBb,SAASQ,cAAcM,gBAAgBC,QAAQC,QAC/DW,qBAAuBd,cAAce,iBAAiB7B,mCACxB,IAAhC4B,qBAAqBE,oCAIZpB,mBACT,kBAAU,iBAAkB,SAC5B,kBAAU,wBAAyB,eACnC,kBAAU,SAAU,QACpB,CAACF,eAAgBmB,wBACnBhB,MAAK,WACGC,eAAiB,IAAIC,iBAAQ,gCAC7BkB,iBAAmB,IAAIH,sBAAsBI,KAAIC,OAASA,MAAMC,eAE/D,8BAAeH,kBACjBpB,MAAK,yCACYS,aAAaC,YAAa,CAACC,oBAAoB,GAAOR,eAC7DF,eAAeW,aAEzBC,MAAMC,sBAAaC,cACzBF,OAAM"} \ No newline at end of file diff --git a/comment/amd/src/admin.js b/comment/amd/src/admin.js index 4d3fb48c070..663f349bc33 100644 --- a/comment/amd/src/admin.js +++ b/comment/amd/src/admin.js @@ -27,7 +27,7 @@ import {dispatchEvent} from 'core/event_dispatcher'; import Notification from 'core/notification'; import Pending from 'core/pending'; import {prefetchStrings} from 'core/prefetch'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {deleteComment, deleteComments} from 'core_comment/repository'; import * as reportEvents from 'core_reportbuilder/local/events'; import * as reportSelectors from 'core_reportbuilder/local/selectors'; diff --git a/contentbank/amd/build/search.min.js b/contentbank/amd/build/search.min.js index 677143eb438..9828d4b5d92 100644 --- a/contentbank/amd/build/search.min.js +++ b/contentbank/amd/build/search.min.js @@ -5,6 +5,6 @@ define("core_contentbank/search",["exports","jquery","core_contentbank/selectors * @module core_contentbank/search * @copyright 2020 Sara Arjona * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_selectors=_interopRequireDefault(_selectors),_pending=_interopRequireDefault(_pending);_exports.init=()=>{const pendingPromise=new _pending.default,root=(0,_jquery.default)(_selectors.default.regions.contentbank);registerListenerEvents(root),pendingPromise.resolve()};const registerListenerEvents=root=>{const searchInput=root.find(_selectors.default.elements.searchinput)[0];root.on("click",_selectors.default.actions.search,(function(e){e.preventDefault(),toggleSearchResultsView(root,searchInput.value)})),root.on("click",_selectors.default.actions.clearSearch,(function(e){e.preventDefault(),searchInput.value="",searchInput.focus(),toggleSearchResultsView(root,searchInput.value)})),searchInput.addEventListener("input",(0,_utils.debounce)((()=>{toggleSearchResultsView(root,searchInput.value)}),300))},toggleSearchResultsView=async(body,searchQuery)=>{const clearSearchButton=body.find(_selectors.default.actions.clearSearch)[0],navbarBreadcrumb=body.find(_selectors.default.elements.cbnavbarbreadcrumb)[0],navbarTotal=body.find(_selectors.default.elements.cbnavbartotalsearch)[0],filteredContents=filterContents(body,searchQuery);searchQuery.length>0?(clearSearchButton.classList.remove("d-none"),navbarBreadcrumb.classList.add("d-none"),navbarTotal.innerHTML=await(0,_str.get_string)("itemsfound","core_contentbank",filteredContents.length),navbarTotal.classList.remove("d-none")):(clearSearchButton.classList.add("d-none"),navbarBreadcrumb.classList.remove("d-none"),navbarTotal.classList.add("d-none"))},filterContents=(body,searchTerm)=>{const contents=Array.from(body.find(_selectors.default.elements.listitem)),searchResults=[];return contents.forEach((content=>{const contentName=content.getAttribute("data-name");if(""===searchTerm||contentName.toLowerCase().includes(searchTerm.toLowerCase())){searchResults.push(content);content.querySelector(_selectors.default.regions.cbcontentname).innerHTML=highlight(contentName,searchTerm),content.classList.remove("d-none")}else content.classList.add("d-none")})),searchResults},highlight=(text,highlightText)=>{let result=text;if(""!==highlightText){const pos=text.toLowerCase().indexOf(highlightText.toLowerCase());pos>-1&&(result=text.substr(0,pos)+''+text.substr(pos,highlightText.length)+""+text.substr(pos+highlightText.length))}return result}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_selectors=_interopRequireDefault(_selectors),_pending=_interopRequireDefault(_pending);_exports.init=()=>{const pendingPromise=new _pending.default,root=(0,_jquery.default)(_selectors.default.regions.contentbank);registerListenerEvents(root),pendingPromise.resolve()};const registerListenerEvents=root=>{const searchInput=root.find(_selectors.default.elements.searchinput)[0];root.on("click",_selectors.default.actions.search,(function(e){e.preventDefault(),toggleSearchResultsView(root,searchInput.value)})),root.on("click",_selectors.default.actions.clearSearch,(function(e){e.preventDefault(),searchInput.value="",searchInput.focus(),toggleSearchResultsView(root,searchInput.value)})),searchInput.addEventListener("input",(0,_utils.debounce)((()=>{toggleSearchResultsView(root,searchInput.value)}),300))},toggleSearchResultsView=async(body,searchQuery)=>{const clearSearchButton=body.find(_selectors.default.actions.clearSearch)[0],navbarBreadcrumb=body.find(_selectors.default.elements.cbnavbarbreadcrumb)[0],navbarTotal=body.find(_selectors.default.elements.cbnavbartotalsearch)[0],filteredContents=filterContents(body,searchQuery);searchQuery.length>0?(clearSearchButton.classList.remove("d-none"),navbarBreadcrumb.classList.add("d-none"),navbarTotal.innerHTML=await(0,_str.getString)("itemsfound","core_contentbank",filteredContents.length),navbarTotal.classList.remove("d-none")):(clearSearchButton.classList.add("d-none"),navbarBreadcrumb.classList.remove("d-none"),navbarTotal.classList.add("d-none"))},filterContents=(body,searchTerm)=>{const contents=Array.from(body.find(_selectors.default.elements.listitem)),searchResults=[];return contents.forEach((content=>{const contentName=content.getAttribute("data-name");if(""===searchTerm||contentName.toLowerCase().includes(searchTerm.toLowerCase())){searchResults.push(content);content.querySelector(_selectors.default.regions.cbcontentname).innerHTML=highlight(contentName,searchTerm),content.classList.remove("d-none")}else content.classList.add("d-none")})),searchResults},highlight=(text,highlightText)=>{let result=text;if(""!==highlightText){const pos=text.toLowerCase().indexOf(highlightText.toLowerCase());pos>-1&&(result=text.substr(0,pos)+''+text.substr(pos,highlightText.length)+""+text.substr(pos+highlightText.length))}return result}})); //# sourceMappingURL=search.min.js.map \ No newline at end of file diff --git a/contentbank/amd/build/search.min.js.map b/contentbank/amd/build/search.min.js.map index f1f44cb4aca..65bc680c3d5 100644 --- a/contentbank/amd/build/search.min.js.map +++ b/contentbank/amd/build/search.min.js.map @@ -1 +1 @@ -{"version":3,"file":"search.min.js","sources":["../src/search.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 * Search methods for finding contents in the content bank.\n *\n * @module core_contentbank/search\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport selectors from 'core_contentbank/selectors';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {debounce} from 'core/utils';\n\n/**\n * Set up the search.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n\n const root = $(selectors.regions.contentbank);\n registerListenerEvents(root);\n\n pendingPromise.resolve();\n};\n\n/**\n * Register contentbank search related event listeners.\n *\n * @method registerListenerEvents\n * @param {Object} root The root element for the contentbank.\n */\nconst registerListenerEvents = (root) => {\n\n const searchInput = root.find(selectors.elements.searchinput)[0];\n\n root.on('click', selectors.actions.search, function(e) {\n e.preventDefault();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n root.on('click', selectors.actions.clearSearch, function(e) {\n e.preventDefault();\n searchInput.value = \"\";\n searchInput.focus();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n // The search input is also triggered.\n searchInput.addEventListener('input', debounce(() => {\n // Display the search results.\n toggleSearchResultsView(root, searchInput.value);\n }, 300));\n\n};\n\n/**\n * Toggle (display/hide) the search results depending on the value of the search query.\n *\n * @method toggleSearchResultsView\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchQuery The search query.\n */\nconst toggleSearchResultsView = async(body, searchQuery) => {\n const clearSearchButton = body.find(selectors.actions.clearSearch)[0];\n\n const navbarBreadcrumb = body.find(selectors.elements.cbnavbarbreadcrumb)[0];\n const navbarTotal = body.find(selectors.elements.cbnavbartotalsearch)[0];\n // Update the results.\n const filteredContents = filterContents(body, searchQuery);\n if (searchQuery.length > 0) {\n // As the search query is present, search results should be displayed.\n\n // Display the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.remove('d-none');\n\n // Change the cb-navbar to display total items found.\n navbarBreadcrumb.classList.add('d-none');\n navbarTotal.innerHTML = await getString('itemsfound', 'core_contentbank', filteredContents.length);\n navbarTotal.classList.remove('d-none');\n } else {\n // As search query is not present, the search results should be removed.\n\n // Hide the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.add('d-none');\n\n // Display again the breadcrumb in the navbar.\n navbarBreadcrumb.classList.remove('d-none');\n navbarTotal.classList.add('d-none');\n }\n};\n\n/**\n * Return the list of contents which have a name that matches the given search term.\n *\n * @method filterContents\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchTerm The search term to match.\n * @return {Array}\n */\nconst filterContents = (body, searchTerm) => {\n const contents = Array.from(body.find(selectors.elements.listitem));\n const searchResults = [];\n contents.forEach((content) => {\n const contentName = content.getAttribute('data-name');\n if (searchTerm === '' || contentName.toLowerCase().includes(searchTerm.toLowerCase())) {\n // The content matches the search criteria so it should be displayed and hightlighted.\n searchResults.push(content);\n const contentNameElement = content.querySelector(selectors.regions.cbcontentname);\n contentNameElement.innerHTML = highlight(contentName, searchTerm);\n content.classList.remove('d-none');\n } else {\n content.classList.add('d-none');\n }\n });\n\n return searchResults;\n};\n\n/**\n * Highlight a given string in a text.\n *\n * @method highlight\n * @param {String} text The whole text.\n * @param {String} highlightText The piece of text to highlight.\n * @return {String}\n */\nconst highlight = (text, highlightText) => {\n let result = text;\n if (highlightText !== '') {\n const pos = text.toLowerCase().indexOf(highlightText.toLowerCase());\n if (pos > -1) {\n result = text.substr(0, pos) + '' + text.substr(pos, highlightText.length) + '' +\n text.substr(pos + highlightText.length);\n }\n }\n\n return result;\n};\n"],"names":["pendingPromise","Pending","root","selectors","regions","contentbank","registerListenerEvents","resolve","searchInput","find","elements","searchinput","on","actions","search","e","preventDefault","toggleSearchResultsView","value","clearSearch","focus","addEventListener","async","body","searchQuery","clearSearchButton","navbarBreadcrumb","cbnavbarbreadcrumb","navbarTotal","cbnavbartotalsearch","filteredContents","filterContents","length","classList","remove","add","innerHTML","searchTerm","contents","Array","from","listitem","searchResults","forEach","content","contentName","getAttribute","toLowerCase","includes","push","querySelector","cbcontentname","highlight","text","highlightText","result","pos","indexOf","substr"],"mappings":";;;;;;;gOAkCoB,WACVA,eAAiB,IAAIC,iBAErBC,MAAO,mBAAEC,mBAAUC,QAAQC,aACjCC,uBAAuBJ,MAEvBF,eAAeO,iBASbD,uBAA0BJ,aAEtBM,YAAcN,KAAKO,KAAKN,mBAAUO,SAASC,aAAa,GAE9DT,KAAKU,GAAG,QAAST,mBAAUU,QAAQC,QAAQ,SAASC,GAChDA,EAAEC,iBACFC,wBAAwBf,KAAMM,YAAYU,UAG9ChB,KAAKU,GAAG,QAAST,mBAAUU,QAAQM,aAAa,SAASJ,GACrDA,EAAEC,iBACFR,YAAYU,MAAQ,GACpBV,YAAYY,QACZH,wBAAwBf,KAAMM,YAAYU,UAI9CV,YAAYa,iBAAiB,SAAS,oBAAS,KAE3CJ,wBAAwBf,KAAMM,YAAYU,SAC3C,OAWDD,wBAA0BK,MAAMC,KAAMC,qBAClCC,kBAAoBF,KAAKd,KAAKN,mBAAUU,QAAQM,aAAa,GAE7DO,iBAAmBH,KAAKd,KAAKN,mBAAUO,SAASiB,oBAAoB,GACpEC,YAAcL,KAAKd,KAAKN,mBAAUO,SAASmB,qBAAqB,GAEhEC,iBAAmBC,eAAeR,KAAMC,aAC1CA,YAAYQ,OAAS,GAIrBP,kBAAkBQ,UAAUC,OAAO,UAGnCR,iBAAiBO,UAAUE,IAAI,UAC/BP,YAAYQ,gBAAkB,mBAAU,aAAc,mBAAoBN,iBAAiBE,QAC3FJ,YAAYK,UAAUC,OAAO,YAK7BT,kBAAkBQ,UAAUE,IAAI,UAGhCT,iBAAiBO,UAAUC,OAAO,UAClCN,YAAYK,UAAUE,IAAI,YAY5BJ,eAAiB,CAACR,KAAMc,oBACpBC,SAAWC,MAAMC,KAAKjB,KAAKd,KAAKN,mBAAUO,SAAS+B,WACnDC,cAAgB,UACtBJ,SAASK,SAASC,gBACRC,YAAcD,QAAQE,aAAa,gBACtB,KAAfT,YAAqBQ,YAAYE,cAAcC,SAASX,WAAWU,eAAgB,CAEnFL,cAAcO,KAAKL,SACQA,QAAQM,cAAc/C,mBAAUC,QAAQ+C,eAChDf,UAAYgB,UAAUP,YAAaR,YACtDO,QAAQX,UAAUC,OAAO,eAEzBU,QAAQX,UAAUE,IAAI,aAIvBO,eAWLU,UAAY,CAACC,KAAMC,qBACjBC,OAASF,QACS,KAAlBC,cAAsB,OAChBE,IAAMH,KAAKN,cAAcU,QAAQH,cAAcP,eACjDS,KAAO,IACPD,OAASF,KAAKK,OAAO,EAAGF,KAAO,2BAA6BH,KAAKK,OAAOF,IAAKF,cAActB,QAAU,UACjGqB,KAAKK,OAAOF,IAAMF,cAActB,gBAIrCuB"} \ No newline at end of file +{"version":3,"file":"search.min.js","sources":["../src/search.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 * Search methods for finding contents in the content bank.\n *\n * @module core_contentbank/search\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport selectors from 'core_contentbank/selectors';\nimport {getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {debounce} from 'core/utils';\n\n/**\n * Set up the search.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n\n const root = $(selectors.regions.contentbank);\n registerListenerEvents(root);\n\n pendingPromise.resolve();\n};\n\n/**\n * Register contentbank search related event listeners.\n *\n * @method registerListenerEvents\n * @param {Object} root The root element for the contentbank.\n */\nconst registerListenerEvents = (root) => {\n\n const searchInput = root.find(selectors.elements.searchinput)[0];\n\n root.on('click', selectors.actions.search, function(e) {\n e.preventDefault();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n root.on('click', selectors.actions.clearSearch, function(e) {\n e.preventDefault();\n searchInput.value = \"\";\n searchInput.focus();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n // The search input is also triggered.\n searchInput.addEventListener('input', debounce(() => {\n // Display the search results.\n toggleSearchResultsView(root, searchInput.value);\n }, 300));\n\n};\n\n/**\n * Toggle (display/hide) the search results depending on the value of the search query.\n *\n * @method toggleSearchResultsView\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchQuery The search query.\n */\nconst toggleSearchResultsView = async(body, searchQuery) => {\n const clearSearchButton = body.find(selectors.actions.clearSearch)[0];\n\n const navbarBreadcrumb = body.find(selectors.elements.cbnavbarbreadcrumb)[0];\n const navbarTotal = body.find(selectors.elements.cbnavbartotalsearch)[0];\n // Update the results.\n const filteredContents = filterContents(body, searchQuery);\n if (searchQuery.length > 0) {\n // As the search query is present, search results should be displayed.\n\n // Display the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.remove('d-none');\n\n // Change the cb-navbar to display total items found.\n navbarBreadcrumb.classList.add('d-none');\n navbarTotal.innerHTML = await getString('itemsfound', 'core_contentbank', filteredContents.length);\n navbarTotal.classList.remove('d-none');\n } else {\n // As search query is not present, the search results should be removed.\n\n // Hide the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.add('d-none');\n\n // Display again the breadcrumb in the navbar.\n navbarBreadcrumb.classList.remove('d-none');\n navbarTotal.classList.add('d-none');\n }\n};\n\n/**\n * Return the list of contents which have a name that matches the given search term.\n *\n * @method filterContents\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchTerm The search term to match.\n * @return {Array}\n */\nconst filterContents = (body, searchTerm) => {\n const contents = Array.from(body.find(selectors.elements.listitem));\n const searchResults = [];\n contents.forEach((content) => {\n const contentName = content.getAttribute('data-name');\n if (searchTerm === '' || contentName.toLowerCase().includes(searchTerm.toLowerCase())) {\n // The content matches the search criteria so it should be displayed and hightlighted.\n searchResults.push(content);\n const contentNameElement = content.querySelector(selectors.regions.cbcontentname);\n contentNameElement.innerHTML = highlight(contentName, searchTerm);\n content.classList.remove('d-none');\n } else {\n content.classList.add('d-none');\n }\n });\n\n return searchResults;\n};\n\n/**\n * Highlight a given string in a text.\n *\n * @method highlight\n * @param {String} text The whole text.\n * @param {String} highlightText The piece of text to highlight.\n * @return {String}\n */\nconst highlight = (text, highlightText) => {\n let result = text;\n if (highlightText !== '') {\n const pos = text.toLowerCase().indexOf(highlightText.toLowerCase());\n if (pos > -1) {\n result = text.substr(0, pos) + '' + text.substr(pos, highlightText.length) + '' +\n text.substr(pos + highlightText.length);\n }\n }\n\n return result;\n};\n"],"names":["pendingPromise","Pending","root","selectors","regions","contentbank","registerListenerEvents","resolve","searchInput","find","elements","searchinput","on","actions","search","e","preventDefault","toggleSearchResultsView","value","clearSearch","focus","addEventListener","async","body","searchQuery","clearSearchButton","navbarBreadcrumb","cbnavbarbreadcrumb","navbarTotal","cbnavbartotalsearch","filteredContents","filterContents","length","classList","remove","add","innerHTML","searchTerm","contents","Array","from","listitem","searchResults","forEach","content","contentName","getAttribute","toLowerCase","includes","push","querySelector","cbcontentname","highlight","text","highlightText","result","pos","indexOf","substr"],"mappings":";;;;;;;gOAkCoB,WACVA,eAAiB,IAAIC,iBAErBC,MAAO,mBAAEC,mBAAUC,QAAQC,aACjCC,uBAAuBJ,MAEvBF,eAAeO,iBASbD,uBAA0BJ,aAEtBM,YAAcN,KAAKO,KAAKN,mBAAUO,SAASC,aAAa,GAE9DT,KAAKU,GAAG,QAAST,mBAAUU,QAAQC,QAAQ,SAASC,GAChDA,EAAEC,iBACFC,wBAAwBf,KAAMM,YAAYU,UAG9ChB,KAAKU,GAAG,QAAST,mBAAUU,QAAQM,aAAa,SAASJ,GACrDA,EAAEC,iBACFR,YAAYU,MAAQ,GACpBV,YAAYY,QACZH,wBAAwBf,KAAMM,YAAYU,UAI9CV,YAAYa,iBAAiB,SAAS,oBAAS,KAE3CJ,wBAAwBf,KAAMM,YAAYU,SAC3C,OAWDD,wBAA0BK,MAAMC,KAAMC,qBAClCC,kBAAoBF,KAAKd,KAAKN,mBAAUU,QAAQM,aAAa,GAE7DO,iBAAmBH,KAAKd,KAAKN,mBAAUO,SAASiB,oBAAoB,GACpEC,YAAcL,KAAKd,KAAKN,mBAAUO,SAASmB,qBAAqB,GAEhEC,iBAAmBC,eAAeR,KAAMC,aAC1CA,YAAYQ,OAAS,GAIrBP,kBAAkBQ,UAAUC,OAAO,UAGnCR,iBAAiBO,UAAUE,IAAI,UAC/BP,YAAYQ,gBAAkB,kBAAU,aAAc,mBAAoBN,iBAAiBE,QAC3FJ,YAAYK,UAAUC,OAAO,YAK7BT,kBAAkBQ,UAAUE,IAAI,UAGhCT,iBAAiBO,UAAUC,OAAO,UAClCN,YAAYK,UAAUE,IAAI,YAY5BJ,eAAiB,CAACR,KAAMc,oBACpBC,SAAWC,MAAMC,KAAKjB,KAAKd,KAAKN,mBAAUO,SAAS+B,WACnDC,cAAgB,UACtBJ,SAASK,SAASC,gBACRC,YAAcD,QAAQE,aAAa,gBACtB,KAAfT,YAAqBQ,YAAYE,cAAcC,SAASX,WAAWU,eAAgB,CAEnFL,cAAcO,KAAKL,SACQA,QAAQM,cAAc/C,mBAAUC,QAAQ+C,eAChDf,UAAYgB,UAAUP,YAAaR,YACtDO,QAAQX,UAAUC,OAAO,eAEzBU,QAAQX,UAAUE,IAAI,aAIvBO,eAWLU,UAAY,CAACC,KAAMC,qBACjBC,OAASF,QACS,KAAlBC,cAAsB,OAChBE,IAAMH,KAAKN,cAAcU,QAAQH,cAAcP,eACjDS,KAAO,IACPD,OAASF,KAAKK,OAAO,EAAGF,KAAO,2BAA6BH,KAAKK,OAAOF,IAAKF,cAActB,QAAU,UACjGqB,KAAKK,OAAOF,IAAMF,cAActB,gBAIrCuB"} \ No newline at end of file diff --git a/contentbank/amd/build/sort.min.js b/contentbank/amd/build/sort.min.js index 9f9a31d4cd5..7d90c0d7248 100644 --- a/contentbank/amd/build/sort.min.js +++ b/contentbank/amd/build/sort.min.js @@ -5,6 +5,6 @@ define("core_contentbank/sort",["exports","./selectors","core/str","core/prefetc * @module core_contentbank/sort * @copyright 2020 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_selectors=_interopRequireDefault(_selectors),_prefetch=_interopRequireDefault(_prefetch),_notification=_interopRequireDefault(_notification);_exports.init=()=>{const contentBank=document.querySelector(_selectors.default.regions.contentbank);_prefetch.default.prefetchStrings("contentbank",["contentname","uses","lastmodified","size","type","author"]),_prefetch.default.prefetchStrings("moodle",["sortbyx","sortbyxreverse"]),registerListenerEvents(contentBank)};const registerListenerEvents=contentBank=>{contentBank.addEventListener("click",(e=>{const viewList=contentBank.querySelector(_selectors.default.actions.viewlist),viewGrid=contentBank.querySelector(_selectors.default.actions.viewgrid),fileArea=contentBank.querySelector(_selectors.default.regions.filearea),shownItems=fileArea.querySelectorAll(_selectors.default.elements.listitem);if(e.target.closest(_selectors.default.actions.viewgrid)){if(contentBank.classList.remove("view-list"),contentBank.classList.add("view-grid"),fileArea&&shownItems){fileArea.setAttribute("role","list"),shownItems.forEach((listItem=>{listItem.setAttribute("role","listitem"),listItem.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.removeAttribute("role")))}));const heading=fileArea.querySelector(_selectors.default.elements.heading);heading&&(heading.removeAttribute("role"),heading.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.removeAttribute("role"))))}return viewGrid.classList.add("active"),viewList.classList.remove("active"),void setViewListPreference(!1)}if(e.target.closest(_selectors.default.actions.viewlist)){if(contentBank.classList.remove("view-grid"),contentBank.classList.add("view-list"),fileArea&&shownItems){fileArea.setAttribute("role","table"),shownItems.forEach((listItem=>{listItem.setAttribute("role","row"),listItem.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.setAttribute("role","cell")))}));const heading=fileArea.querySelector(_selectors.default.elements.heading);heading&&(heading.setAttribute("role","row"),heading.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.setAttribute("role","columnheader"))))}return viewList.classList.add("active"),viewGrid.classList.remove("active"),void setViewListPreference(!0)}if(fileArea&&shownItems){const sortByName=e.target.closest(_selectors.default.actions.sortname);if(sortByName){const ascending=updateSortButtons(contentBank,sortByName);return void updateSortOrder(fileArea,shownItems,"data-file",ascending)}const sortByUses=e.target.closest(_selectors.default.actions.sortuses);if(sortByUses){const ascending=updateSortButtons(contentBank,sortByUses);return void updateSortOrder(fileArea,shownItems,"data-uses",ascending)}const sortByDate=e.target.closest(_selectors.default.actions.sortdate);if(sortByDate){const ascending=updateSortButtons(contentBank,sortByDate);return void updateSortOrder(fileArea,shownItems,"data-timemodified",ascending)}const sortBySize=e.target.closest(_selectors.default.actions.sortsize);if(sortBySize){const ascending=updateSortButtons(contentBank,sortBySize);return void updateSortOrder(fileArea,shownItems,"data-bytes",ascending)}const sortByType=e.target.closest(_selectors.default.actions.sorttype);if(sortByType){const ascending=updateSortButtons(contentBank,sortByType);return void updateSortOrder(fileArea,shownItems,"data-type",ascending)}const sortByAuthor=e.target.closest(_selectors.default.actions.sortauthor);if(sortByAuthor){const ascending=updateSortButtons(contentBank,sortByAuthor);updateSortOrder(fileArea,shownItems,"data-author",ascending)}}else;}))},setViewListPreference=function(viewList){return!1===viewList&&(viewList=null),(0,_repository.setUserPreference)("core_contentbank_view_list",viewList).catch(_notification.default.exception)},updateSortButtons=(contentBank,sortButton)=>{contentBank.querySelectorAll(_selectors.default.elements.sortbutton).forEach((button=>{button!==sortButton&&(button.classList.remove("dir-asc"),button.classList.remove("dir-desc"),button.classList.add("dir-none"),button.closest(_selectors.default.elements.cell).setAttribute("aria-sort","none"),updateButtonTitle(button,!1))}));let ascending=!0;return sortButton.classList.contains("dir-none")?(sortButton.classList.remove("dir-none"),sortButton.classList.add("dir-asc"),sortButton.closest(_selectors.default.elements.cell).setAttribute("aria-sort","ascending")):sortButton.classList.contains("dir-asc")?(sortButton.classList.remove("dir-asc"),sortButton.classList.add("dir-desc"),sortButton.closest(_selectors.default.elements.cell).setAttribute("aria-sort","descending"),ascending=!1):sortButton.classList.contains("dir-desc")&&(sortButton.classList.remove("dir-desc"),sortButton.classList.add("dir-asc"),sortButton.closest(_selectors.default.elements.cell).setAttribute("aria-sort","ascending")),updateButtonTitle(sortButton,ascending),ascending},updateButtonTitle=(button,ascending)=>{const sortString=ascending?"sortbyxreverse":"sortbyx";return(0,_str.get_string)(button.dataset.string,"contentbank").then((columnName=>(0,_str.get_string)(sortString,"core",columnName))).then((sortByString=>(button.setAttribute("title",sortByString),sortByString))).catch()},updateSortOrder=(fileArea,itemList,attribute,ascending)=>{[].slice.call(itemList).sort((function(a,b){let aa=a.getAttribute(attribute),bb=b.getAttribute(attribute);return isNaN(aa)||(aa=parseInt(aa),bb=parseInt(bb)),ascending?aa>bb?1:-1:aafileArea.appendChild(listItem)))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_selectors=_interopRequireDefault(_selectors),_prefetch=_interopRequireDefault(_prefetch),_notification=_interopRequireDefault(_notification);_exports.init=()=>{const contentBank=document.querySelector(_selectors.default.regions.contentbank);_prefetch.default.prefetchStrings("contentbank",["contentname","uses","lastmodified","size","type","author"]),_prefetch.default.prefetchStrings("moodle",["sortbyx","sortbyxreverse"]),registerListenerEvents(contentBank)};const registerListenerEvents=contentBank=>{contentBank.addEventListener("click",(e=>{const viewList=contentBank.querySelector(_selectors.default.actions.viewlist),viewGrid=contentBank.querySelector(_selectors.default.actions.viewgrid),fileArea=contentBank.querySelector(_selectors.default.regions.filearea),shownItems=fileArea.querySelectorAll(_selectors.default.elements.listitem);if(e.target.closest(_selectors.default.actions.viewgrid)){if(contentBank.classList.remove("view-list"),contentBank.classList.add("view-grid"),fileArea&&shownItems){fileArea.setAttribute("role","list"),shownItems.forEach((listItem=>{listItem.setAttribute("role","listitem"),listItem.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.removeAttribute("role")))}));const heading=fileArea.querySelector(_selectors.default.elements.heading);heading&&(heading.removeAttribute("role"),heading.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.removeAttribute("role"))))}return viewGrid.classList.add("active"),viewList.classList.remove("active"),void setViewListPreference(!1)}if(e.target.closest(_selectors.default.actions.viewlist)){if(contentBank.classList.remove("view-grid"),contentBank.classList.add("view-list"),fileArea&&shownItems){fileArea.setAttribute("role","table"),shownItems.forEach((listItem=>{listItem.setAttribute("role","row"),listItem.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.setAttribute("role","cell")))}));const heading=fileArea.querySelector(_selectors.default.elements.heading);heading&&(heading.setAttribute("role","row"),heading.querySelectorAll(_selectors.default.elements.cell).forEach((cell=>cell.setAttribute("role","columnheader"))))}return viewList.classList.add("active"),viewGrid.classList.remove("active"),void setViewListPreference(!0)}if(fileArea&&shownItems){const sortByName=e.target.closest(_selectors.default.actions.sortname);if(sortByName){const ascending=updateSortButtons(contentBank,sortByName);return void updateSortOrder(fileArea,shownItems,"data-file",ascending)}const sortByUses=e.target.closest(_selectors.default.actions.sortuses);if(sortByUses){const ascending=updateSortButtons(contentBank,sortByUses);return void updateSortOrder(fileArea,shownItems,"data-uses",ascending)}const sortByDate=e.target.closest(_selectors.default.actions.sortdate);if(sortByDate){const ascending=updateSortButtons(contentBank,sortByDate);return void updateSortOrder(fileArea,shownItems,"data-timemodified",ascending)}const sortBySize=e.target.closest(_selectors.default.actions.sortsize);if(sortBySize){const ascending=updateSortButtons(contentBank,sortBySize);return void updateSortOrder(fileArea,shownItems,"data-bytes",ascending)}const sortByType=e.target.closest(_selectors.default.actions.sorttype);if(sortByType){const ascending=updateSortButtons(contentBank,sortByType);return void updateSortOrder(fileArea,shownItems,"data-type",ascending)}const sortByAuthor=e.target.closest(_selectors.default.actions.sortauthor);if(sortByAuthor){const ascending=updateSortButtons(contentBank,sortByAuthor);updateSortOrder(fileArea,shownItems,"data-author",ascending)}}else;}))},setViewListPreference=function(viewList){return!1===viewList&&(viewList=null),(0,_repository.setUserPreference)("core_contentbank_view_list",viewList).catch(_notification.default.exception)},updateSortButtons=(contentBank,sortButton)=>{contentBank.querySelectorAll(_selectors.default.elements.sortbutton).forEach((button=>{button!==sortButton&&(button.classList.remove("dir-asc"),button.classList.remove("dir-desc"),button.classList.add("dir-none"),button.closest(_selectors.default.elements.cell).setAttribute("aria-sort","none"),updateButtonTitle(button,!1))}));let ascending=!0;return sortButton.classList.contains("dir-none")?(sortButton.classList.remove("dir-none"),sortButton.classList.add("dir-asc"),sortButton.closest(_selectors.default.elements.cell).setAttribute("aria-sort","ascending")):sortButton.classList.contains("dir-asc")?(sortButton.classList.remove("dir-asc"),sortButton.classList.add("dir-desc"),sortButton.closest(_selectors.default.elements.cell).setAttribute("aria-sort","descending"),ascending=!1):sortButton.classList.contains("dir-desc")&&(sortButton.classList.remove("dir-desc"),sortButton.classList.add("dir-asc"),sortButton.closest(_selectors.default.elements.cell).setAttribute("aria-sort","ascending")),updateButtonTitle(sortButton,ascending),ascending},updateButtonTitle=(button,ascending)=>{const sortString=ascending?"sortbyxreverse":"sortbyx";return(0,_str.getString)(button.dataset.string,"contentbank").then((columnName=>(0,_str.getString)(sortString,"core",columnName))).then((sortByString=>(button.setAttribute("title",sortByString),sortByString))).catch()},updateSortOrder=(fileArea,itemList,attribute,ascending)=>{[].slice.call(itemList).sort((function(a,b){let aa=a.getAttribute(attribute),bb=b.getAttribute(attribute);return isNaN(aa)||(aa=parseInt(aa),bb=parseInt(bb)),ascending?aa>bb?1:-1:aafileArea.appendChild(listItem)))}})); //# sourceMappingURL=sort.min.js.map \ No newline at end of file diff --git a/contentbank/amd/build/sort.min.js.map b/contentbank/amd/build/sort.min.js.map index e5480a01774..c888b7dd187 100644 --- a/contentbank/amd/build/sort.min.js.map +++ b/contentbank/amd/build/sort.min.js.map @@ -1 +1 @@ -{"version":3,"file":"sort.min.js","sources":["../src/sort.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 * Content bank UI actions.\n *\n * @module core_contentbank/sort\n * @copyright 2020 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport selectors from './selectors';\nimport {get_string as getString} from 'core/str';\nimport Prefetch from 'core/prefetch';\nimport Notification from 'core/notification';\nimport {setUserPreference} from 'core_user/repository';\n\n/**\n * Set up the contentbank views.\n *\n * @method init\n */\nexport const init = () => {\n const contentBank = document.querySelector(selectors.regions.contentbank);\n Prefetch.prefetchStrings('contentbank', ['contentname', 'uses', 'lastmodified', 'size', 'type', 'author']);\n Prefetch.prefetchStrings('moodle', ['sortbyx', 'sortbyxreverse']);\n registerListenerEvents(contentBank);\n};\n\n/**\n * Register contentbank related event listeners.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} contentBank The DOM node of the content bank\n */\nconst registerListenerEvents = (contentBank) => {\n\n contentBank.addEventListener('click', e => {\n const viewList = contentBank.querySelector(selectors.actions.viewlist);\n const viewGrid = contentBank.querySelector(selectors.actions.viewgrid);\n const fileArea = contentBank.querySelector(selectors.regions.filearea);\n const shownItems = fileArea.querySelectorAll(selectors.elements.listitem);\n\n // View as Grid button.\n if (e.target.closest(selectors.actions.viewgrid)) {\n contentBank.classList.remove('view-list');\n contentBank.classList.add('view-grid');\n if (fileArea && shownItems) {\n fileArea.setAttribute('role', 'list');\n shownItems.forEach(listItem => {\n listItem.setAttribute('role', 'listitem');\n listItem.querySelectorAll(selectors.elements.cell).forEach(cell => cell.removeAttribute('role'));\n });\n\n const heading = fileArea.querySelector(selectors.elements.heading);\n if (heading) {\n heading.removeAttribute('role');\n heading.querySelectorAll(selectors.elements.cell).forEach(cell => cell.removeAttribute('role'));\n }\n }\n viewGrid.classList.add('active');\n viewList.classList.remove('active');\n setViewListPreference(false);\n\n return;\n }\n\n // View as List button.\n if (e.target.closest(selectors.actions.viewlist)) {\n contentBank.classList.remove('view-grid');\n contentBank.classList.add('view-list');\n if (fileArea && shownItems) {\n fileArea.setAttribute('role', 'table');\n shownItems.forEach(listItem => {\n listItem.setAttribute('role', 'row');\n listItem.querySelectorAll(selectors.elements.cell).forEach(cell => cell.setAttribute('role', 'cell'));\n });\n\n const heading = fileArea.querySelector(selectors.elements.heading);\n if (heading) {\n heading.setAttribute('role', 'row');\n heading.querySelectorAll(selectors.elements.cell).forEach(cell => cell.setAttribute('role', 'columnheader'));\n }\n }\n viewList.classList.add('active');\n viewGrid.classList.remove('active');\n setViewListPreference(true);\n\n return;\n }\n\n if (fileArea && shownItems) {\n\n // Sort by file name alphabetical\n const sortByName = e.target.closest(selectors.actions.sortname);\n if (sortByName) {\n const ascending = updateSortButtons(contentBank, sortByName);\n updateSortOrder(fileArea, shownItems, 'data-file', ascending);\n return;\n }\n\n // Sort by uses.\n const sortByUses = e.target.closest(selectors.actions.sortuses);\n if (sortByUses) {\n const ascending = updateSortButtons(contentBank, sortByUses);\n updateSortOrder(fileArea, shownItems, 'data-uses', ascending);\n return;\n }\n\n // Sort by date.\n const sortByDate = e.target.closest(selectors.actions.sortdate);\n if (sortByDate) {\n const ascending = updateSortButtons(contentBank, sortByDate);\n updateSortOrder(fileArea, shownItems, 'data-timemodified', ascending);\n return;\n }\n\n // Sort by size.\n const sortBySize = e.target.closest(selectors.actions.sortsize);\n if (sortBySize) {\n const ascending = updateSortButtons(contentBank, sortBySize);\n updateSortOrder(fileArea, shownItems, 'data-bytes', ascending);\n return;\n }\n\n // Sort by type.\n const sortByType = e.target.closest(selectors.actions.sorttype);\n if (sortByType) {\n const ascending = updateSortButtons(contentBank, sortByType);\n updateSortOrder(fileArea, shownItems, 'data-type', ascending);\n return;\n }\n\n // Sort by author.\n const sortByAuthor = e.target.closest(selectors.actions.sortauthor);\n if (sortByAuthor) {\n const ascending = updateSortButtons(contentBank, sortByAuthor);\n updateSortOrder(fileArea, shownItems, 'data-author', ascending);\n }\n return;\n }\n });\n};\n\n\n/**\n * Set the contentbank user preference in list view\n *\n * @param {Bool} viewList view ContentBank as list.\n * @return {Promise} Repository promise.\n */\nconst setViewListPreference = function(viewList) {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (viewList === false) {\n viewList = null;\n }\n\n return setUserPreference('core_contentbank_view_list', viewList)\n .catch(Notification.exception);\n};\n\n/**\n * Update the sort button view.\n *\n * @method updateSortButtons\n * @param {HTMLElement} contentBank The DOM node of the contentbank button\n * @param {HTMLElement} sortButton The DOM node of the sort button\n * @return {Bool} sort ascending\n */\nconst updateSortButtons = (contentBank, sortButton) => {\n const sortButtons = contentBank.querySelectorAll(selectors.elements.sortbutton);\n\n sortButtons.forEach((button) => {\n if (button !== sortButton) {\n button.classList.remove('dir-asc');\n button.classList.remove('dir-desc');\n button.classList.add('dir-none');\n\n button.closest(selectors.elements.cell).setAttribute('aria-sort', 'none');\n\n updateButtonTitle(button, false);\n }\n });\n\n let ascending = true;\n\n if (sortButton.classList.contains('dir-none')) {\n sortButton.classList.remove('dir-none');\n sortButton.classList.add('dir-asc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'ascending');\n } else if (sortButton.classList.contains('dir-asc')) {\n sortButton.classList.remove('dir-asc');\n sortButton.classList.add('dir-desc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'descending');\n ascending = false;\n } else if (sortButton.classList.contains('dir-desc')) {\n sortButton.classList.remove('dir-desc');\n sortButton.classList.add('dir-asc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'ascending');\n }\n\n updateButtonTitle(sortButton, ascending);\n\n return ascending;\n};\n\n/**\n * Update the button title.\n *\n * @method updateButtonTitle\n * @param {HTMLElement} button Button to update\n * @param {Bool} ascending Sort direction\n * @return {Promise} string promise\n */\nconst updateButtonTitle = (button, ascending) => {\n\n const sortString = (ascending ? 'sortbyxreverse' : 'sortbyx');\n\n return getString(button.dataset.string, 'contentbank')\n .then(columnName => {\n return getString(sortString, 'core', columnName);\n })\n .then(sortByString => {\n button.setAttribute('title', sortByString);\n return sortByString;\n })\n .catch();\n};\n\n/**\n * Update the sort order of the itemlist and update the DOM\n *\n * @method updateSortOrder\n * @param {HTMLElement} fileArea the Dom container for the itemlist\n * @param {Array} itemList Nodelist of Dom elements\n * @param {String} attribute the attribut to sort on\n * @param {Bool} ascending Sort Ascending\n */\nconst updateSortOrder = (fileArea, itemList, attribute, ascending) => {\n const sortList = [].slice.call(itemList).sort(function(a, b) {\n\n let aa = a.getAttribute(attribute);\n let bb = b.getAttribute(attribute);\n if (!isNaN(aa)) {\n aa = parseInt(aa);\n bb = parseInt(bb);\n }\n\n if (ascending) {\n return aa > bb ? 1 : -1;\n } else {\n return aa < bb ? 1 : -1;\n }\n });\n sortList.forEach(listItem => fileArea.appendChild(listItem));\n};\n"],"names":["contentBank","document","querySelector","selectors","regions","contentbank","prefetchStrings","registerListenerEvents","addEventListener","e","viewList","actions","viewlist","viewGrid","viewgrid","fileArea","filearea","shownItems","querySelectorAll","elements","listitem","target","closest","classList","remove","add","setAttribute","forEach","listItem","cell","removeAttribute","heading","setViewListPreference","sortByName","sortname","ascending","updateSortButtons","updateSortOrder","sortByUses","sortuses","sortByDate","sortdate","sortBySize","sortsize","sortByType","sorttype","sortByAuthor","sortauthor","catch","Notification","exception","sortButton","sortbutton","button","updateButtonTitle","contains","sortString","dataset","string","then","columnName","sortByString","itemList","attribute","slice","call","sort","a","b","aa","getAttribute","bb","isNaN","parseInt","appendChild"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"sort.min.js","sources":["../src/sort.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 * Content bank UI actions.\n *\n * @module core_contentbank/sort\n * @copyright 2020 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport selectors from './selectors';\nimport {getString} from 'core/str';\nimport Prefetch from 'core/prefetch';\nimport Notification from 'core/notification';\nimport {setUserPreference} from 'core_user/repository';\n\n/**\n * Set up the contentbank views.\n *\n * @method init\n */\nexport const init = () => {\n const contentBank = document.querySelector(selectors.regions.contentbank);\n Prefetch.prefetchStrings('contentbank', ['contentname', 'uses', 'lastmodified', 'size', 'type', 'author']);\n Prefetch.prefetchStrings('moodle', ['sortbyx', 'sortbyxreverse']);\n registerListenerEvents(contentBank);\n};\n\n/**\n * Register contentbank related event listeners.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} contentBank The DOM node of the content bank\n */\nconst registerListenerEvents = (contentBank) => {\n\n contentBank.addEventListener('click', e => {\n const viewList = contentBank.querySelector(selectors.actions.viewlist);\n const viewGrid = contentBank.querySelector(selectors.actions.viewgrid);\n const fileArea = contentBank.querySelector(selectors.regions.filearea);\n const shownItems = fileArea.querySelectorAll(selectors.elements.listitem);\n\n // View as Grid button.\n if (e.target.closest(selectors.actions.viewgrid)) {\n contentBank.classList.remove('view-list');\n contentBank.classList.add('view-grid');\n if (fileArea && shownItems) {\n fileArea.setAttribute('role', 'list');\n shownItems.forEach(listItem => {\n listItem.setAttribute('role', 'listitem');\n listItem.querySelectorAll(selectors.elements.cell).forEach(cell => cell.removeAttribute('role'));\n });\n\n const heading = fileArea.querySelector(selectors.elements.heading);\n if (heading) {\n heading.removeAttribute('role');\n heading.querySelectorAll(selectors.elements.cell).forEach(cell => cell.removeAttribute('role'));\n }\n }\n viewGrid.classList.add('active');\n viewList.classList.remove('active');\n setViewListPreference(false);\n\n return;\n }\n\n // View as List button.\n if (e.target.closest(selectors.actions.viewlist)) {\n contentBank.classList.remove('view-grid');\n contentBank.classList.add('view-list');\n if (fileArea && shownItems) {\n fileArea.setAttribute('role', 'table');\n shownItems.forEach(listItem => {\n listItem.setAttribute('role', 'row');\n listItem.querySelectorAll(selectors.elements.cell).forEach(cell => cell.setAttribute('role', 'cell'));\n });\n\n const heading = fileArea.querySelector(selectors.elements.heading);\n if (heading) {\n heading.setAttribute('role', 'row');\n heading.querySelectorAll(selectors.elements.cell).forEach(cell => cell.setAttribute('role', 'columnheader'));\n }\n }\n viewList.classList.add('active');\n viewGrid.classList.remove('active');\n setViewListPreference(true);\n\n return;\n }\n\n if (fileArea && shownItems) {\n\n // Sort by file name alphabetical\n const sortByName = e.target.closest(selectors.actions.sortname);\n if (sortByName) {\n const ascending = updateSortButtons(contentBank, sortByName);\n updateSortOrder(fileArea, shownItems, 'data-file', ascending);\n return;\n }\n\n // Sort by uses.\n const sortByUses = e.target.closest(selectors.actions.sortuses);\n if (sortByUses) {\n const ascending = updateSortButtons(contentBank, sortByUses);\n updateSortOrder(fileArea, shownItems, 'data-uses', ascending);\n return;\n }\n\n // Sort by date.\n const sortByDate = e.target.closest(selectors.actions.sortdate);\n if (sortByDate) {\n const ascending = updateSortButtons(contentBank, sortByDate);\n updateSortOrder(fileArea, shownItems, 'data-timemodified', ascending);\n return;\n }\n\n // Sort by size.\n const sortBySize = e.target.closest(selectors.actions.sortsize);\n if (sortBySize) {\n const ascending = updateSortButtons(contentBank, sortBySize);\n updateSortOrder(fileArea, shownItems, 'data-bytes', ascending);\n return;\n }\n\n // Sort by type.\n const sortByType = e.target.closest(selectors.actions.sorttype);\n if (sortByType) {\n const ascending = updateSortButtons(contentBank, sortByType);\n updateSortOrder(fileArea, shownItems, 'data-type', ascending);\n return;\n }\n\n // Sort by author.\n const sortByAuthor = e.target.closest(selectors.actions.sortauthor);\n if (sortByAuthor) {\n const ascending = updateSortButtons(contentBank, sortByAuthor);\n updateSortOrder(fileArea, shownItems, 'data-author', ascending);\n }\n return;\n }\n });\n};\n\n\n/**\n * Set the contentbank user preference in list view\n *\n * @param {Bool} viewList view ContentBank as list.\n * @return {Promise} Repository promise.\n */\nconst setViewListPreference = function(viewList) {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (viewList === false) {\n viewList = null;\n }\n\n return setUserPreference('core_contentbank_view_list', viewList)\n .catch(Notification.exception);\n};\n\n/**\n * Update the sort button view.\n *\n * @method updateSortButtons\n * @param {HTMLElement} contentBank The DOM node of the contentbank button\n * @param {HTMLElement} sortButton The DOM node of the sort button\n * @return {Bool} sort ascending\n */\nconst updateSortButtons = (contentBank, sortButton) => {\n const sortButtons = contentBank.querySelectorAll(selectors.elements.sortbutton);\n\n sortButtons.forEach((button) => {\n if (button !== sortButton) {\n button.classList.remove('dir-asc');\n button.classList.remove('dir-desc');\n button.classList.add('dir-none');\n\n button.closest(selectors.elements.cell).setAttribute('aria-sort', 'none');\n\n updateButtonTitle(button, false);\n }\n });\n\n let ascending = true;\n\n if (sortButton.classList.contains('dir-none')) {\n sortButton.classList.remove('dir-none');\n sortButton.classList.add('dir-asc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'ascending');\n } else if (sortButton.classList.contains('dir-asc')) {\n sortButton.classList.remove('dir-asc');\n sortButton.classList.add('dir-desc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'descending');\n ascending = false;\n } else if (sortButton.classList.contains('dir-desc')) {\n sortButton.classList.remove('dir-desc');\n sortButton.classList.add('dir-asc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'ascending');\n }\n\n updateButtonTitle(sortButton, ascending);\n\n return ascending;\n};\n\n/**\n * Update the button title.\n *\n * @method updateButtonTitle\n * @param {HTMLElement} button Button to update\n * @param {Bool} ascending Sort direction\n * @return {Promise} string promise\n */\nconst updateButtonTitle = (button, ascending) => {\n\n const sortString = (ascending ? 'sortbyxreverse' : 'sortbyx');\n\n return getString(button.dataset.string, 'contentbank')\n .then(columnName => {\n return getString(sortString, 'core', columnName);\n })\n .then(sortByString => {\n button.setAttribute('title', sortByString);\n return sortByString;\n })\n .catch();\n};\n\n/**\n * Update the sort order of the itemlist and update the DOM\n *\n * @method updateSortOrder\n * @param {HTMLElement} fileArea the Dom container for the itemlist\n * @param {Array} itemList Nodelist of Dom elements\n * @param {String} attribute the attribut to sort on\n * @param {Bool} ascending Sort Ascending\n */\nconst updateSortOrder = (fileArea, itemList, attribute, ascending) => {\n const sortList = [].slice.call(itemList).sort(function(a, b) {\n\n let aa = a.getAttribute(attribute);\n let bb = b.getAttribute(attribute);\n if (!isNaN(aa)) {\n aa = parseInt(aa);\n bb = parseInt(bb);\n }\n\n if (ascending) {\n return aa > bb ? 1 : -1;\n } else {\n return aa < bb ? 1 : -1;\n }\n });\n sortList.forEach(listItem => fileArea.appendChild(listItem));\n};\n"],"names":["contentBank","document","querySelector","selectors","regions","contentbank","prefetchStrings","registerListenerEvents","addEventListener","e","viewList","actions","viewlist","viewGrid","viewgrid","fileArea","filearea","shownItems","querySelectorAll","elements","listitem","target","closest","classList","remove","add","setAttribute","forEach","listItem","cell","removeAttribute","heading","setViewListPreference","sortByName","sortname","ascending","updateSortButtons","updateSortOrder","sortByUses","sortuses","sortByDate","sortdate","sortBySize","sortsize","sortByType","sorttype","sortByAuthor","sortauthor","catch","Notification","exception","sortButton","sortbutton","button","updateButtonTitle","contains","sortString","dataset","string","then","columnName","sortByString","itemList","attribute","slice","call","sort","a","b","aa","getAttribute","bb","isNaN","parseInt","appendChild"],"mappings":"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"} \ No newline at end of file diff --git a/contentbank/amd/build/upload.min.js b/contentbank/amd/build/upload.min.js index 7183b10dda7..fecee813797 100644 --- a/contentbank/amd/build/upload.min.js +++ b/contentbank/amd/build/upload.min.js @@ -5,6 +5,6 @@ define("core_contentbank/upload",["exports","core_form/modalform","core/str"],(f * @module core_contentbank/upload * @copyright 2021 Sara Arjona * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.initModal=void 0,_modalform=(obj=_modalform)&&obj.__esModule?obj:{default:obj};_exports.initModal=(elementSelector,formClass,contextId,contentId)=>{document.querySelector(elementSelector).addEventListener("click",(function(e){e.preventDefault();const form=new _modalform.default({formClass:formClass,args:{contextid:contextId,id:contentId},modalConfig:{title:(0,_str.get_string)("upload","contentbank")},returnFocus:e.target});form.addEventListener(form.events.FORM_SUBMITTED,(event=>{document.location=event.detail.returnurl})),form.show()}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.initModal=void 0,_modalform=(obj=_modalform)&&obj.__esModule?obj:{default:obj};_exports.initModal=(elementSelector,formClass,contextId,contentId)=>{document.querySelector(elementSelector).addEventListener("click",(function(e){e.preventDefault();const form=new _modalform.default({formClass:formClass,args:{contextid:contextId,id:contentId},modalConfig:{title:(0,_str.getString)("upload","contentbank")},returnFocus:e.target});form.addEventListener(form.events.FORM_SUBMITTED,(event=>{document.location=event.detail.returnurl})),form.show()}))}})); //# sourceMappingURL=upload.min.js.map \ No newline at end of file diff --git a/contentbank/amd/build/upload.min.js.map b/contentbank/amd/build/upload.min.js.map index 6d9dd313580..32755e122f2 100644 --- a/contentbank/amd/build/upload.min.js.map +++ b/contentbank/amd/build/upload.min.js.map @@ -1 +1 @@ -{"version":3,"file":"upload.min.js","sources":["../src/upload.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 * Module to handle AJAX interactions with content bank upload files.\n *\n * @module core_contentbank/upload\n * @copyright 2021 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport ModalForm from 'core_form/modalform';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Initialize upload files to the content bank form as Modal form.\n *\n * @param {String} elementSelector\n * @param {String} formClass\n * @param {Integer} contextId\n * @param {Integer} contentId\n */\nexport const initModal = (elementSelector, formClass, contextId, contentId) => {\n const element = document.querySelector(elementSelector);\n element.addEventListener('click', function(e) {\n e.preventDefault();\n const form = new ModalForm({\n formClass,\n args: {\n contextid: contextId,\n id: contentId,\n },\n modalConfig: {title: getString('upload', 'contentbank')},\n returnFocus: e.target,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, (event) => {\n document.location = event.detail.returnurl;\n });\n form.show();\n });\n};\n"],"names":["elementSelector","formClass","contextId","contentId","document","querySelector","addEventListener","e","preventDefault","form","ModalForm","args","contextid","id","modalConfig","title","returnFocus","target","events","FORM_SUBMITTED","event","location","detail","returnurl","show"],"mappings":";;;;;;;wKAiCyB,CAACA,gBAAiBC,UAAWC,UAAWC,aAC7CC,SAASC,cAAcL,iBAC/BM,iBAAiB,SAAS,SAASC,GACvCA,EAAEC,uBACIC,KAAO,IAAIC,mBAAU,CACvBT,UAAAA,UACAU,KAAM,CACFC,UAAWV,UACXW,GAAIV,WAERW,YAAa,CAACC,OAAO,mBAAU,SAAU,gBACzCC,YAAaT,EAAEU,SAEnBR,KAAKH,iBAAiBG,KAAKS,OAAOC,gBAAiBC,QAC/ChB,SAASiB,SAAWD,MAAME,OAAOC,aAErCd,KAAKe"} \ No newline at end of file +{"version":3,"file":"upload.min.js","sources":["../src/upload.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 * Module to handle AJAX interactions with content bank upload files.\n *\n * @module core_contentbank/upload\n * @copyright 2021 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport ModalForm from 'core_form/modalform';\nimport {getString} from 'core/str';\n\n/**\n * Initialize upload files to the content bank form as Modal form.\n *\n * @param {String} elementSelector\n * @param {String} formClass\n * @param {Integer} contextId\n * @param {Integer} contentId\n */\nexport const initModal = (elementSelector, formClass, contextId, contentId) => {\n const element = document.querySelector(elementSelector);\n element.addEventListener('click', function(e) {\n e.preventDefault();\n const form = new ModalForm({\n formClass,\n args: {\n contextid: contextId,\n id: contentId,\n },\n modalConfig: {title: getString('upload', 'contentbank')},\n returnFocus: e.target,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, (event) => {\n document.location = event.detail.returnurl;\n });\n form.show();\n });\n};\n"],"names":["elementSelector","formClass","contextId","contentId","document","querySelector","addEventListener","e","preventDefault","form","ModalForm","args","contextid","id","modalConfig","title","returnFocus","target","events","FORM_SUBMITTED","event","location","detail","returnurl","show"],"mappings":";;;;;;;wKAiCyB,CAACA,gBAAiBC,UAAWC,UAAWC,aAC7CC,SAASC,cAAcL,iBAC/BM,iBAAiB,SAAS,SAASC,GACvCA,EAAEC,uBACIC,KAAO,IAAIC,mBAAU,CACvBT,UAAAA,UACAU,KAAM,CACFC,UAAWV,UACXW,GAAIV,WAERW,YAAa,CAACC,OAAO,kBAAU,SAAU,gBACzCC,YAAaT,EAAEU,SAEnBR,KAAKH,iBAAiBG,KAAKS,OAAOC,gBAAiBC,QAC/ChB,SAASiB,SAAWD,MAAME,OAAOC,aAErCd,KAAKe"} \ No newline at end of file diff --git a/contentbank/amd/src/search.js b/contentbank/amd/src/search.js index 9fa09c2317f..05af05c6e7d 100644 --- a/contentbank/amd/src/search.js +++ b/contentbank/amd/src/search.js @@ -23,7 +23,7 @@ import $ from 'jquery'; import selectors from 'core_contentbank/selectors'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Pending from 'core/pending'; import {debounce} from 'core/utils'; diff --git a/contentbank/amd/src/sort.js b/contentbank/amd/src/sort.js index 99ff12d935b..1ee74e0875d 100644 --- a/contentbank/amd/src/sort.js +++ b/contentbank/amd/src/sort.js @@ -22,7 +22,7 @@ */ import selectors from './selectors'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Prefetch from 'core/prefetch'; import Notification from 'core/notification'; import {setUserPreference} from 'core_user/repository'; diff --git a/contentbank/amd/src/upload.js b/contentbank/amd/src/upload.js index fb1f7c8b241..a832d4cb2d7 100644 --- a/contentbank/amd/src/upload.js +++ b/contentbank/amd/src/upload.js @@ -21,7 +21,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ import ModalForm from 'core_form/modalform'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; /** * Initialize upload files to the content bank form as Modal form. diff --git a/course/amd/build/activitychooser.min.js b/course/amd/build/activitychooser.min.js index 188b252d294..054731e7e49 100644 --- a/course/amd/build/activitychooser.min.js +++ b/course/amd/build/activitychooser.min.js @@ -5,6 +5,6 @@ define("core_course/activitychooser",["exports","core_course/local/activitychoos * @module core_course/activitychooser * @copyright 2020 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,ChooserDialogue=_interopRequireWildcard(ChooserDialogue),Repository=_interopRequireWildcard(Repository),_selectors=_interopRequireDefault(_selectors),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),Templates=_interopRequireWildcard(Templates),ModalFactory=_interopRequireWildcard(ModalFactory),_pending=_interopRequireDefault(_pending);let initialized=!1;_exports.init=(courseId,chooserConfig)=>{const pendingPromise=new _pending.default;registerListenerEvents(courseId,chooserConfig),pendingPromise.resolve()};const registerListenerEvents=(courseId,chooserConfig)=>{if(initialized)return;const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate],fetchModuleData=(()=>{let innerPromise=null;return()=>(innerPromise||(innerPromise=new Promise((resolve=>{resolve(Repository.activityModules(courseId))}))),innerPromise)})(),fetchFooterData=(()=>{let footerInnerPromise=null;return sectionId=>(footerInnerPromise||(footerInnerPromise=new Promise((resolve=>{resolve(Repository.fetchFooterData(courseId,sectionId))}))),footerInnerPromise)})();_custom_interaction_events.default.define(document,events),events.forEach((event=>{document.addEventListener(event,(async e=>{if(e.target.closest(_selectors.default.elements.sectionmodchooser)){let caller;const sectionDiv=e.target.closest(_selectors.default.elements.section),button=e.target.closest(_selectors.default.elements.sectionmodchooser);let bodyPromiseResolver;caller=null!==sectionDiv&§ionDiv.hasAttribute("data-sectionid")?sectionDiv:button;const bodyPromise=new Promise((resolve=>{bodyPromiseResolver=resolve})),footerData=await fetchFooterData(caller.dataset.sectionid),sectionModal=buildModal(bodyPromise,footerData),data=await fetchModuleData().catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_course/local/activitychooser/error",errorTemplateData))}));if(!data)return;const builtModuleData=sectionIdMapper(data,caller.dataset.sectionid,caller.dataset.sectionreturnid,caller.dataset.beforemod);ChooserDialogue.displayChooser(sectionModal,builtModuleData,partiallyAppliedFavouriteManager(data,caller.dataset.sectionid),footerData),bodyPromiseResolver(await Templates.render("core_course/activitychooser",templateDataBuilder(builtModuleData,chooserConfig)))}}))})),initialized=!0},sectionIdMapper=(webServiceData,id,sectionreturnid,beforemod)=>{const newData=JSON.parse(JSON.stringify(webServiceData));return newData.content_items.forEach((module=>{module.link+="§ion="+id+"&sr="+(null!=sectionreturnid?sectionreturnid:0)+"&beforemod="+(null!=beforemod?beforemod:0)})),newData.content_items},templateDataBuilder=(data,chooserConfig)=>{let activities=[],resources=[],showAll=!0,showActivities=!1,showResources=!1;const tabMode=parseInt(chooserConfig.tabmode),favourites=data.filter((mod=>!0===mod.favourite)),recommended=data.filter((mod=>!0===mod.recommended));(tabMode=>-1!==[0,3,2,5].indexOf(tabMode))(tabMode)&&(activities=data.filter((mod=>0===mod.archetype)),resources=data.filter((mod=>1===mod.archetype)),showActivities=!0,showResources=!0,2!==tabMode&&5!==tabMode||(showAll=!1));const recommendedBeginning=-1!==[3,4,5].indexOf(tabMode),favouritesFirst=!!favourites.length,recommendedFirst=!1===favouritesFirst&&!0===recommendedBeginning&&!!recommended.length;return{default:data,showAll:showAll,activities:activities,showActivities:showActivities,activitiesFirst:!1===showAll&&!1===favouritesFirst&&!1===recommendedFirst,resources:resources,showResources:showResources,favourites:favourites,recommended:recommended,recommendedFirst:recommendedFirst,recommendedBeginning:recommendedBeginning,favouritesFirst:favouritesFirst,fallback:!0===showAll&&!1===favouritesFirst&&!1===recommendedFirst}},buildModal=(bodyPromise,footer)=>ModalFactory.create({type:ModalFactory.types.DEFAULT,title:(0,_str.get_string)("addresourceoractivity"),body:bodyPromise,footer:footer.customfootertemplate,large:!0,scrollable:!1,templateContext:{classes:"modchooser"}}).then((modal=>(modal.show(),modal))),partiallyAppliedFavouriteManager=(moduleData,sectionId)=>async(internal,favourite,modalBody)=>{const favouriteArea=modalBody.querySelector(_selectors.default.render.favourites),favouriteButtons=modalBody.querySelectorAll('[data-internal="'.concat(internal,'"] ').concat(_selectors.default.actions.optionActions.manageFavourite)),favouriteTabNav=modalBody.querySelector(_selectors.default.regions.favouriteTabNav),result=moduleData.content_items.find((_ref=>{let{name:name}=_ref;return name===internal})),newFaves={};if(result)if(favourite){result.favourite=!0,newFaves.content_items=moduleData.content_items.filter((mod=>!0===mod.favourite));const builtFaves=sectionIdMapper(newFaves,sectionId),{html:html,js:js}=await Templates.renderForPromise("core_course/local/activitychooser/favourites",{favourites:builtFaves});await Templates.replaceNodeContents(favouriteArea,html,js),Array.from(favouriteButtons).forEach((element=>{element.classList.remove("text-muted"),element.classList.add("text-primary"),element.dataset.favourited="true",element.setAttribute("aria-pressed",!0),element.firstElementChild.classList.remove("fa-star-o"),element.firstElementChild.classList.add("fa-star")})),favouriteTabNav.classList.remove("d-none")}else{result.favourite=!1;const nodeToRemove=favouriteArea.querySelector('[data-internal="'.concat(internal,'"]'));nodeToRemove.parentNode.removeChild(nodeToRemove),Array.from(favouriteButtons).forEach((element=>{element.classList.add("text-muted"),element.classList.remove("text-primary"),element.dataset.favourited="false",element.setAttribute("aria-pressed",!1),element.firstElementChild.classList.remove("fa-star"),element.firstElementChild.classList.add("fa-star-o")}));0===moduleData.content_items.filter((mod=>!0===mod.favourite)).length&&((favouriteTabNav,modalBody)=>{if(favouriteTabNav.tabIndex=-1,favouriteTabNav.classList.add("d-none"),favouriteTabNav.classList.contains("active")){favouriteTabNav.classList.remove("active"),favouriteTabNav.setAttribute("aria-selected","false"),modalBody.querySelector(_selectors.default.regions.favouriteTab).classList.remove("active");const defaultTabNav=modalBody.querySelector(_selectors.default.regions.defaultTabNav),activitiesTabNav=modalBody.querySelector(_selectors.default.regions.activityTabNav);!1===defaultTabNav.classList.contains("d-none")?(defaultTabNav.classList.add("active"),defaultTabNav.setAttribute("aria-selected","true"),defaultTabNav.tabIndex=0,defaultTabNav.focus(),modalBody.querySelector(_selectors.default.regions.defaultTab).classList.add("active")):(activitiesTabNav.classList.add("active"),activitiesTabNav.setAttribute("aria-selected","true"),activitiesTabNav.tabIndex=0,activitiesTabNav.focus(),modalBody.querySelector(_selectors.default.regions.activityTab).classList.add("active"))}})(favouriteTabNav,modalBody)}}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,ChooserDialogue=_interopRequireWildcard(ChooserDialogue),Repository=_interopRequireWildcard(Repository),_selectors=_interopRequireDefault(_selectors),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),Templates=_interopRequireWildcard(Templates),ModalFactory=_interopRequireWildcard(ModalFactory),_pending=_interopRequireDefault(_pending);let initialized=!1;_exports.init=(courseId,chooserConfig)=>{const pendingPromise=new _pending.default;registerListenerEvents(courseId,chooserConfig),pendingPromise.resolve()};const registerListenerEvents=(courseId,chooserConfig)=>{if(initialized)return;const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate],fetchModuleData=(()=>{let innerPromise=null;return()=>(innerPromise||(innerPromise=new Promise((resolve=>{resolve(Repository.activityModules(courseId))}))),innerPromise)})(),fetchFooterData=(()=>{let footerInnerPromise=null;return sectionId=>(footerInnerPromise||(footerInnerPromise=new Promise((resolve=>{resolve(Repository.fetchFooterData(courseId,sectionId))}))),footerInnerPromise)})();_custom_interaction_events.default.define(document,events),events.forEach((event=>{document.addEventListener(event,(async e=>{if(e.target.closest(_selectors.default.elements.sectionmodchooser)){let caller;const sectionDiv=e.target.closest(_selectors.default.elements.section),button=e.target.closest(_selectors.default.elements.sectionmodchooser);let bodyPromiseResolver;caller=null!==sectionDiv&§ionDiv.hasAttribute("data-sectionid")?sectionDiv:button;const bodyPromise=new Promise((resolve=>{bodyPromiseResolver=resolve})),footerData=await fetchFooterData(caller.dataset.sectionid),sectionModal=buildModal(bodyPromise,footerData),data=await fetchModuleData().catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_course/local/activitychooser/error",errorTemplateData))}));if(!data)return;const builtModuleData=sectionIdMapper(data,caller.dataset.sectionid,caller.dataset.sectionreturnid,caller.dataset.beforemod);ChooserDialogue.displayChooser(sectionModal,builtModuleData,partiallyAppliedFavouriteManager(data,caller.dataset.sectionid),footerData),bodyPromiseResolver(await Templates.render("core_course/activitychooser",templateDataBuilder(builtModuleData,chooserConfig)))}}))})),initialized=!0},sectionIdMapper=(webServiceData,id,sectionreturnid,beforemod)=>{const newData=JSON.parse(JSON.stringify(webServiceData));return newData.content_items.forEach((module=>{module.link+="§ion="+id+"&sr="+(null!=sectionreturnid?sectionreturnid:0)+"&beforemod="+(null!=beforemod?beforemod:0)})),newData.content_items},templateDataBuilder=(data,chooserConfig)=>{let activities=[],resources=[],showAll=!0,showActivities=!1,showResources=!1;const tabMode=parseInt(chooserConfig.tabmode),favourites=data.filter((mod=>!0===mod.favourite)),recommended=data.filter((mod=>!0===mod.recommended));(tabMode=>-1!==[0,3,2,5].indexOf(tabMode))(tabMode)&&(activities=data.filter((mod=>0===mod.archetype)),resources=data.filter((mod=>1===mod.archetype)),showActivities=!0,showResources=!0,2!==tabMode&&5!==tabMode||(showAll=!1));const recommendedBeginning=-1!==[3,4,5].indexOf(tabMode),favouritesFirst=!!favourites.length,recommendedFirst=!1===favouritesFirst&&!0===recommendedBeginning&&!!recommended.length;return{default:data,showAll:showAll,activities:activities,showActivities:showActivities,activitiesFirst:!1===showAll&&!1===favouritesFirst&&!1===recommendedFirst,resources:resources,showResources:showResources,favourites:favourites,recommended:recommended,recommendedFirst:recommendedFirst,recommendedBeginning:recommendedBeginning,favouritesFirst:favouritesFirst,fallback:!0===showAll&&!1===favouritesFirst&&!1===recommendedFirst}},buildModal=(bodyPromise,footer)=>ModalFactory.create({type:ModalFactory.types.DEFAULT,title:(0,_str.getString)("addresourceoractivity"),body:bodyPromise,footer:footer.customfootertemplate,large:!0,scrollable:!1,templateContext:{classes:"modchooser"}}).then((modal=>(modal.show(),modal))),partiallyAppliedFavouriteManager=(moduleData,sectionId)=>async(internal,favourite,modalBody)=>{const favouriteArea=modalBody.querySelector(_selectors.default.render.favourites),favouriteButtons=modalBody.querySelectorAll('[data-internal="'.concat(internal,'"] ').concat(_selectors.default.actions.optionActions.manageFavourite)),favouriteTabNav=modalBody.querySelector(_selectors.default.regions.favouriteTabNav),result=moduleData.content_items.find((_ref=>{let{name:name}=_ref;return name===internal})),newFaves={};if(result)if(favourite){result.favourite=!0,newFaves.content_items=moduleData.content_items.filter((mod=>!0===mod.favourite));const builtFaves=sectionIdMapper(newFaves,sectionId),{html:html,js:js}=await Templates.renderForPromise("core_course/local/activitychooser/favourites",{favourites:builtFaves});await Templates.replaceNodeContents(favouriteArea,html,js),Array.from(favouriteButtons).forEach((element=>{element.classList.remove("text-muted"),element.classList.add("text-primary"),element.dataset.favourited="true",element.setAttribute("aria-pressed",!0),element.firstElementChild.classList.remove("fa-star-o"),element.firstElementChild.classList.add("fa-star")})),favouriteTabNav.classList.remove("d-none")}else{result.favourite=!1;const nodeToRemove=favouriteArea.querySelector('[data-internal="'.concat(internal,'"]'));nodeToRemove.parentNode.removeChild(nodeToRemove),Array.from(favouriteButtons).forEach((element=>{element.classList.add("text-muted"),element.classList.remove("text-primary"),element.dataset.favourited="false",element.setAttribute("aria-pressed",!1),element.firstElementChild.classList.remove("fa-star"),element.firstElementChild.classList.add("fa-star-o")}));0===moduleData.content_items.filter((mod=>!0===mod.favourite)).length&&((favouriteTabNav,modalBody)=>{if(favouriteTabNav.tabIndex=-1,favouriteTabNav.classList.add("d-none"),favouriteTabNav.classList.contains("active")){favouriteTabNav.classList.remove("active"),favouriteTabNav.setAttribute("aria-selected","false"),modalBody.querySelector(_selectors.default.regions.favouriteTab).classList.remove("active");const defaultTabNav=modalBody.querySelector(_selectors.default.regions.defaultTabNav),activitiesTabNav=modalBody.querySelector(_selectors.default.regions.activityTabNav);!1===defaultTabNav.classList.contains("d-none")?(defaultTabNav.classList.add("active"),defaultTabNav.setAttribute("aria-selected","true"),defaultTabNav.tabIndex=0,defaultTabNav.focus(),modalBody.querySelector(_selectors.default.regions.defaultTab).classList.add("active")):(activitiesTabNav.classList.add("active"),activitiesTabNav.setAttribute("aria-selected","true"),activitiesTabNav.tabIndex=0,activitiesTabNav.focus(),modalBody.querySelector(_selectors.default.regions.activityTab).classList.add("active"))}})(favouriteTabNav,modalBody)}}})); //# sourceMappingURL=activitychooser.min.js.map \ No newline at end of file diff --git a/course/amd/build/activitychooser.min.js.map b/course/amd/build/activitychooser.min.js.map index 4184326dd45..9eb8b58f57b 100644 --- a/course/amd/build/activitychooser.min.js.map +++ b/course/amd/build/activitychooser.min.js.map @@ -1 +1 @@ -{"version":3,"file":"activitychooser.min.js","sources":["../src/activitychooser.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 * A type of dialogue used as for choosing modules in a course.\n *\n * @module core_course/activitychooser\n * @copyright 2020 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as ChooserDialogue from 'core_course/local/activitychooser/dialogue';\nimport * as Repository from 'core_course/local/activitychooser/repository';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport CustomEvents from 'core/custom_interaction_events';\nimport * as Templates from 'core/templates';\nimport * as ModalFactory from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\n\n// Set up some JS module wide constants that can be added to in the future.\n\n// Tab config options.\nconst ALLACTIVITIESRESOURCES = 0;\nconst ACTIVITIESRESOURCES = 2;\nconst ALLACTIVITIESRESOURCESREC = 3;\nconst ONLYALLREC = 4;\nconst ACTIVITIESRESOURCESREC = 5;\n\n\n// Module types.\nconst ACTIVITY = 0;\nconst RESOURCE = 1;\n\nlet initialized = false;\n\n/**\n * Set up the activity chooser.\n *\n * @method init\n * @param {Number} courseId Course ID to use later on in fetchModules()\n * @param {Object} chooserConfig Any PHP config settings that we may need to reference\n */\nexport const init = (courseId, chooserConfig) => {\n const pendingPromise = new Pending();\n\n registerListenerEvents(courseId, chooserConfig);\n\n pendingPromise.resolve();\n};\n\n/**\n * Once a selection has been made make the modal & module information and pass it along\n *\n * @method registerListenerEvents\n * @param {Number} courseId\n * @param {Object} chooserConfig Any PHP config settings that we may need to reference\n */\nconst registerListenerEvents = (courseId, chooserConfig) => {\n\n // Ensure we only add our listeners once.\n if (initialized) {\n return;\n }\n\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n\n const fetchModuleData = (() => {\n let innerPromise = null;\n\n return () => {\n if (!innerPromise) {\n innerPromise = new Promise((resolve) => {\n resolve(Repository.activityModules(courseId));\n });\n }\n\n return innerPromise;\n };\n })();\n\n const fetchFooterData = (() => {\n let footerInnerPromise = null;\n\n return (sectionId) => {\n if (!footerInnerPromise) {\n footerInnerPromise = new Promise((resolve) => {\n resolve(Repository.fetchFooterData(courseId, sectionId));\n });\n }\n\n return footerInnerPromise;\n };\n })();\n\n CustomEvents.define(document, events);\n\n // Display module chooser event listeners.\n events.forEach((event) => {\n document.addEventListener(event, async(e) => {\n if (e.target.closest(selectors.elements.sectionmodchooser)) {\n let caller;\n // We need to know who called this.\n // Standard courses use the ID in the main section info.\n const sectionDiv = e.target.closest(selectors.elements.section);\n // Front page courses need some special handling.\n const button = e.target.closest(selectors.elements.sectionmodchooser);\n\n // If we don't have a section ID use the fallback ID.\n // We always want the sectionDiv caller first as it keeps track of section ID's after DnD changes.\n // The button attribute is always just a fallback for us as the section div is not always available.\n // A YUI change could be done maybe to only update the button attribute but we are going for minimal change here.\n if (sectionDiv !== null && sectionDiv.hasAttribute('data-sectionid')) {\n // We check for attributes just in case of outdated contrib course formats.\n caller = sectionDiv;\n } else {\n caller = button;\n }\n\n // We want to show the modal instantly but loading whilst waiting for our data.\n let bodyPromiseResolver;\n const bodyPromise = new Promise(resolve => {\n bodyPromiseResolver = resolve;\n });\n\n const footerData = await fetchFooterData(caller.dataset.sectionid);\n const sectionModal = buildModal(bodyPromise, footerData);\n\n // Now we have a modal we should start fetching data.\n // If an error occurs while fetching the data, display the error within the modal.\n const data = await fetchModuleData().catch(async(e) => {\n const errorTemplateData = {\n 'errormessage': e.message\n };\n bodyPromiseResolver(await Templates.render('core_course/local/activitychooser/error', errorTemplateData));\n });\n\n // Early return if there is no module data.\n if (!data) {\n return;\n }\n\n // Apply the section id to all the module instance links.\n const builtModuleData = sectionIdMapper(\n data,\n caller.dataset.sectionid,\n caller.dataset.sectionreturnid,\n caller.dataset.beforemod\n );\n\n ChooserDialogue.displayChooser(\n sectionModal,\n builtModuleData,\n partiallyAppliedFavouriteManager(data, caller.dataset.sectionid),\n footerData,\n );\n\n bodyPromiseResolver(await Templates.render(\n 'core_course/activitychooser',\n templateDataBuilder(builtModuleData, chooserConfig)\n ));\n }\n });\n });\n\n initialized = true;\n};\n\n/**\n * Given the web service data and an ID we want to make a deep copy\n * of the WS data then add on the section ID to the addoption URL\n *\n * @method sectionIdMapper\n * @param {Object} webServiceData Our original data from the Web service call\n * @param {Number} id The ID of the section we need to append to the links\n * @param {Number|null} sectionreturnid The ID of the section return we need to append to the links\n * @param {Number|null} beforemod The ID of the cm we need to append to the links\n * @return {Array} [modules] with URL's built\n */\nconst sectionIdMapper = (webServiceData, id, sectionreturnid, beforemod) => {\n // We need to take a fresh deep copy of the original data as an object is a reference type.\n const newData = JSON.parse(JSON.stringify(webServiceData));\n newData.content_items.forEach((module) => {\n module.link += '§ion=' + id + '&sr=' + (sectionreturnid ?? 0) + '&beforemod=' + (beforemod ?? 0);\n });\n return newData.content_items;\n};\n\n/**\n * Given an array of modules we want to figure out where & how to place them into our template object\n *\n * @method templateDataBuilder\n * @param {Array} data our modules to manipulate into a Templatable object\n * @param {Object} chooserConfig Any PHP config settings that we may need to reference\n * @return {Object} Our built object ready to render out\n */\nconst templateDataBuilder = (data, chooserConfig) => {\n // Setup of various bits and pieces we need to mutate before throwing it to the wolves.\n let activities = [];\n let resources = [];\n let showAll = true;\n let showActivities = false;\n let showResources = false;\n\n // Tab mode can be the following [All, Resources & Activities, All & Activities & Resources].\n const tabMode = parseInt(chooserConfig.tabmode);\n\n // Filter the incoming data to find favourite & recommended modules.\n const favourites = data.filter(mod => mod.favourite === true);\n const recommended = data.filter(mod => mod.recommended === true);\n\n // Whether the activities and resources tabs should be displayed or not.\n const showActivitiesAndResources = (tabMode) => {\n const acceptableModes = [\n ALLACTIVITIESRESOURCES,\n ALLACTIVITIESRESOURCESREC,\n ACTIVITIESRESOURCES,\n ACTIVITIESRESOURCESREC,\n ];\n\n return acceptableModes.indexOf(tabMode) !== -1;\n };\n\n // These modes need Activity & Resource tabs.\n if (showActivitiesAndResources(tabMode)) {\n // Filter the incoming data to find activities then resources.\n activities = data.filter(mod => mod.archetype === ACTIVITY);\n resources = data.filter(mod => mod.archetype === RESOURCE);\n showActivities = true;\n showResources = true;\n\n // We want all of the previous information but no 'All' tab.\n if (tabMode === ACTIVITIESRESOURCES || tabMode === ACTIVITIESRESOURCESREC) {\n showAll = false;\n }\n }\n\n const recommendedBeforeTabs = [\n ALLACTIVITIESRESOURCESREC,\n ONLYALLREC,\n ACTIVITIESRESOURCESREC,\n ];\n // Whether the recommended tab should be displayed before the All/Activities/Resources tabs.\n const recommendedBeginning = recommendedBeforeTabs.indexOf(tabMode) !== -1;\n\n // Given the results of the above filters lets figure out what tab to set active.\n // We have some favourites.\n const favouritesFirst = !!favourites.length;\n const recommendedFirst = favouritesFirst === false && recommendedBeginning === true && !!recommended.length;\n // We are in tabMode 2 without any favourites.\n const activitiesFirst = showAll === false && favouritesFirst === false && recommendedFirst === false;\n // We have nothing fallback to show all modules.\n const fallback = showAll === true && favouritesFirst === false && recommendedFirst === false;\n\n return {\n 'default': data,\n showAll: showAll,\n activities: activities,\n showActivities: showActivities,\n activitiesFirst: activitiesFirst,\n resources: resources,\n showResources: showResources,\n favourites: favourites,\n recommended: recommended,\n recommendedFirst: recommendedFirst,\n recommendedBeginning: recommendedBeginning,\n favouritesFirst: favouritesFirst,\n fallback: fallback,\n };\n};\n\n/**\n * Given an object we want to build a modal ready to show\n *\n * @method buildModal\n * @param {Promise} bodyPromise\n * @param {String|Boolean} footer Either a footer to add or nothing\n * @return {Object} The modal ready to display immediately and render body in later.\n */\nconst buildModal = (bodyPromise, footer) => {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: getString('addresourceoractivity'),\n body: bodyPromise,\n footer: footer.customfootertemplate,\n large: true,\n scrollable: false,\n templateContext: {\n classes: 'modchooser'\n }\n })\n .then(modal => {\n modal.show();\n return modal;\n });\n};\n\n/**\n * A small helper function to handle the case where there are no more favourites\n * and we need to mess a bit with the available tabs in the chooser\n *\n * @method nullFavouriteDomManager\n * @param {HTMLElement} favouriteTabNav Dom node of the favourite tab nav\n * @param {HTMLElement} modalBody Our current modals' body\n */\nconst nullFavouriteDomManager = (favouriteTabNav, modalBody) => {\n favouriteTabNav.tabIndex = -1;\n favouriteTabNav.classList.add('d-none');\n // Need to set active to an available tab.\n if (favouriteTabNav.classList.contains('active')) {\n favouriteTabNav.classList.remove('active');\n favouriteTabNav.setAttribute('aria-selected', 'false');\n const favouriteTab = modalBody.querySelector(selectors.regions.favouriteTab);\n favouriteTab.classList.remove('active');\n const defaultTabNav = modalBody.querySelector(selectors.regions.defaultTabNav);\n const activitiesTabNav = modalBody.querySelector(selectors.regions.activityTabNav);\n if (defaultTabNav.classList.contains('d-none') === false) {\n defaultTabNav.classList.add('active');\n defaultTabNav.setAttribute('aria-selected', 'true');\n defaultTabNav.tabIndex = 0;\n defaultTabNav.focus();\n const defaultTab = modalBody.querySelector(selectors.regions.defaultTab);\n defaultTab.classList.add('active');\n } else {\n activitiesTabNav.classList.add('active');\n activitiesTabNav.setAttribute('aria-selected', 'true');\n activitiesTabNav.tabIndex = 0;\n activitiesTabNav.focus();\n const activitiesTab = modalBody.querySelector(selectors.regions.activityTab);\n activitiesTab.classList.add('active');\n }\n\n }\n};\n\n/**\n * Export a curried function where the builtModules has been applied.\n * We have our array of modules so we can rerender the favourites area and have all of the items sorted.\n *\n * @method partiallyAppliedFavouriteManager\n * @param {Array} moduleData This is our raw WS data that we need to manipulate\n * @param {Number} sectionId We need this to add the sectionID to the URL's in the faves area after rerender\n * @return {Function} partially applied function so we can manipulate DOM nodes easily & update our internal array\n */\nconst partiallyAppliedFavouriteManager = (moduleData, sectionId) => {\n /**\n * Curried function that is being returned.\n *\n * @param {String} internal Internal name of the module to manage\n * @param {Boolean} favourite Is the caller adding a favourite or removing one?\n * @param {HTMLElement} modalBody What we need to update whilst we are here\n */\n return async(internal, favourite, modalBody) => {\n const favouriteArea = modalBody.querySelector(selectors.render.favourites);\n\n // eslint-disable-next-line max-len\n const favouriteButtons = modalBody.querySelectorAll(`[data-internal=\"${internal}\"] ${selectors.actions.optionActions.manageFavourite}`);\n const favouriteTabNav = modalBody.querySelector(selectors.regions.favouriteTabNav);\n const result = moduleData.content_items.find(({name}) => name === internal);\n const newFaves = {};\n if (result) {\n if (favourite) {\n result.favourite = true;\n\n // eslint-disable-next-line camelcase\n newFaves.content_items = moduleData.content_items.filter(mod => mod.favourite === true);\n\n const builtFaves = sectionIdMapper(newFaves, sectionId);\n\n const {html, js} = await Templates.renderForPromise('core_course/local/activitychooser/favourites',\n {favourites: builtFaves});\n\n await Templates.replaceNodeContents(favouriteArea, html, js);\n\n Array.from(favouriteButtons).forEach((element) => {\n element.classList.remove('text-muted');\n element.classList.add('text-primary');\n element.dataset.favourited = 'true';\n element.setAttribute('aria-pressed', true);\n element.firstElementChild.classList.remove('fa-star-o');\n element.firstElementChild.classList.add('fa-star');\n });\n\n favouriteTabNav.classList.remove('d-none');\n } else {\n result.favourite = false;\n\n const nodeToRemove = favouriteArea.querySelector(`[data-internal=\"${internal}\"]`);\n\n nodeToRemove.parentNode.removeChild(nodeToRemove);\n\n Array.from(favouriteButtons).forEach((element) => {\n element.classList.add('text-muted');\n element.classList.remove('text-primary');\n element.dataset.favourited = 'false';\n element.setAttribute('aria-pressed', false);\n element.firstElementChild.classList.remove('fa-star');\n element.firstElementChild.classList.add('fa-star-o');\n });\n const newFaves = moduleData.content_items.filter(mod => mod.favourite === true);\n\n if (newFaves.length === 0) {\n nullFavouriteDomManager(favouriteTabNav, modalBody);\n }\n }\n }\n };\n};\n"],"names":["initialized","courseId","chooserConfig","pendingPromise","Pending","registerListenerEvents","resolve","events","CustomEvents","activate","keyboardActivate","fetchModuleData","innerPromise","Promise","Repository","activityModules","fetchFooterData","footerInnerPromise","sectionId","define","document","forEach","event","addEventListener","async","e","target","closest","selectors","elements","sectionmodchooser","caller","sectionDiv","section","button","bodyPromiseResolver","hasAttribute","bodyPromise","footerData","dataset","sectionid","sectionModal","buildModal","data","catch","errorTemplateData","message","Templates","render","builtModuleData","sectionIdMapper","sectionreturnid","beforemod","ChooserDialogue","displayChooser","partiallyAppliedFavouriteManager","templateDataBuilder","webServiceData","id","newData","JSON","parse","stringify","content_items","module","link","activities","resources","showAll","showActivities","showResources","tabMode","parseInt","tabmode","favourites","filter","mod","favourite","recommended","indexOf","showActivitiesAndResources","archetype","recommendedBeginning","favouritesFirst","length","recommendedFirst","activitiesFirst","fallback","footer","ModalFactory","create","type","types","DEFAULT","title","body","customfootertemplate","large","scrollable","templateContext","classes","then","modal","show","moduleData","internal","modalBody","favouriteArea","querySelector","favouriteButtons","querySelectorAll","actions","optionActions","manageFavourite","favouriteTabNav","regions","result","find","_ref","name","newFaves","builtFaves","html","js","renderForPromise","replaceNodeContents","Array","from","element","classList","remove","add","favourited","setAttribute","firstElementChild","nodeToRemove","parentNode","removeChild","tabIndex","contains","favouriteTab","defaultTabNav","activitiesTabNav","activityTabNav","focus","defaultTab","activityTab","nullFavouriteDomManager"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"activitychooser.min.js","sources":["../src/activitychooser.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 * A type of dialogue used as for choosing modules in a course.\n *\n * @module core_course/activitychooser\n * @copyright 2020 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as ChooserDialogue from 'core_course/local/activitychooser/dialogue';\nimport * as Repository from 'core_course/local/activitychooser/repository';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport CustomEvents from 'core/custom_interaction_events';\nimport * as Templates from 'core/templates';\nimport * as ModalFactory from 'core/modal_factory';\nimport {getString} from 'core/str';\nimport Pending from 'core/pending';\n\n// Set up some JS module wide constants that can be added to in the future.\n\n// Tab config options.\nconst ALLACTIVITIESRESOURCES = 0;\nconst ACTIVITIESRESOURCES = 2;\nconst ALLACTIVITIESRESOURCESREC = 3;\nconst ONLYALLREC = 4;\nconst ACTIVITIESRESOURCESREC = 5;\n\n\n// Module types.\nconst ACTIVITY = 0;\nconst RESOURCE = 1;\n\nlet initialized = false;\n\n/**\n * Set up the activity chooser.\n *\n * @method init\n * @param {Number} courseId Course ID to use later on in fetchModules()\n * @param {Object} chooserConfig Any PHP config settings that we may need to reference\n */\nexport const init = (courseId, chooserConfig) => {\n const pendingPromise = new Pending();\n\n registerListenerEvents(courseId, chooserConfig);\n\n pendingPromise.resolve();\n};\n\n/**\n * Once a selection has been made make the modal & module information and pass it along\n *\n * @method registerListenerEvents\n * @param {Number} courseId\n * @param {Object} chooserConfig Any PHP config settings that we may need to reference\n */\nconst registerListenerEvents = (courseId, chooserConfig) => {\n\n // Ensure we only add our listeners once.\n if (initialized) {\n return;\n }\n\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n\n const fetchModuleData = (() => {\n let innerPromise = null;\n\n return () => {\n if (!innerPromise) {\n innerPromise = new Promise((resolve) => {\n resolve(Repository.activityModules(courseId));\n });\n }\n\n return innerPromise;\n };\n })();\n\n const fetchFooterData = (() => {\n let footerInnerPromise = null;\n\n return (sectionId) => {\n if (!footerInnerPromise) {\n footerInnerPromise = new Promise((resolve) => {\n resolve(Repository.fetchFooterData(courseId, sectionId));\n });\n }\n\n return footerInnerPromise;\n };\n })();\n\n CustomEvents.define(document, events);\n\n // Display module chooser event listeners.\n events.forEach((event) => {\n document.addEventListener(event, async(e) => {\n if (e.target.closest(selectors.elements.sectionmodchooser)) {\n let caller;\n // We need to know who called this.\n // Standard courses use the ID in the main section info.\n const sectionDiv = e.target.closest(selectors.elements.section);\n // Front page courses need some special handling.\n const button = e.target.closest(selectors.elements.sectionmodchooser);\n\n // If we don't have a section ID use the fallback ID.\n // We always want the sectionDiv caller first as it keeps track of section ID's after DnD changes.\n // The button attribute is always just a fallback for us as the section div is not always available.\n // A YUI change could be done maybe to only update the button attribute but we are going for minimal change here.\n if (sectionDiv !== null && sectionDiv.hasAttribute('data-sectionid')) {\n // We check for attributes just in case of outdated contrib course formats.\n caller = sectionDiv;\n } else {\n caller = button;\n }\n\n // We want to show the modal instantly but loading whilst waiting for our data.\n let bodyPromiseResolver;\n const bodyPromise = new Promise(resolve => {\n bodyPromiseResolver = resolve;\n });\n\n const footerData = await fetchFooterData(caller.dataset.sectionid);\n const sectionModal = buildModal(bodyPromise, footerData);\n\n // Now we have a modal we should start fetching data.\n // If an error occurs while fetching the data, display the error within the modal.\n const data = await fetchModuleData().catch(async(e) => {\n const errorTemplateData = {\n 'errormessage': e.message\n };\n bodyPromiseResolver(await Templates.render('core_course/local/activitychooser/error', errorTemplateData));\n });\n\n // Early return if there is no module data.\n if (!data) {\n return;\n }\n\n // Apply the section id to all the module instance links.\n const builtModuleData = sectionIdMapper(\n data,\n caller.dataset.sectionid,\n caller.dataset.sectionreturnid,\n caller.dataset.beforemod\n );\n\n ChooserDialogue.displayChooser(\n sectionModal,\n builtModuleData,\n partiallyAppliedFavouriteManager(data, caller.dataset.sectionid),\n footerData,\n );\n\n bodyPromiseResolver(await Templates.render(\n 'core_course/activitychooser',\n templateDataBuilder(builtModuleData, chooserConfig)\n ));\n }\n });\n });\n\n initialized = true;\n};\n\n/**\n * Given the web service data and an ID we want to make a deep copy\n * of the WS data then add on the section ID to the addoption URL\n *\n * @method sectionIdMapper\n * @param {Object} webServiceData Our original data from the Web service call\n * @param {Number} id The ID of the section we need to append to the links\n * @param {Number|null} sectionreturnid The ID of the section return we need to append to the links\n * @param {Number|null} beforemod The ID of the cm we need to append to the links\n * @return {Array} [modules] with URL's built\n */\nconst sectionIdMapper = (webServiceData, id, sectionreturnid, beforemod) => {\n // We need to take a fresh deep copy of the original data as an object is a reference type.\n const newData = JSON.parse(JSON.stringify(webServiceData));\n newData.content_items.forEach((module) => {\n module.link += '§ion=' + id + '&sr=' + (sectionreturnid ?? 0) + '&beforemod=' + (beforemod ?? 0);\n });\n return newData.content_items;\n};\n\n/**\n * Given an array of modules we want to figure out where & how to place them into our template object\n *\n * @method templateDataBuilder\n * @param {Array} data our modules to manipulate into a Templatable object\n * @param {Object} chooserConfig Any PHP config settings that we may need to reference\n * @return {Object} Our built object ready to render out\n */\nconst templateDataBuilder = (data, chooserConfig) => {\n // Setup of various bits and pieces we need to mutate before throwing it to the wolves.\n let activities = [];\n let resources = [];\n let showAll = true;\n let showActivities = false;\n let showResources = false;\n\n // Tab mode can be the following [All, Resources & Activities, All & Activities & Resources].\n const tabMode = parseInt(chooserConfig.tabmode);\n\n // Filter the incoming data to find favourite & recommended modules.\n const favourites = data.filter(mod => mod.favourite === true);\n const recommended = data.filter(mod => mod.recommended === true);\n\n // Whether the activities and resources tabs should be displayed or not.\n const showActivitiesAndResources = (tabMode) => {\n const acceptableModes = [\n ALLACTIVITIESRESOURCES,\n ALLACTIVITIESRESOURCESREC,\n ACTIVITIESRESOURCES,\n ACTIVITIESRESOURCESREC,\n ];\n\n return acceptableModes.indexOf(tabMode) !== -1;\n };\n\n // These modes need Activity & Resource tabs.\n if (showActivitiesAndResources(tabMode)) {\n // Filter the incoming data to find activities then resources.\n activities = data.filter(mod => mod.archetype === ACTIVITY);\n resources = data.filter(mod => mod.archetype === RESOURCE);\n showActivities = true;\n showResources = true;\n\n // We want all of the previous information but no 'All' tab.\n if (tabMode === ACTIVITIESRESOURCES || tabMode === ACTIVITIESRESOURCESREC) {\n showAll = false;\n }\n }\n\n const recommendedBeforeTabs = [\n ALLACTIVITIESRESOURCESREC,\n ONLYALLREC,\n ACTIVITIESRESOURCESREC,\n ];\n // Whether the recommended tab should be displayed before the All/Activities/Resources tabs.\n const recommendedBeginning = recommendedBeforeTabs.indexOf(tabMode) !== -1;\n\n // Given the results of the above filters lets figure out what tab to set active.\n // We have some favourites.\n const favouritesFirst = !!favourites.length;\n const recommendedFirst = favouritesFirst === false && recommendedBeginning === true && !!recommended.length;\n // We are in tabMode 2 without any favourites.\n const activitiesFirst = showAll === false && favouritesFirst === false && recommendedFirst === false;\n // We have nothing fallback to show all modules.\n const fallback = showAll === true && favouritesFirst === false && recommendedFirst === false;\n\n return {\n 'default': data,\n showAll: showAll,\n activities: activities,\n showActivities: showActivities,\n activitiesFirst: activitiesFirst,\n resources: resources,\n showResources: showResources,\n favourites: favourites,\n recommended: recommended,\n recommendedFirst: recommendedFirst,\n recommendedBeginning: recommendedBeginning,\n favouritesFirst: favouritesFirst,\n fallback: fallback,\n };\n};\n\n/**\n * Given an object we want to build a modal ready to show\n *\n * @method buildModal\n * @param {Promise} bodyPromise\n * @param {String|Boolean} footer Either a footer to add or nothing\n * @return {Object} The modal ready to display immediately and render body in later.\n */\nconst buildModal = (bodyPromise, footer) => {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: getString('addresourceoractivity'),\n body: bodyPromise,\n footer: footer.customfootertemplate,\n large: true,\n scrollable: false,\n templateContext: {\n classes: 'modchooser'\n }\n })\n .then(modal => {\n modal.show();\n return modal;\n });\n};\n\n/**\n * A small helper function to handle the case where there are no more favourites\n * and we need to mess a bit with the available tabs in the chooser\n *\n * @method nullFavouriteDomManager\n * @param {HTMLElement} favouriteTabNav Dom node of the favourite tab nav\n * @param {HTMLElement} modalBody Our current modals' body\n */\nconst nullFavouriteDomManager = (favouriteTabNav, modalBody) => {\n favouriteTabNav.tabIndex = -1;\n favouriteTabNav.classList.add('d-none');\n // Need to set active to an available tab.\n if (favouriteTabNav.classList.contains('active')) {\n favouriteTabNav.classList.remove('active');\n favouriteTabNav.setAttribute('aria-selected', 'false');\n const favouriteTab = modalBody.querySelector(selectors.regions.favouriteTab);\n favouriteTab.classList.remove('active');\n const defaultTabNav = modalBody.querySelector(selectors.regions.defaultTabNav);\n const activitiesTabNav = modalBody.querySelector(selectors.regions.activityTabNav);\n if (defaultTabNav.classList.contains('d-none') === false) {\n defaultTabNav.classList.add('active');\n defaultTabNav.setAttribute('aria-selected', 'true');\n defaultTabNav.tabIndex = 0;\n defaultTabNav.focus();\n const defaultTab = modalBody.querySelector(selectors.regions.defaultTab);\n defaultTab.classList.add('active');\n } else {\n activitiesTabNav.classList.add('active');\n activitiesTabNav.setAttribute('aria-selected', 'true');\n activitiesTabNav.tabIndex = 0;\n activitiesTabNav.focus();\n const activitiesTab = modalBody.querySelector(selectors.regions.activityTab);\n activitiesTab.classList.add('active');\n }\n\n }\n};\n\n/**\n * Export a curried function where the builtModules has been applied.\n * We have our array of modules so we can rerender the favourites area and have all of the items sorted.\n *\n * @method partiallyAppliedFavouriteManager\n * @param {Array} moduleData This is our raw WS data that we need to manipulate\n * @param {Number} sectionId We need this to add the sectionID to the URL's in the faves area after rerender\n * @return {Function} partially applied function so we can manipulate DOM nodes easily & update our internal array\n */\nconst partiallyAppliedFavouriteManager = (moduleData, sectionId) => {\n /**\n * Curried function that is being returned.\n *\n * @param {String} internal Internal name of the module to manage\n * @param {Boolean} favourite Is the caller adding a favourite or removing one?\n * @param {HTMLElement} modalBody What we need to update whilst we are here\n */\n return async(internal, favourite, modalBody) => {\n const favouriteArea = modalBody.querySelector(selectors.render.favourites);\n\n // eslint-disable-next-line max-len\n const favouriteButtons = modalBody.querySelectorAll(`[data-internal=\"${internal}\"] ${selectors.actions.optionActions.manageFavourite}`);\n const favouriteTabNav = modalBody.querySelector(selectors.regions.favouriteTabNav);\n const result = moduleData.content_items.find(({name}) => name === internal);\n const newFaves = {};\n if (result) {\n if (favourite) {\n result.favourite = true;\n\n // eslint-disable-next-line camelcase\n newFaves.content_items = moduleData.content_items.filter(mod => mod.favourite === true);\n\n const builtFaves = sectionIdMapper(newFaves, sectionId);\n\n const {html, js} = await Templates.renderForPromise('core_course/local/activitychooser/favourites',\n {favourites: builtFaves});\n\n await Templates.replaceNodeContents(favouriteArea, html, js);\n\n Array.from(favouriteButtons).forEach((element) => {\n element.classList.remove('text-muted');\n element.classList.add('text-primary');\n element.dataset.favourited = 'true';\n element.setAttribute('aria-pressed', true);\n element.firstElementChild.classList.remove('fa-star-o');\n element.firstElementChild.classList.add('fa-star');\n });\n\n favouriteTabNav.classList.remove('d-none');\n } else {\n result.favourite = false;\n\n const nodeToRemove = favouriteArea.querySelector(`[data-internal=\"${internal}\"]`);\n\n nodeToRemove.parentNode.removeChild(nodeToRemove);\n\n Array.from(favouriteButtons).forEach((element) => {\n element.classList.add('text-muted');\n element.classList.remove('text-primary');\n element.dataset.favourited = 'false';\n element.setAttribute('aria-pressed', false);\n element.firstElementChild.classList.remove('fa-star');\n element.firstElementChild.classList.add('fa-star-o');\n });\n const newFaves = moduleData.content_items.filter(mod => mod.favourite === true);\n\n if (newFaves.length === 0) {\n nullFavouriteDomManager(favouriteTabNav, modalBody);\n }\n }\n }\n };\n};\n"],"names":["initialized","courseId","chooserConfig","pendingPromise","Pending","registerListenerEvents","resolve","events","CustomEvents","activate","keyboardActivate","fetchModuleData","innerPromise","Promise","Repository","activityModules","fetchFooterData","footerInnerPromise","sectionId","define","document","forEach","event","addEventListener","async","e","target","closest","selectors","elements","sectionmodchooser","caller","sectionDiv","section","button","bodyPromiseResolver","hasAttribute","bodyPromise","footerData","dataset","sectionid","sectionModal","buildModal","data","catch","errorTemplateData","message","Templates","render","builtModuleData","sectionIdMapper","sectionreturnid","beforemod","ChooserDialogue","displayChooser","partiallyAppliedFavouriteManager","templateDataBuilder","webServiceData","id","newData","JSON","parse","stringify","content_items","module","link","activities","resources","showAll","showActivities","showResources","tabMode","parseInt","tabmode","favourites","filter","mod","favourite","recommended","indexOf","showActivitiesAndResources","archetype","recommendedBeginning","favouritesFirst","length","recommendedFirst","activitiesFirst","fallback","footer","ModalFactory","create","type","types","DEFAULT","title","body","customfootertemplate","large","scrollable","templateContext","classes","then","modal","show","moduleData","internal","modalBody","favouriteArea","querySelector","favouriteButtons","querySelectorAll","actions","optionActions","manageFavourite","favouriteTabNav","regions","result","find","_ref","name","newFaves","builtFaves","html","js","renderForPromise","replaceNodeContents","Array","from","element","classList","remove","add","favourited","setAttribute","firstElementChild","nodeToRemove","parentNode","removeChild","tabIndex","contains","favouriteTab","defaultTabNav","activitiesTabNav","activityTabNav","focus","defaultTab","activityTab","nullFavouriteDomManager"],"mappings":"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"} \ No newline at end of file diff --git a/course/amd/src/activitychooser.js b/course/amd/src/activitychooser.js index 243df32995a..cb4daba469d 100644 --- a/course/amd/src/activitychooser.js +++ b/course/amd/src/activitychooser.js @@ -27,7 +27,7 @@ import selectors from 'core_course/local/activitychooser/selectors'; import CustomEvents from 'core/custom_interaction_events'; import * as Templates from 'core/templates'; import * as ModalFactory from 'core/modal_factory'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Pending from 'core/pending'; // Set up some JS module wide constants that can be added to in the future. diff --git a/course/format/amd/build/local/content/actions.min.js b/course/format/amd/build/local/content/actions.min.js index 1c1cb1fed3a..24b4e4396d1 100644 --- a/course/format/amd/build/local/content/actions.min.js +++ b/course/format/amd/build/local/content/actions.min.js @@ -9,6 +9,6 @@ define("core_courseformat/local/content/actions",["exports","core/reactive","cor * @class core_courseformat/local/content/actions * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_templates=_interopRequireDefault(_templates),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_pending=_interopRequireDefault(_pending),_contenttree=_interopRequireDefault(_contenttree),_jquery=_interopRequireDefault(_jquery),(0,_prefetch.prefetchStrings)("core",["movecoursesection","movecoursemodule","confirm","delete"]);const directMutations={sectionHide:"sectionHide",sectionShow:"sectionShow",cmHide:"cmHide",cmShow:"cmShow",cmStealth:"cmStealth",cmMoveRight:"cmMoveRight",cmMoveLeft:"cmMoveLeft",cmNoGroups:"cmNoGroups",cmSeparateGroups:"cmSeparateGroups",cmVisibleGroups:"cmVisibleGroups"};class _default extends _reactive.BaseComponent{create(){this.name="content_actions",this.selectors={ACTIONLINK:"[data-action]",SECTIONLINK:"[data-for='section']",CMLINK:"[data-for='cm']",SECTIONNODE:"[data-for='sectionnode']",MODALTOGGLER:"[data-toggle='collapse']",ADDSECTION:"[data-action='addSection']",CONTENTTREE:"#destination-selector",ACTIONMENU:".action-menu",ACTIONMENUTOGGLER:'[data-toggle="dropdown"]',OPTIONSRADIO:"[type='radio']"},this.classes={DISABLED:"text-body",ITALIC:"font-italic"}}static addActions(actions){for(const[action,mutationReference]of Object.entries(actions)){if("function"!=typeof mutationReference&&"string"!=typeof mutationReference)throw new Error("".concat(action," action must be a mutation name or a function"));directMutations[action]=mutationReference}}stateReady(state){this.addEventListener(this.element,"click",this._dispatchClick),this._checkSectionlist({state:state}),this.addEventListener(this.element,CourseEvents.sectionRefreshed,(()=>this._checkSectionlist({state:state})))}getWatchers(){return[{watch:"course.sectionlist:updated",handler:this._checkSectionlist}]}_dispatchClick(event){const target=event.target.closest(this.selectors.ACTIONLINK);if(!target)return;if(target.classList.contains(this.classes.DISABLED))return void event.preventDefault();const actionName=target.dataset.action,methodName=this._actionMethodName(actionName);if(void 0===this[methodName])return void 0!==directMutations[actionName]?"function"==typeof directMutations[actionName]?void directMutations[actionName](target,event):void this._requestMutationAction(target,event,directMutations[actionName]):void 0;this[methodName](target,event)}_actionMethodName(name){const requestName=name.charAt(0).toUpperCase()+name.slice(1);return"_request".concat(requestName)}_checkSectionlist(_ref){let{state:state}=_ref;this._setAddSectionLocked(state.course.sectionlist.length>state.course.maxsections)}_getTargetIds(target){var _target$dataset,_target$dataset2;let ids=[];null!=target&&null!==(_target$dataset=target.dataset)&&void 0!==_target$dataset&&_target$dataset.id&&ids.push(target.dataset.id);const bulkType=null==target||null===(_target$dataset2=target.dataset)||void 0===_target$dataset2?void 0:_target$dataset2.bulk;if(!bulkType)return ids;const bulk=this.reactive.get("bulk");return bulk.enabled&&bulk.selectedType===bulkType&&(ids=[...ids,...bulk.selection]),ids}async _requestMoveSection(target,event){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;event.preventDefault();const pendingModalReady=new _pending.default("courseformat/actions:prepareMoveSectionModal"),editTools=this._getClosestActionMenuToogler(target),data=this.reactive.getExporter().course(this.reactive.state);let titleText=null,sectionInfo=null;1==sectionIds.length?(sectionInfo=this.reactive.get("section",sectionIds[0]),data.sectionid=sectionInfo.id,data.sectiontitle=sectionInfo.title,data.information=await this.reactive.getFormatString("sectionmove_info",data.sectiontitle),titleText=this.reactive.getFormatString("sectionmove_title")):(data.information=await this.reactive.getFormatString("sectionsmove_info",sectionIds.length),titleText=this.reactive.getFormatString("sectionsmove_title"));const modalParams={title:titleText,body:_templates.default.render("core_courseformat/local/content/movesection",data)},modal=await this._modalBodyRenderedPromise(modalParams),modalBody=(0,_normalise.getFirst)(modal.getBody());sectionIds.forEach((sectionId=>{const currentElement=modalBody.querySelector("".concat(this.selectors.SECTIONLINK,"[data-id='").concat(sectionId,"']"));this._disableLink(currentElement)})),new _contenttree.default(modalBody.querySelector(this.selectors.CONTENTTREE),{SECTION:this.selectors.SECTIONNODE,TOGGLER:this.selectors.MODALTOGGLER,COLLAPSE:this.selectors.MODALTOGGLER},!0),modalBody.addEventListener("click",(event=>{const target=event.target;target.matches("a")&&"section"==target.dataset.for&&void 0!==target.dataset.id&&(target.getAttribute("aria-disabled")||(event.preventDefault(),this.reactive.dispatch("sectionMoveAfter",sectionIds,target.dataset.id),this._destroyModal(modal,editTools)))})),pendingModalReady.resolve()}async _requestMoveCm(target,event){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;event.preventDefault();const pendingModalReady=new _pending.default("courseformat/actions:prepareMoveCmModal"),editTools=this._getClosestActionMenuToogler(target),exporter=this.reactive.getExporter(),data=exporter.course(this.reactive.state);let titleText=null;if(1==cmIds.length){const cmInfo=this.reactive.get("cm",cmIds[0]);data.cmid=cmInfo.id,data.cmname=cmInfo.name,data.information=await this.reactive.getFormatString("cmmove_info",data.cmname),titleText=this.reactive.getFormatString("cmmove_title")}else data.information=await this.reactive.getFormatString("cmsmove_info",cmIds.length),titleText=this.reactive.getFormatString("cmsmove_title");const modalParams={title:titleText,body:_templates.default.render("core_courseformat/local/content/movecm",data)},modal=await this._modalBodyRenderedPromise(modalParams),modalBody=(0,_normalise.getFirst)(modal.getBody());cmIds.forEach((cmId=>{const currentElement=modalBody.querySelector("".concat(this.selectors.CMLINK,"[data-id='").concat(cmId,"']"));this._disableLink(currentElement)})),new _contenttree.default(modalBody.querySelector(this.selectors.CONTENTTREE),{SECTION:this.selectors.SECTIONNODE,TOGGLER:this.selectors.MODALTOGGLER,COLLAPSE:this.selectors.MODALTOGGLER,ENTER:this.selectors.SECTIONLINK}),cmIds.forEach((cmId=>{var _toggler$data;const sectionnode=modalBody.querySelector("".concat(this.selectors.CMLINK,"[data-id='").concat(cmId,"']")).closest(this.selectors.SECTIONNODE),toggler=(0,_jquery.default)(sectionnode).find(this.selectors.MODALTOGGLER);let collapsibleId=null!==(_toggler$data=toggler.data("target"))&&void 0!==_toggler$data?_toggler$data:toggler.attr("href");if(collapsibleId){collapsibleId=collapsibleId.replace("#","");const expandNode=modalBody.querySelector("#".concat(collapsibleId));(0,_jquery.default)(expandNode).collapse("show")}})),modalBody.addEventListener("click",(event=>{const target=event.target;if(!target.matches("a")||void 0===target.dataset.for||void 0===target.dataset.id)return;if(target.getAttribute("aria-disabled"))return;let targetSectionId,targetCmId;if(event.preventDefault(),"cm"==target.dataset.for){const dropData=exporter.cmDraggableData(this.reactive.state,target.dataset.id);targetSectionId=dropData.sectionid,targetCmId=dropData.nextcmid}else{const section=this.reactive.get("section",target.dataset.id);targetSectionId=target.dataset.id,targetCmId=null==section?void 0:section.cmlist[0]}this.reactive.dispatch("cmMove",cmIds,targetSectionId,targetCmId),this._destroyModal(modal,editTools)})),pendingModalReady.resolve()}async _requestAddSection(target,event){var _target$dataset$id;event.preventDefault(),this.reactive.dispatch("addSection",null!==(_target$dataset$id=target.dataset.id)&&void 0!==_target$dataset$id?_target$dataset$id:0)}async _requestDeleteSection(target,event){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;if(event.preventDefault(),!sectionIds.some((sectionId=>{var _sectionInfo$cmlist;const sectionInfo=this.reactive.get("section",sectionId);return(null!==(_sectionInfo$cmlist=sectionInfo.cmlist)&&void 0!==_sectionInfo$cmlist?_sectionInfo$cmlist:[]).length||sectionInfo.hassummary||sectionInfo.rawtitle})))return void this.reactive.dispatch("sectionDelete",sectionIds);let bodyText=null,titleText=null;if(1==sectionIds.length){titleText=this.reactive.getFormatString("sectiondelete_title");const sectionInfo=this.reactive.get("section",sectionIds[0]);bodyText=this.reactive.getFormatString("sectiondelete_info",{name:sectionInfo.title})}else titleText=this.reactive.getFormatString("sectionsdelete_title"),bodyText=this.reactive.getFormatString("sectionsdelete_info",{count:sectionIds.length});const modalParams={title:titleText,body:bodyText,type:_modal_factory.default.types.DELETE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);modal.getRoot().on(_modal_events.default.delete,(e=>{e.preventDefault(),modal.destroy(),this.reactive.dispatch("sectionDelete",sectionIds)}))}async _requestToggleSelectionCm(target,event){(0,_bulkselection.toggleBulkSelectionAction)(this.reactive,target,event,"cm")}async _requestToggleSelectionSection(target,event){(0,_bulkselection.toggleBulkSelectionAction)(this.reactive,target,event,"section")}async _requestMutationAction(target,event,mutationName){target.dataset.id&&(event.preventDefault(),this.reactive.dispatch(mutationName,[target.dataset.id]))}async _requestCmDuplicate(target,event){var _target$dataset$secti;const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;const sectionId=null!==(_target$dataset$secti=target.dataset.sectionid)&&void 0!==_target$dataset$secti?_target$dataset$secti:null;event.preventDefault(),this.reactive.dispatch("cmDuplicate",cmIds,sectionId)}async _requestCmDelete(target,event){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;event.preventDefault();let bodyText=null,titleText=null;if(1==cmIds.length){const cmInfo=this.reactive.get("cm",cmIds[0]);titleText=(0,_str.get_string)("cmdelete_title","core_courseformat"),bodyText=(0,_str.get_string)("cmdelete_info","core_courseformat",{type:cmInfo.modname,name:cmInfo.name})}else titleText=(0,_str.get_string)("cmsdelete_title","core_courseformat"),bodyText=(0,_str.get_string)("cmsdelete_info","core_courseformat",{count:cmIds.length});const modalParams={title:titleText,body:bodyText,type:_modal_factory.default.types.DELETE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);modal.getRoot().on(_modal_events.default.delete,(e=>{e.preventDefault(),modal.destroy(),this.reactive.dispatch("cmDelete",cmIds)}))}async _requestCmAvailability(target){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;const data={allowstealth:this.reactive.getExporter().canUseStealth(this.reactive.state,cmIds)},modalParams={title:(0,_str.get_string)("availability","core"),body:_templates.default.render("core_courseformat/local/content/cm/availabilitymodal",data),saveButtonText:(0,_str.get_string)("apply","core"),type:_modal_factory.default.types.SAVE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);this._setupMutationRadioButtonModal(modal,cmIds)}async _requestSectionAvailability(target){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;const title=1==sectionIds.length?"sectionavailability_title":"sectionsavailability_title",modalParams={title:this.reactive.getFormatString(title),body:_templates.default.render("core_courseformat/local/content/section/availabilitymodal",[]),saveButtonText:(0,_str.get_string)("apply","core"),type:_modal_factory.default.types.SAVE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);this._setupMutationRadioButtonModal(modal,sectionIds)}_setupMutationRadioButtonModal(modal,ids){modal.setButtonDisabled("save",!0);const submitFunction=radio=>{const mutation=null==radio?void 0:radio.value;return!!mutation&&(this.reactive.dispatch(mutation,ids),!0)},modalBody=(0,_normalise.getFirst)(modal.getBody());modalBody.querySelectorAll(this.selectors.OPTIONSRADIO).forEach((radio=>{radio.addEventListener("change",(()=>{modal.setButtonDisabled("save",!1)})),radio.parentNode.addEventListener("click",(()=>{radio.checked=!0,modal.setButtonDisabled("save",!1)})),radio.parentNode.addEventListener("dblclick",(dbClickEvent=>{submitFunction(radio)&&(dbClickEvent.preventDefault(),modal.destroy())}))})),modal.getRoot().on(_modal_events.default.save,(()=>{const radio=modalBody.querySelector("".concat(this.selectors.OPTIONSRADIO,":checked"));submitFunction(radio)}))}_setAddSectionLocked(locked){this.getElements(this.selectors.ADDSECTION).forEach((element=>{element.classList.toggle(this.classes.DISABLED,locked),element.classList.toggle(this.classes.ITALIC,locked),this.setElementLocked(element,locked)}))}_disableLink(element){element&&(element.style.pointerEvents="none",element.style.userSelect="none",element.classList.add(this.classes.DISABLED),element.classList.add(this.classes.ITALIC),element.setAttribute("aria-disabled",!0),element.addEventListener("click",(event=>event.preventDefault())))}_modalBodyRenderedPromise(modalParams){return new Promise(((resolve,reject)=>{_modal_factory.default.create(modalParams).then((modal=>{modal.setRemoveOnClose(!0),modal.getRoot().on(_modal_events.default.bodyRendered,(()=>{resolve(modal)})),void 0!==modalParams.saveButtonText&&modal.setSaveButtonText(modalParams.saveButtonText),void 0!==modalParams.deleteButtonText&&modal.setDeleteButtonText(modalParams.saveButtonText),modal.show()})).catch((()=>{reject("Cannot load modal content")}))}))}_destroyModal(modal,element){modal.hide();const pendingDestroy=new _pending.default("courseformat/actions:destroyModal");element&&element.focus(),setTimeout((()=>{modal.destroy(),pendingDestroy.resolve()}),500)}_getClosestActionMenuToogler(element){const actionMenu=element.closest(this.selectors.ACTIONMENU);if(actionMenu)return actionMenu.querySelector(this.selectors.ACTIONMENUTOGGLER)}}return _exports.default=_default,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_templates=_interopRequireDefault(_templates),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_pending=_interopRequireDefault(_pending),_contenttree=_interopRequireDefault(_contenttree),_jquery=_interopRequireDefault(_jquery),(0,_prefetch.prefetchStrings)("core",["movecoursesection","movecoursemodule","confirm","delete"]);const directMutations={sectionHide:"sectionHide",sectionShow:"sectionShow",cmHide:"cmHide",cmShow:"cmShow",cmStealth:"cmStealth",cmMoveRight:"cmMoveRight",cmMoveLeft:"cmMoveLeft",cmNoGroups:"cmNoGroups",cmSeparateGroups:"cmSeparateGroups",cmVisibleGroups:"cmVisibleGroups"};class _default extends _reactive.BaseComponent{create(){this.name="content_actions",this.selectors={ACTIONLINK:"[data-action]",SECTIONLINK:"[data-for='section']",CMLINK:"[data-for='cm']",SECTIONNODE:"[data-for='sectionnode']",MODALTOGGLER:"[data-toggle='collapse']",ADDSECTION:"[data-action='addSection']",CONTENTTREE:"#destination-selector",ACTIONMENU:".action-menu",ACTIONMENUTOGGLER:'[data-toggle="dropdown"]',OPTIONSRADIO:"[type='radio']"},this.classes={DISABLED:"text-body",ITALIC:"font-italic"}}static addActions(actions){for(const[action,mutationReference]of Object.entries(actions)){if("function"!=typeof mutationReference&&"string"!=typeof mutationReference)throw new Error("".concat(action," action must be a mutation name or a function"));directMutations[action]=mutationReference}}stateReady(state){this.addEventListener(this.element,"click",this._dispatchClick),this._checkSectionlist({state:state}),this.addEventListener(this.element,CourseEvents.sectionRefreshed,(()=>this._checkSectionlist({state:state})))}getWatchers(){return[{watch:"course.sectionlist:updated",handler:this._checkSectionlist}]}_dispatchClick(event){const target=event.target.closest(this.selectors.ACTIONLINK);if(!target)return;if(target.classList.contains(this.classes.DISABLED))return void event.preventDefault();const actionName=target.dataset.action,methodName=this._actionMethodName(actionName);if(void 0===this[methodName])return void 0!==directMutations[actionName]?"function"==typeof directMutations[actionName]?void directMutations[actionName](target,event):void this._requestMutationAction(target,event,directMutations[actionName]):void 0;this[methodName](target,event)}_actionMethodName(name){const requestName=name.charAt(0).toUpperCase()+name.slice(1);return"_request".concat(requestName)}_checkSectionlist(_ref){let{state:state}=_ref;this._setAddSectionLocked(state.course.sectionlist.length>state.course.maxsections)}_getTargetIds(target){var _target$dataset,_target$dataset2;let ids=[];null!=target&&null!==(_target$dataset=target.dataset)&&void 0!==_target$dataset&&_target$dataset.id&&ids.push(target.dataset.id);const bulkType=null==target||null===(_target$dataset2=target.dataset)||void 0===_target$dataset2?void 0:_target$dataset2.bulk;if(!bulkType)return ids;const bulk=this.reactive.get("bulk");return bulk.enabled&&bulk.selectedType===bulkType&&(ids=[...ids,...bulk.selection]),ids}async _requestMoveSection(target,event){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;event.preventDefault();const pendingModalReady=new _pending.default("courseformat/actions:prepareMoveSectionModal"),editTools=this._getClosestActionMenuToogler(target),data=this.reactive.getExporter().course(this.reactive.state);let titleText=null,sectionInfo=null;1==sectionIds.length?(sectionInfo=this.reactive.get("section",sectionIds[0]),data.sectionid=sectionInfo.id,data.sectiontitle=sectionInfo.title,data.information=await this.reactive.getFormatString("sectionmove_info",data.sectiontitle),titleText=this.reactive.getFormatString("sectionmove_title")):(data.information=await this.reactive.getFormatString("sectionsmove_info",sectionIds.length),titleText=this.reactive.getFormatString("sectionsmove_title"));const modalParams={title:titleText,body:_templates.default.render("core_courseformat/local/content/movesection",data)},modal=await this._modalBodyRenderedPromise(modalParams),modalBody=(0,_normalise.getFirst)(modal.getBody());sectionIds.forEach((sectionId=>{const currentElement=modalBody.querySelector("".concat(this.selectors.SECTIONLINK,"[data-id='").concat(sectionId,"']"));this._disableLink(currentElement)})),new _contenttree.default(modalBody.querySelector(this.selectors.CONTENTTREE),{SECTION:this.selectors.SECTIONNODE,TOGGLER:this.selectors.MODALTOGGLER,COLLAPSE:this.selectors.MODALTOGGLER},!0),modalBody.addEventListener("click",(event=>{const target=event.target;target.matches("a")&&"section"==target.dataset.for&&void 0!==target.dataset.id&&(target.getAttribute("aria-disabled")||(event.preventDefault(),this.reactive.dispatch("sectionMoveAfter",sectionIds,target.dataset.id),this._destroyModal(modal,editTools)))})),pendingModalReady.resolve()}async _requestMoveCm(target,event){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;event.preventDefault();const pendingModalReady=new _pending.default("courseformat/actions:prepareMoveCmModal"),editTools=this._getClosestActionMenuToogler(target),exporter=this.reactive.getExporter(),data=exporter.course(this.reactive.state);let titleText=null;if(1==cmIds.length){const cmInfo=this.reactive.get("cm",cmIds[0]);data.cmid=cmInfo.id,data.cmname=cmInfo.name,data.information=await this.reactive.getFormatString("cmmove_info",data.cmname),titleText=this.reactive.getFormatString("cmmove_title")}else data.information=await this.reactive.getFormatString("cmsmove_info",cmIds.length),titleText=this.reactive.getFormatString("cmsmove_title");const modalParams={title:titleText,body:_templates.default.render("core_courseformat/local/content/movecm",data)},modal=await this._modalBodyRenderedPromise(modalParams),modalBody=(0,_normalise.getFirst)(modal.getBody());cmIds.forEach((cmId=>{const currentElement=modalBody.querySelector("".concat(this.selectors.CMLINK,"[data-id='").concat(cmId,"']"));this._disableLink(currentElement)})),new _contenttree.default(modalBody.querySelector(this.selectors.CONTENTTREE),{SECTION:this.selectors.SECTIONNODE,TOGGLER:this.selectors.MODALTOGGLER,COLLAPSE:this.selectors.MODALTOGGLER,ENTER:this.selectors.SECTIONLINK}),cmIds.forEach((cmId=>{var _toggler$data;const sectionnode=modalBody.querySelector("".concat(this.selectors.CMLINK,"[data-id='").concat(cmId,"']")).closest(this.selectors.SECTIONNODE),toggler=(0,_jquery.default)(sectionnode).find(this.selectors.MODALTOGGLER);let collapsibleId=null!==(_toggler$data=toggler.data("target"))&&void 0!==_toggler$data?_toggler$data:toggler.attr("href");if(collapsibleId){collapsibleId=collapsibleId.replace("#","");const expandNode=modalBody.querySelector("#".concat(collapsibleId));(0,_jquery.default)(expandNode).collapse("show")}})),modalBody.addEventListener("click",(event=>{const target=event.target;if(!target.matches("a")||void 0===target.dataset.for||void 0===target.dataset.id)return;if(target.getAttribute("aria-disabled"))return;let targetSectionId,targetCmId;if(event.preventDefault(),"cm"==target.dataset.for){const dropData=exporter.cmDraggableData(this.reactive.state,target.dataset.id);targetSectionId=dropData.sectionid,targetCmId=dropData.nextcmid}else{const section=this.reactive.get("section",target.dataset.id);targetSectionId=target.dataset.id,targetCmId=null==section?void 0:section.cmlist[0]}this.reactive.dispatch("cmMove",cmIds,targetSectionId,targetCmId),this._destroyModal(modal,editTools)})),pendingModalReady.resolve()}async _requestAddSection(target,event){var _target$dataset$id;event.preventDefault(),this.reactive.dispatch("addSection",null!==(_target$dataset$id=target.dataset.id)&&void 0!==_target$dataset$id?_target$dataset$id:0)}async _requestDeleteSection(target,event){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;if(event.preventDefault(),!sectionIds.some((sectionId=>{var _sectionInfo$cmlist;const sectionInfo=this.reactive.get("section",sectionId);return(null!==(_sectionInfo$cmlist=sectionInfo.cmlist)&&void 0!==_sectionInfo$cmlist?_sectionInfo$cmlist:[]).length||sectionInfo.hassummary||sectionInfo.rawtitle})))return void this.reactive.dispatch("sectionDelete",sectionIds);let bodyText=null,titleText=null;if(1==sectionIds.length){titleText=this.reactive.getFormatString("sectiondelete_title");const sectionInfo=this.reactive.get("section",sectionIds[0]);bodyText=this.reactive.getFormatString("sectiondelete_info",{name:sectionInfo.title})}else titleText=this.reactive.getFormatString("sectionsdelete_title"),bodyText=this.reactive.getFormatString("sectionsdelete_info",{count:sectionIds.length});const modalParams={title:titleText,body:bodyText,type:_modal_factory.default.types.DELETE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);modal.getRoot().on(_modal_events.default.delete,(e=>{e.preventDefault(),modal.destroy(),this.reactive.dispatch("sectionDelete",sectionIds)}))}async _requestToggleSelectionCm(target,event){(0,_bulkselection.toggleBulkSelectionAction)(this.reactive,target,event,"cm")}async _requestToggleSelectionSection(target,event){(0,_bulkselection.toggleBulkSelectionAction)(this.reactive,target,event,"section")}async _requestMutationAction(target,event,mutationName){target.dataset.id&&(event.preventDefault(),this.reactive.dispatch(mutationName,[target.dataset.id]))}async _requestCmDuplicate(target,event){var _target$dataset$secti;const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;const sectionId=null!==(_target$dataset$secti=target.dataset.sectionid)&&void 0!==_target$dataset$secti?_target$dataset$secti:null;event.preventDefault(),this.reactive.dispatch("cmDuplicate",cmIds,sectionId)}async _requestCmDelete(target,event){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;event.preventDefault();let bodyText=null,titleText=null;if(1==cmIds.length){const cmInfo=this.reactive.get("cm",cmIds[0]);titleText=(0,_str.getString)("cmdelete_title","core_courseformat"),bodyText=(0,_str.getString)("cmdelete_info","core_courseformat",{type:cmInfo.modname,name:cmInfo.name})}else titleText=(0,_str.getString)("cmsdelete_title","core_courseformat"),bodyText=(0,_str.getString)("cmsdelete_info","core_courseformat",{count:cmIds.length});const modalParams={title:titleText,body:bodyText,type:_modal_factory.default.types.DELETE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);modal.getRoot().on(_modal_events.default.delete,(e=>{e.preventDefault(),modal.destroy(),this.reactive.dispatch("cmDelete",cmIds)}))}async _requestCmAvailability(target){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;const data={allowstealth:this.reactive.getExporter().canUseStealth(this.reactive.state,cmIds)},modalParams={title:(0,_str.getString)("availability","core"),body:_templates.default.render("core_courseformat/local/content/cm/availabilitymodal",data),saveButtonText:(0,_str.getString)("apply","core"),type:_modal_factory.default.types.SAVE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);this._setupMutationRadioButtonModal(modal,cmIds)}async _requestSectionAvailability(target){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;const title=1==sectionIds.length?"sectionavailability_title":"sectionsavailability_title",modalParams={title:this.reactive.getFormatString(title),body:_templates.default.render("core_courseformat/local/content/section/availabilitymodal",[]),saveButtonText:(0,_str.getString)("apply","core"),type:_modal_factory.default.types.SAVE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);this._setupMutationRadioButtonModal(modal,sectionIds)}_setupMutationRadioButtonModal(modal,ids){modal.setButtonDisabled("save",!0);const submitFunction=radio=>{const mutation=null==radio?void 0:radio.value;return!!mutation&&(this.reactive.dispatch(mutation,ids),!0)},modalBody=(0,_normalise.getFirst)(modal.getBody());modalBody.querySelectorAll(this.selectors.OPTIONSRADIO).forEach((radio=>{radio.addEventListener("change",(()=>{modal.setButtonDisabled("save",!1)})),radio.parentNode.addEventListener("click",(()=>{radio.checked=!0,modal.setButtonDisabled("save",!1)})),radio.parentNode.addEventListener("dblclick",(dbClickEvent=>{submitFunction(radio)&&(dbClickEvent.preventDefault(),modal.destroy())}))})),modal.getRoot().on(_modal_events.default.save,(()=>{const radio=modalBody.querySelector("".concat(this.selectors.OPTIONSRADIO,":checked"));submitFunction(radio)}))}_setAddSectionLocked(locked){this.getElements(this.selectors.ADDSECTION).forEach((element=>{element.classList.toggle(this.classes.DISABLED,locked),element.classList.toggle(this.classes.ITALIC,locked),this.setElementLocked(element,locked)}))}_disableLink(element){element&&(element.style.pointerEvents="none",element.style.userSelect="none",element.classList.add(this.classes.DISABLED),element.classList.add(this.classes.ITALIC),element.setAttribute("aria-disabled",!0),element.addEventListener("click",(event=>event.preventDefault())))}_modalBodyRenderedPromise(modalParams){return new Promise(((resolve,reject)=>{_modal_factory.default.create(modalParams).then((modal=>{modal.setRemoveOnClose(!0),modal.getRoot().on(_modal_events.default.bodyRendered,(()=>{resolve(modal)})),void 0!==modalParams.saveButtonText&&modal.setSaveButtonText(modalParams.saveButtonText),void 0!==modalParams.deleteButtonText&&modal.setDeleteButtonText(modalParams.saveButtonText),modal.show()})).catch((()=>{reject("Cannot load modal content")}))}))}_destroyModal(modal,element){modal.hide();const pendingDestroy=new _pending.default("courseformat/actions:destroyModal");element&&element.focus(),setTimeout((()=>{modal.destroy(),pendingDestroy.resolve()}),500)}_getClosestActionMenuToogler(element){const actionMenu=element.closest(this.selectors.ACTIONMENU);if(actionMenu)return actionMenu.querySelector(this.selectors.ACTIONMENUTOGGLER)}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=actions.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/content/actions.min.js.map b/course/format/amd/build/local/content/actions.min.js.map index d943e45e56c..016ef4e721c 100644 --- a/course/format/amd/build/local/content/actions.min.js.map +++ b/course/format/amd/build/local/content/actions.min.js.map @@ -1 +1 @@ -{"version":3,"file":"actions.min.js","sources":["../../../src/local/content/actions.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 * Course state actions dispatcher.\n *\n * This module captures all data-dispatch links in the course content and dispatch the proper\n * state mutation, including any confirmation and modal required.\n *\n * @module core_courseformat/local/content/actions\n * @class core_courseformat/local/content/actions\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Templates from 'core/templates';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {getFirst} from 'core/normalise';\nimport {toggleBulkSelectionAction} from 'core_courseformat/local/content/actions/bulkselection';\nimport * as CourseEvents from 'core_course/events';\nimport Pending from 'core/pending';\nimport ContentTree from 'core_courseformat/local/courseeditor/contenttree';\n// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated.\nimport jQuery from 'jquery';\n\n// Load global strings.\nprefetchStrings('core', ['movecoursesection', 'movecoursemodule', 'confirm', 'delete']);\n\n// Mutations are dispatched by the course content actions.\n// Formats can use this module addActions static method to add custom actions.\n// Direct mutations can be simple strings (mutation) name or functions.\nconst directMutations = {\n sectionHide: 'sectionHide',\n sectionShow: 'sectionShow',\n cmHide: 'cmHide',\n cmShow: 'cmShow',\n cmStealth: 'cmStealth',\n cmMoveRight: 'cmMoveRight',\n cmMoveLeft: 'cmMoveLeft',\n cmNoGroups: 'cmNoGroups',\n cmSeparateGroups: 'cmSeparateGroups',\n cmVisibleGroups: 'cmVisibleGroups',\n};\n\nexport default class extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'content_actions';\n // Default query selectors.\n this.selectors = {\n ACTIONLINK: `[data-action]`,\n // Move modal selectors.\n SECTIONLINK: `[data-for='section']`,\n CMLINK: `[data-for='cm']`,\n SECTIONNODE: `[data-for='sectionnode']`,\n MODALTOGGLER: `[data-toggle='collapse']`,\n ADDSECTION: `[data-action='addSection']`,\n CONTENTTREE: `#destination-selector`,\n ACTIONMENU: `.action-menu`,\n ACTIONMENUTOGGLER: `[data-toggle=\"dropdown\"]`,\n // Availability modal selectors.\n OPTIONSRADIO: `[type='radio']`,\n };\n // Component css classes.\n this.classes = {\n DISABLED: `text-body`,\n ITALIC: `font-italic`,\n };\n }\n\n /**\n * Add extra actions to the module.\n *\n * @param {array} actions array of methods to execute\n */\n static addActions(actions) {\n for (const [action, mutationReference] of Object.entries(actions)) {\n if (typeof mutationReference !== 'function' && typeof mutationReference !== 'string') {\n throw new Error(`${action} action must be a mutation name or a function`);\n }\n directMutations[action] = mutationReference;\n }\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the state data.\n *\n */\n stateReady(state) {\n // Delegate dispatch clicks.\n this.addEventListener(\n this.element,\n 'click',\n this._dispatchClick\n );\n // Check section limit.\n this._checkSectionlist({state});\n // Add an Event listener to recalculate limits it if a section HTML is altered.\n this.addEventListener(\n this.element,\n CourseEvents.sectionRefreshed,\n () => this._checkSectionlist({state})\n );\n }\n\n /**\n * Return the component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n // Check section limit.\n {watch: `course.sectionlist:updated`, handler: this._checkSectionlist},\n ];\n }\n\n _dispatchClick(event) {\n const target = event.target.closest(this.selectors.ACTIONLINK);\n if (!target) {\n return;\n }\n if (target.classList.contains(this.classes.DISABLED)) {\n event.preventDefault();\n return;\n }\n\n // Invoke proper method.\n const actionName = target.dataset.action;\n const methodName = this._actionMethodName(actionName);\n\n if (this[methodName] !== undefined) {\n this[methodName](target, event);\n return;\n }\n\n // Check direct mutations or mutations handlers.\n if (directMutations[actionName] !== undefined) {\n if (typeof directMutations[actionName] === 'function') {\n directMutations[actionName](target, event);\n return;\n }\n this._requestMutationAction(target, event, directMutations[actionName]);\n return;\n }\n }\n\n _actionMethodName(name) {\n const requestName = name.charAt(0).toUpperCase() + name.slice(1);\n return `_request${requestName}`;\n }\n\n /**\n * Check the section list and disable some options if needed.\n *\n * @param {Object} detail the update details.\n * @param {Object} detail.state the state object.\n */\n _checkSectionlist({state}) {\n // Disable \"add section\" actions if the course max sections has been exceeded.\n this._setAddSectionLocked(state.course.sectionlist.length > state.course.maxsections);\n }\n\n /**\n * Return the ids represented by this element.\n *\n * Depending on the dataset attributes the action could represent a single id\n * or a bulk actions with all the current selected ids.\n *\n * @param {HTMLElement} target\n * @returns {Number[]} array of Ids\n */\n _getTargetIds(target) {\n let ids = [];\n if (target?.dataset?.id) {\n ids.push(target.dataset.id);\n }\n const bulkType = target?.dataset?.bulk;\n if (!bulkType) {\n return ids;\n }\n const bulk = this.reactive.get('bulk');\n if (bulk.enabled && bulk.selectedType === bulkType) {\n ids = [...ids, ...bulk.selection];\n }\n return ids;\n }\n\n /**\n * Handle a move section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestMoveSection(target, event) {\n // Check we have an id.\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n const pendingModalReady = new Pending(`courseformat/actions:prepareMoveSectionModal`);\n\n // The section edit menu to refocus on end.\n const editTools = this._getClosestActionMenuToogler(target);\n\n // Collect section information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.course(this.reactive.state);\n let titleText = null;\n\n // Add the target section id and title.\n let sectionInfo = null;\n if (sectionIds.length == 1) {\n sectionInfo = this.reactive.get('section', sectionIds[0]);\n data.sectionid = sectionInfo.id;\n data.sectiontitle = sectionInfo.title;\n data.information = await this.reactive.getFormatString('sectionmove_info', data.sectiontitle);\n titleText = this.reactive.getFormatString('sectionmove_title');\n } else {\n data.information = await this.reactive.getFormatString('sectionsmove_info', sectionIds.length);\n titleText = this.reactive.getFormatString('sectionsmove_title');\n }\n\n\n // Build the modal parameters from the event data.\n const modalParams = {\n title: titleText,\n body: Templates.render('core_courseformat/local/content/movesection', data),\n };\n\n // Create the modal.\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n const modalBody = getFirst(modal.getBody());\n\n // Disable current selected section ids.\n sectionIds.forEach(sectionId => {\n const currentElement = modalBody.querySelector(`${this.selectors.SECTIONLINK}[data-id='${sectionId}']`);\n this._disableLink(currentElement);\n });\n\n // Setup keyboard navigation.\n new ContentTree(\n modalBody.querySelector(this.selectors.CONTENTTREE),\n {\n SECTION: this.selectors.SECTIONNODE,\n TOGGLER: this.selectors.MODALTOGGLER,\n COLLAPSE: this.selectors.MODALTOGGLER,\n },\n true\n );\n\n // Capture click.\n modalBody.addEventListener('click', (event) => {\n const target = event.target;\n if (!target.matches('a') || target.dataset.for != 'section' || target.dataset.id === undefined) {\n return;\n }\n if (target.getAttribute('aria-disabled')) {\n return;\n }\n event.preventDefault();\n this.reactive.dispatch('sectionMoveAfter', sectionIds, target.dataset.id);\n this._destroyModal(modal, editTools);\n });\n\n pendingModalReady.resolve();\n }\n\n /**\n * Handle a move cm request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestMoveCm(target, event) {\n // Check we have an id.\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n const pendingModalReady = new Pending(`courseformat/actions:prepareMoveCmModal`);\n\n // The section edit menu to refocus on end.\n const editTools = this._getClosestActionMenuToogler(target);\n\n // Collect information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.course(this.reactive.state);\n\n let titleText = null;\n if (cmIds.length == 1) {\n const cmInfo = this.reactive.get('cm', cmIds[0]);\n data.cmid = cmInfo.id;\n data.cmname = cmInfo.name;\n data.information = await this.reactive.getFormatString('cmmove_info', data.cmname);\n titleText = this.reactive.getFormatString('cmmove_title');\n } else {\n data.information = await this.reactive.getFormatString('cmsmove_info', cmIds.length);\n titleText = this.reactive.getFormatString('cmsmove_title');\n }\n\n // Build the modal parameters from the event data.\n const modalParams = {\n title: titleText,\n body: Templates.render('core_courseformat/local/content/movecm', data),\n };\n\n // Create the modal.\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n const modalBody = getFirst(modal.getBody());\n\n // Disable current selected section ids.\n cmIds.forEach(cmId => {\n const currentElement = modalBody.querySelector(`${this.selectors.CMLINK}[data-id='${cmId}']`);\n this._disableLink(currentElement);\n });\n\n // Setup keyboard navigation.\n new ContentTree(\n modalBody.querySelector(this.selectors.CONTENTTREE),\n {\n SECTION: this.selectors.SECTIONNODE,\n TOGGLER: this.selectors.MODALTOGGLER,\n COLLAPSE: this.selectors.MODALTOGGLER,\n ENTER: this.selectors.SECTIONLINK,\n }\n );\n\n // Open the cm section node if possible (Bootstrap 4 uses jQuery to interact with collapsibles).\n // All jQuery in this code can be replaced when MDL-71979 is integrated.\n cmIds.forEach(cmId => {\n const currentElement = modalBody.querySelector(`${this.selectors.CMLINK}[data-id='${cmId}']`);\n const sectionnode = currentElement.closest(this.selectors.SECTIONNODE);\n const toggler = jQuery(sectionnode).find(this.selectors.MODALTOGGLER);\n let collapsibleId = toggler.data('target') ?? toggler.attr('href');\n if (collapsibleId) {\n // We cannot be sure we have # in the id element name.\n collapsibleId = collapsibleId.replace('#', '');\n const expandNode = modalBody.querySelector(`#${collapsibleId}`);\n jQuery(expandNode).collapse('show');\n }\n });\n\n modalBody.addEventListener('click', (event) => {\n const target = event.target;\n if (!target.matches('a') || target.dataset.for === undefined || target.dataset.id === undefined) {\n return;\n }\n if (target.getAttribute('aria-disabled')) {\n return;\n }\n event.preventDefault();\n\n let targetSectionId;\n let targetCmId;\n if (target.dataset.for == 'cm') {\n const dropData = exporter.cmDraggableData(this.reactive.state, target.dataset.id);\n targetSectionId = dropData.sectionid;\n targetCmId = dropData.nextcmid;\n } else {\n const section = this.reactive.get('section', target.dataset.id);\n targetSectionId = target.dataset.id;\n targetCmId = section?.cmlist[0];\n }\n this.reactive.dispatch('cmMove', cmIds, targetSectionId, targetCmId);\n this._destroyModal(modal, editTools);\n });\n\n pendingModalReady.resolve();\n }\n\n /**\n * Handle a create section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestAddSection(target, event) {\n event.preventDefault();\n this.reactive.dispatch('addSection', target.dataset.id ?? 0);\n }\n\n /**\n * Handle a delete section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestDeleteSection(target, event) {\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n // We don't need confirmation to delete empty sections.\n let needsConfirmation = sectionIds.some(sectionId => {\n const sectionInfo = this.reactive.get('section', sectionId);\n const cmList = sectionInfo.cmlist ?? [];\n return (cmList.length || sectionInfo.hassummary || sectionInfo.rawtitle);\n });\n if (!needsConfirmation) {\n this.reactive.dispatch('sectionDelete', sectionIds);\n return;\n }\n\n let bodyText = null;\n let titleText = null;\n if (sectionIds.length == 1) {\n titleText = this.reactive.getFormatString('sectiondelete_title');\n const sectionInfo = this.reactive.get('section', sectionIds[0]);\n bodyText = this.reactive.getFormatString('sectiondelete_info', {name: sectionInfo.title});\n } else {\n titleText = this.reactive.getFormatString('sectionsdelete_title');\n bodyText = this.reactive.getFormatString('sectionsdelete_info', {count: sectionIds.length});\n }\n\n const modalParams = {\n title: titleText,\n body: bodyText,\n type: ModalFactory.types.DELETE_CANCEL,\n };\n\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n modal.getRoot().on(\n ModalEvents.delete,\n e => {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n modal.destroy();\n this.reactive.dispatch('sectionDelete', sectionIds);\n }\n );\n }\n\n /**\n * Handle a toggle cm selection.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestToggleSelectionCm(target, event) {\n toggleBulkSelectionAction(this.reactive, target, event, 'cm');\n }\n\n /**\n * Handle a toggle section selection.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestToggleSelectionSection(target, event) {\n toggleBulkSelectionAction(this.reactive, target, event, 'section');\n }\n\n /**\n * Basic mutation action helper.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n * @param {string} mutationName the mutation name\n */\n async _requestMutationAction(target, event, mutationName) {\n if (!target.dataset.id) {\n return;\n }\n event.preventDefault();\n this.reactive.dispatch(mutationName, [target.dataset.id]);\n }\n\n /**\n * Handle a course module duplicate request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestCmDuplicate(target, event) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n const sectionId = target.dataset.sectionid ?? null;\n event.preventDefault();\n this.reactive.dispatch('cmDuplicate', cmIds, sectionId);\n }\n\n /**\n * Handle a delete cm request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestCmDelete(target, event) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n let bodyText = null;\n let titleText = null;\n if (cmIds.length == 1) {\n const cmInfo = this.reactive.get('cm', cmIds[0]);\n titleText = getString('cmdelete_title', 'core_courseformat');\n bodyText = getString(\n 'cmdelete_info',\n 'core_courseformat',\n {\n type: cmInfo.modname,\n name: cmInfo.name,\n }\n );\n } else {\n titleText = getString('cmsdelete_title', 'core_courseformat');\n bodyText = getString(\n 'cmsdelete_info',\n 'core_courseformat',\n {count: cmIds.length}\n );\n }\n\n const modalParams = {\n title: titleText,\n body: bodyText,\n type: ModalFactory.types.DELETE_CANCEL,\n };\n\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n modal.getRoot().on(\n ModalEvents.delete,\n e => {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n modal.destroy();\n this.reactive.dispatch('cmDelete', cmIds);\n }\n );\n }\n\n /**\n * Handle a cm availability change request.\n *\n * @param {Element} target the dispatch action element\n */\n async _requestCmAvailability(target) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n // Show the availability modal to decide which action to trigger.\n const exporter = this.reactive.getExporter();\n const data = {\n allowstealth: exporter.canUseStealth(this.reactive.state, cmIds),\n };\n const modalParams = {\n title: getString('availability', 'core'),\n body: Templates.render('core_courseformat/local/content/cm/availabilitymodal', data),\n saveButtonText: getString('apply', 'core'),\n type: ModalFactory.types.SAVE_CANCEL,\n };\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n this._setupMutationRadioButtonModal(modal, cmIds);\n }\n\n /**\n * Handle a section availability change request.\n *\n * @param {Element} target the dispatch action element\n */\n async _requestSectionAvailability(target) {\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n const title = (sectionIds.length == 1) ? 'sectionavailability_title' : 'sectionsavailability_title';\n // Show the availability modal to decide which action to trigger.\n const modalParams = {\n title: this.reactive.getFormatString(title),\n body: Templates.render('core_courseformat/local/content/section/availabilitymodal', []),\n saveButtonText: getString('apply', 'core'),\n type: ModalFactory.types.SAVE_CANCEL,\n };\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n this._setupMutationRadioButtonModal(modal, sectionIds);\n }\n\n /**\n * Add events to a mutation selector radio buttons modal.\n * @param {Modal} modal\n * @param {Number[]} ids the section or cm ids to apply the mutation\n */\n _setupMutationRadioButtonModal(modal, ids) {\n // The save button is not enabled until the user selects an option.\n modal.setButtonDisabled('save', true);\n\n const submitFunction = (radio) => {\n const mutation = radio?.value;\n if (!mutation) {\n return false;\n }\n this.reactive.dispatch(mutation, ids);\n return true;\n };\n\n const modalBody = getFirst(modal.getBody());\n const radioOptions = modalBody.querySelectorAll(this.selectors.OPTIONSRADIO);\n radioOptions.forEach(radio => {\n radio.addEventListener('change', () => {\n modal.setButtonDisabled('save', false);\n });\n radio.parentNode.addEventListener('click', () => {\n radio.checked = true;\n modal.setButtonDisabled('save', false);\n });\n radio.parentNode.addEventListener('dblclick', dbClickEvent => {\n if (submitFunction(radio)) {\n dbClickEvent.preventDefault();\n modal.destroy();\n }\n });\n });\n\n modal.getRoot().on(\n ModalEvents.save,\n () => {\n const radio = modalBody.querySelector(`${this.selectors.OPTIONSRADIO}:checked`);\n submitFunction(radio);\n }\n );\n }\n\n /**\n * Disable all add sections actions.\n *\n * @param {boolean} locked the new locked value.\n */\n _setAddSectionLocked(locked) {\n const targets = this.getElements(this.selectors.ADDSECTION);\n targets.forEach(element => {\n element.classList.toggle(this.classes.DISABLED, locked);\n element.classList.toggle(this.classes.ITALIC, locked);\n this.setElementLocked(element, locked);\n });\n }\n\n /**\n * Replace an element with a copy with a different tag name.\n *\n * @param {Element} element the original element\n */\n _disableLink(element) {\n if (element) {\n element.style.pointerEvents = 'none';\n element.style.userSelect = 'none';\n element.classList.add(this.classes.DISABLED);\n element.classList.add(this.classes.ITALIC);\n element.setAttribute('aria-disabled', true);\n element.addEventListener('click', event => event.preventDefault());\n }\n }\n\n /**\n * Render a modal and return a body ready promise.\n *\n * @param {object} modalParams the modal params\n * @return {Promise} the modal body ready promise\n */\n _modalBodyRenderedPromise(modalParams) {\n return new Promise((resolve, reject) => {\n ModalFactory.create(modalParams).then((modal) => {\n modal.setRemoveOnClose(true);\n // Handle body loading event.\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n resolve(modal);\n });\n // Configure some extra modal params.\n if (modalParams.saveButtonText !== undefined) {\n modal.setSaveButtonText(modalParams.saveButtonText);\n }\n if (modalParams.deleteButtonText !== undefined) {\n modal.setDeleteButtonText(modalParams.saveButtonText);\n }\n modal.show();\n return;\n }).catch(() => {\n reject(`Cannot load modal content`);\n });\n });\n }\n\n /**\n * Hide and later destroy a modal.\n *\n * Behat will fail if we remove the modal while some boostrap collapse is executing.\n *\n * @param {Modal} modal\n * @param {HTMLElement} element the dom element to focus on.\n */\n _destroyModal(modal, element) {\n modal.hide();\n const pendingDestroy = new Pending(`courseformat/actions:destroyModal`);\n if (element) {\n element.focus();\n }\n setTimeout(() =>{\n modal.destroy();\n pendingDestroy.resolve();\n }, 500);\n }\n\n /**\n * Get the closest actions menu toggler to an action element.\n *\n * @param {HTMLElement} element the action link element\n * @returns {HTMLElement|undefined}\n */\n _getClosestActionMenuToogler(element) {\n const actionMenu = element.closest(this.selectors.ACTIONMENU);\n if (!actionMenu) {\n return undefined;\n }\n return actionMenu.querySelector(this.selectors.ACTIONMENUTOGGLER);\n }\n}\n"],"names":["directMutations","sectionHide","sectionShow","cmHide","cmShow","cmStealth","cmMoveRight","cmMoveLeft","cmNoGroups","cmSeparateGroups","cmVisibleGroups","BaseComponent","create","name","selectors","ACTIONLINK","SECTIONLINK","CMLINK","SECTIONNODE","MODALTOGGLER","ADDSECTION","CONTENTTREE","ACTIONMENU","ACTIONMENUTOGGLER","OPTIONSRADIO","classes","DISABLED","ITALIC","actions","action","mutationReference","Object","entries","Error","stateReady","state","addEventListener","this","element","_dispatchClick","_checkSectionlist","CourseEvents","sectionRefreshed","getWatchers","watch","handler","event","target","closest","classList","contains","preventDefault","actionName","dataset","methodName","_actionMethodName","undefined","_requestMutationAction","requestName","charAt","toUpperCase","slice","_setAddSectionLocked","course","sectionlist","length","maxsections","_getTargetIds","ids","_target$dataset","id","push","bulkType","_target$dataset2","bulk","reactive","get","enabled","selectedType","selection","sectionIds","pendingModalReady","Pending","editTools","_getClosestActionMenuToogler","data","getExporter","titleText","sectionInfo","sectionid","sectiontitle","title","information","getFormatString","modalParams","body","Templates","render","modal","_modalBodyRenderedPromise","modalBody","getBody","forEach","sectionId","currentElement","querySelector","_disableLink","ContentTree","SECTION","TOGGLER","COLLAPSE","matches","for","getAttribute","dispatch","_destroyModal","resolve","cmIds","exporter","cmInfo","cmid","cmname","cmId","ENTER","sectionnode","toggler","find","collapsibleId","attr","replace","expandNode","collapse","targetSectionId","targetCmId","dropData","cmDraggableData","nextcmid","section","cmlist","some","hassummary","rawtitle","bodyText","count","type","ModalFactory","types","DELETE_CANCEL","getRoot","on","ModalEvents","delete","e","destroy","mutationName","modname","allowstealth","canUseStealth","saveButtonText","SAVE_CANCEL","_setupMutationRadioButtonModal","setButtonDisabled","submitFunction","radio","mutation","value","querySelectorAll","parentNode","checked","dbClickEvent","save","locked","getElements","toggle","setElementLocked","style","pointerEvents","userSelect","add","setAttribute","Promise","reject","then","setRemoveOnClose","bodyRendered","setSaveButtonText","deleteButtonText","setDeleteButtonText","show","catch","hide","pendingDestroy","focus","setTimeout","actionMenu"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"actions.min.js","sources":["../../../src/local/content/actions.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 * Course state actions dispatcher.\n *\n * This module captures all data-dispatch links in the course content and dispatch the proper\n * state mutation, including any confirmation and modal required.\n *\n * @module core_courseformat/local/content/actions\n * @class core_courseformat/local/content/actions\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Templates from 'core/templates';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport {getFirst} from 'core/normalise';\nimport {toggleBulkSelectionAction} from 'core_courseformat/local/content/actions/bulkselection';\nimport * as CourseEvents from 'core_course/events';\nimport Pending from 'core/pending';\nimport ContentTree from 'core_courseformat/local/courseeditor/contenttree';\n// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated.\nimport jQuery from 'jquery';\n\n// Load global strings.\nprefetchStrings('core', ['movecoursesection', 'movecoursemodule', 'confirm', 'delete']);\n\n// Mutations are dispatched by the course content actions.\n// Formats can use this module addActions static method to add custom actions.\n// Direct mutations can be simple strings (mutation) name or functions.\nconst directMutations = {\n sectionHide: 'sectionHide',\n sectionShow: 'sectionShow',\n cmHide: 'cmHide',\n cmShow: 'cmShow',\n cmStealth: 'cmStealth',\n cmMoveRight: 'cmMoveRight',\n cmMoveLeft: 'cmMoveLeft',\n cmNoGroups: 'cmNoGroups',\n cmSeparateGroups: 'cmSeparateGroups',\n cmVisibleGroups: 'cmVisibleGroups',\n};\n\nexport default class extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'content_actions';\n // Default query selectors.\n this.selectors = {\n ACTIONLINK: `[data-action]`,\n // Move modal selectors.\n SECTIONLINK: `[data-for='section']`,\n CMLINK: `[data-for='cm']`,\n SECTIONNODE: `[data-for='sectionnode']`,\n MODALTOGGLER: `[data-toggle='collapse']`,\n ADDSECTION: `[data-action='addSection']`,\n CONTENTTREE: `#destination-selector`,\n ACTIONMENU: `.action-menu`,\n ACTIONMENUTOGGLER: `[data-toggle=\"dropdown\"]`,\n // Availability modal selectors.\n OPTIONSRADIO: `[type='radio']`,\n };\n // Component css classes.\n this.classes = {\n DISABLED: `text-body`,\n ITALIC: `font-italic`,\n };\n }\n\n /**\n * Add extra actions to the module.\n *\n * @param {array} actions array of methods to execute\n */\n static addActions(actions) {\n for (const [action, mutationReference] of Object.entries(actions)) {\n if (typeof mutationReference !== 'function' && typeof mutationReference !== 'string') {\n throw new Error(`${action} action must be a mutation name or a function`);\n }\n directMutations[action] = mutationReference;\n }\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the state data.\n *\n */\n stateReady(state) {\n // Delegate dispatch clicks.\n this.addEventListener(\n this.element,\n 'click',\n this._dispatchClick\n );\n // Check section limit.\n this._checkSectionlist({state});\n // Add an Event listener to recalculate limits it if a section HTML is altered.\n this.addEventListener(\n this.element,\n CourseEvents.sectionRefreshed,\n () => this._checkSectionlist({state})\n );\n }\n\n /**\n * Return the component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n // Check section limit.\n {watch: `course.sectionlist:updated`, handler: this._checkSectionlist},\n ];\n }\n\n _dispatchClick(event) {\n const target = event.target.closest(this.selectors.ACTIONLINK);\n if (!target) {\n return;\n }\n if (target.classList.contains(this.classes.DISABLED)) {\n event.preventDefault();\n return;\n }\n\n // Invoke proper method.\n const actionName = target.dataset.action;\n const methodName = this._actionMethodName(actionName);\n\n if (this[methodName] !== undefined) {\n this[methodName](target, event);\n return;\n }\n\n // Check direct mutations or mutations handlers.\n if (directMutations[actionName] !== undefined) {\n if (typeof directMutations[actionName] === 'function') {\n directMutations[actionName](target, event);\n return;\n }\n this._requestMutationAction(target, event, directMutations[actionName]);\n return;\n }\n }\n\n _actionMethodName(name) {\n const requestName = name.charAt(0).toUpperCase() + name.slice(1);\n return `_request${requestName}`;\n }\n\n /**\n * Check the section list and disable some options if needed.\n *\n * @param {Object} detail the update details.\n * @param {Object} detail.state the state object.\n */\n _checkSectionlist({state}) {\n // Disable \"add section\" actions if the course max sections has been exceeded.\n this._setAddSectionLocked(state.course.sectionlist.length > state.course.maxsections);\n }\n\n /**\n * Return the ids represented by this element.\n *\n * Depending on the dataset attributes the action could represent a single id\n * or a bulk actions with all the current selected ids.\n *\n * @param {HTMLElement} target\n * @returns {Number[]} array of Ids\n */\n _getTargetIds(target) {\n let ids = [];\n if (target?.dataset?.id) {\n ids.push(target.dataset.id);\n }\n const bulkType = target?.dataset?.bulk;\n if (!bulkType) {\n return ids;\n }\n const bulk = this.reactive.get('bulk');\n if (bulk.enabled && bulk.selectedType === bulkType) {\n ids = [...ids, ...bulk.selection];\n }\n return ids;\n }\n\n /**\n * Handle a move section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestMoveSection(target, event) {\n // Check we have an id.\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n const pendingModalReady = new Pending(`courseformat/actions:prepareMoveSectionModal`);\n\n // The section edit menu to refocus on end.\n const editTools = this._getClosestActionMenuToogler(target);\n\n // Collect section information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.course(this.reactive.state);\n let titleText = null;\n\n // Add the target section id and title.\n let sectionInfo = null;\n if (sectionIds.length == 1) {\n sectionInfo = this.reactive.get('section', sectionIds[0]);\n data.sectionid = sectionInfo.id;\n data.sectiontitle = sectionInfo.title;\n data.information = await this.reactive.getFormatString('sectionmove_info', data.sectiontitle);\n titleText = this.reactive.getFormatString('sectionmove_title');\n } else {\n data.information = await this.reactive.getFormatString('sectionsmove_info', sectionIds.length);\n titleText = this.reactive.getFormatString('sectionsmove_title');\n }\n\n\n // Build the modal parameters from the event data.\n const modalParams = {\n title: titleText,\n body: Templates.render('core_courseformat/local/content/movesection', data),\n };\n\n // Create the modal.\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n const modalBody = getFirst(modal.getBody());\n\n // Disable current selected section ids.\n sectionIds.forEach(sectionId => {\n const currentElement = modalBody.querySelector(`${this.selectors.SECTIONLINK}[data-id='${sectionId}']`);\n this._disableLink(currentElement);\n });\n\n // Setup keyboard navigation.\n new ContentTree(\n modalBody.querySelector(this.selectors.CONTENTTREE),\n {\n SECTION: this.selectors.SECTIONNODE,\n TOGGLER: this.selectors.MODALTOGGLER,\n COLLAPSE: this.selectors.MODALTOGGLER,\n },\n true\n );\n\n // Capture click.\n modalBody.addEventListener('click', (event) => {\n const target = event.target;\n if (!target.matches('a') || target.dataset.for != 'section' || target.dataset.id === undefined) {\n return;\n }\n if (target.getAttribute('aria-disabled')) {\n return;\n }\n event.preventDefault();\n this.reactive.dispatch('sectionMoveAfter', sectionIds, target.dataset.id);\n this._destroyModal(modal, editTools);\n });\n\n pendingModalReady.resolve();\n }\n\n /**\n * Handle a move cm request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestMoveCm(target, event) {\n // Check we have an id.\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n const pendingModalReady = new Pending(`courseformat/actions:prepareMoveCmModal`);\n\n // The section edit menu to refocus on end.\n const editTools = this._getClosestActionMenuToogler(target);\n\n // Collect information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.course(this.reactive.state);\n\n let titleText = null;\n if (cmIds.length == 1) {\n const cmInfo = this.reactive.get('cm', cmIds[0]);\n data.cmid = cmInfo.id;\n data.cmname = cmInfo.name;\n data.information = await this.reactive.getFormatString('cmmove_info', data.cmname);\n titleText = this.reactive.getFormatString('cmmove_title');\n } else {\n data.information = await this.reactive.getFormatString('cmsmove_info', cmIds.length);\n titleText = this.reactive.getFormatString('cmsmove_title');\n }\n\n // Build the modal parameters from the event data.\n const modalParams = {\n title: titleText,\n body: Templates.render('core_courseformat/local/content/movecm', data),\n };\n\n // Create the modal.\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n const modalBody = getFirst(modal.getBody());\n\n // Disable current selected section ids.\n cmIds.forEach(cmId => {\n const currentElement = modalBody.querySelector(`${this.selectors.CMLINK}[data-id='${cmId}']`);\n this._disableLink(currentElement);\n });\n\n // Setup keyboard navigation.\n new ContentTree(\n modalBody.querySelector(this.selectors.CONTENTTREE),\n {\n SECTION: this.selectors.SECTIONNODE,\n TOGGLER: this.selectors.MODALTOGGLER,\n COLLAPSE: this.selectors.MODALTOGGLER,\n ENTER: this.selectors.SECTIONLINK,\n }\n );\n\n // Open the cm section node if possible (Bootstrap 4 uses jQuery to interact with collapsibles).\n // All jQuery in this code can be replaced when MDL-71979 is integrated.\n cmIds.forEach(cmId => {\n const currentElement = modalBody.querySelector(`${this.selectors.CMLINK}[data-id='${cmId}']`);\n const sectionnode = currentElement.closest(this.selectors.SECTIONNODE);\n const toggler = jQuery(sectionnode).find(this.selectors.MODALTOGGLER);\n let collapsibleId = toggler.data('target') ?? toggler.attr('href');\n if (collapsibleId) {\n // We cannot be sure we have # in the id element name.\n collapsibleId = collapsibleId.replace('#', '');\n const expandNode = modalBody.querySelector(`#${collapsibleId}`);\n jQuery(expandNode).collapse('show');\n }\n });\n\n modalBody.addEventListener('click', (event) => {\n const target = event.target;\n if (!target.matches('a') || target.dataset.for === undefined || target.dataset.id === undefined) {\n return;\n }\n if (target.getAttribute('aria-disabled')) {\n return;\n }\n event.preventDefault();\n\n let targetSectionId;\n let targetCmId;\n if (target.dataset.for == 'cm') {\n const dropData = exporter.cmDraggableData(this.reactive.state, target.dataset.id);\n targetSectionId = dropData.sectionid;\n targetCmId = dropData.nextcmid;\n } else {\n const section = this.reactive.get('section', target.dataset.id);\n targetSectionId = target.dataset.id;\n targetCmId = section?.cmlist[0];\n }\n this.reactive.dispatch('cmMove', cmIds, targetSectionId, targetCmId);\n this._destroyModal(modal, editTools);\n });\n\n pendingModalReady.resolve();\n }\n\n /**\n * Handle a create section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestAddSection(target, event) {\n event.preventDefault();\n this.reactive.dispatch('addSection', target.dataset.id ?? 0);\n }\n\n /**\n * Handle a delete section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestDeleteSection(target, event) {\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n // We don't need confirmation to delete empty sections.\n let needsConfirmation = sectionIds.some(sectionId => {\n const sectionInfo = this.reactive.get('section', sectionId);\n const cmList = sectionInfo.cmlist ?? [];\n return (cmList.length || sectionInfo.hassummary || sectionInfo.rawtitle);\n });\n if (!needsConfirmation) {\n this.reactive.dispatch('sectionDelete', sectionIds);\n return;\n }\n\n let bodyText = null;\n let titleText = null;\n if (sectionIds.length == 1) {\n titleText = this.reactive.getFormatString('sectiondelete_title');\n const sectionInfo = this.reactive.get('section', sectionIds[0]);\n bodyText = this.reactive.getFormatString('sectiondelete_info', {name: sectionInfo.title});\n } else {\n titleText = this.reactive.getFormatString('sectionsdelete_title');\n bodyText = this.reactive.getFormatString('sectionsdelete_info', {count: sectionIds.length});\n }\n\n const modalParams = {\n title: titleText,\n body: bodyText,\n type: ModalFactory.types.DELETE_CANCEL,\n };\n\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n modal.getRoot().on(\n ModalEvents.delete,\n e => {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n modal.destroy();\n this.reactive.dispatch('sectionDelete', sectionIds);\n }\n );\n }\n\n /**\n * Handle a toggle cm selection.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestToggleSelectionCm(target, event) {\n toggleBulkSelectionAction(this.reactive, target, event, 'cm');\n }\n\n /**\n * Handle a toggle section selection.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestToggleSelectionSection(target, event) {\n toggleBulkSelectionAction(this.reactive, target, event, 'section');\n }\n\n /**\n * Basic mutation action helper.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n * @param {string} mutationName the mutation name\n */\n async _requestMutationAction(target, event, mutationName) {\n if (!target.dataset.id) {\n return;\n }\n event.preventDefault();\n this.reactive.dispatch(mutationName, [target.dataset.id]);\n }\n\n /**\n * Handle a course module duplicate request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestCmDuplicate(target, event) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n const sectionId = target.dataset.sectionid ?? null;\n event.preventDefault();\n this.reactive.dispatch('cmDuplicate', cmIds, sectionId);\n }\n\n /**\n * Handle a delete cm request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestCmDelete(target, event) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n let bodyText = null;\n let titleText = null;\n if (cmIds.length == 1) {\n const cmInfo = this.reactive.get('cm', cmIds[0]);\n titleText = getString('cmdelete_title', 'core_courseformat');\n bodyText = getString(\n 'cmdelete_info',\n 'core_courseformat',\n {\n type: cmInfo.modname,\n name: cmInfo.name,\n }\n );\n } else {\n titleText = getString('cmsdelete_title', 'core_courseformat');\n bodyText = getString(\n 'cmsdelete_info',\n 'core_courseformat',\n {count: cmIds.length}\n );\n }\n\n const modalParams = {\n title: titleText,\n body: bodyText,\n type: ModalFactory.types.DELETE_CANCEL,\n };\n\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n modal.getRoot().on(\n ModalEvents.delete,\n e => {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n modal.destroy();\n this.reactive.dispatch('cmDelete', cmIds);\n }\n );\n }\n\n /**\n * Handle a cm availability change request.\n *\n * @param {Element} target the dispatch action element\n */\n async _requestCmAvailability(target) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n // Show the availability modal to decide which action to trigger.\n const exporter = this.reactive.getExporter();\n const data = {\n allowstealth: exporter.canUseStealth(this.reactive.state, cmIds),\n };\n const modalParams = {\n title: getString('availability', 'core'),\n body: Templates.render('core_courseformat/local/content/cm/availabilitymodal', data),\n saveButtonText: getString('apply', 'core'),\n type: ModalFactory.types.SAVE_CANCEL,\n };\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n this._setupMutationRadioButtonModal(modal, cmIds);\n }\n\n /**\n * Handle a section availability change request.\n *\n * @param {Element} target the dispatch action element\n */\n async _requestSectionAvailability(target) {\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n const title = (sectionIds.length == 1) ? 'sectionavailability_title' : 'sectionsavailability_title';\n // Show the availability modal to decide which action to trigger.\n const modalParams = {\n title: this.reactive.getFormatString(title),\n body: Templates.render('core_courseformat/local/content/section/availabilitymodal', []),\n saveButtonText: getString('apply', 'core'),\n type: ModalFactory.types.SAVE_CANCEL,\n };\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n this._setupMutationRadioButtonModal(modal, sectionIds);\n }\n\n /**\n * Add events to a mutation selector radio buttons modal.\n * @param {Modal} modal\n * @param {Number[]} ids the section or cm ids to apply the mutation\n */\n _setupMutationRadioButtonModal(modal, ids) {\n // The save button is not enabled until the user selects an option.\n modal.setButtonDisabled('save', true);\n\n const submitFunction = (radio) => {\n const mutation = radio?.value;\n if (!mutation) {\n return false;\n }\n this.reactive.dispatch(mutation, ids);\n return true;\n };\n\n const modalBody = getFirst(modal.getBody());\n const radioOptions = modalBody.querySelectorAll(this.selectors.OPTIONSRADIO);\n radioOptions.forEach(radio => {\n radio.addEventListener('change', () => {\n modal.setButtonDisabled('save', false);\n });\n radio.parentNode.addEventListener('click', () => {\n radio.checked = true;\n modal.setButtonDisabled('save', false);\n });\n radio.parentNode.addEventListener('dblclick', dbClickEvent => {\n if (submitFunction(radio)) {\n dbClickEvent.preventDefault();\n modal.destroy();\n }\n });\n });\n\n modal.getRoot().on(\n ModalEvents.save,\n () => {\n const radio = modalBody.querySelector(`${this.selectors.OPTIONSRADIO}:checked`);\n submitFunction(radio);\n }\n );\n }\n\n /**\n * Disable all add sections actions.\n *\n * @param {boolean} locked the new locked value.\n */\n _setAddSectionLocked(locked) {\n const targets = this.getElements(this.selectors.ADDSECTION);\n targets.forEach(element => {\n element.classList.toggle(this.classes.DISABLED, locked);\n element.classList.toggle(this.classes.ITALIC, locked);\n this.setElementLocked(element, locked);\n });\n }\n\n /**\n * Replace an element with a copy with a different tag name.\n *\n * @param {Element} element the original element\n */\n _disableLink(element) {\n if (element) {\n element.style.pointerEvents = 'none';\n element.style.userSelect = 'none';\n element.classList.add(this.classes.DISABLED);\n element.classList.add(this.classes.ITALIC);\n element.setAttribute('aria-disabled', true);\n element.addEventListener('click', event => event.preventDefault());\n }\n }\n\n /**\n * Render a modal and return a body ready promise.\n *\n * @param {object} modalParams the modal params\n * @return {Promise} the modal body ready promise\n */\n _modalBodyRenderedPromise(modalParams) {\n return new Promise((resolve, reject) => {\n ModalFactory.create(modalParams).then((modal) => {\n modal.setRemoveOnClose(true);\n // Handle body loading event.\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n resolve(modal);\n });\n // Configure some extra modal params.\n if (modalParams.saveButtonText !== undefined) {\n modal.setSaveButtonText(modalParams.saveButtonText);\n }\n if (modalParams.deleteButtonText !== undefined) {\n modal.setDeleteButtonText(modalParams.saveButtonText);\n }\n modal.show();\n return;\n }).catch(() => {\n reject(`Cannot load modal content`);\n });\n });\n }\n\n /**\n * Hide and later destroy a modal.\n *\n * Behat will fail if we remove the modal while some boostrap collapse is executing.\n *\n * @param {Modal} modal\n * @param {HTMLElement} element the dom element to focus on.\n */\n _destroyModal(modal, element) {\n modal.hide();\n const pendingDestroy = new Pending(`courseformat/actions:destroyModal`);\n if (element) {\n element.focus();\n }\n setTimeout(() =>{\n modal.destroy();\n pendingDestroy.resolve();\n }, 500);\n }\n\n /**\n * Get the closest actions menu toggler to an action element.\n *\n * @param {HTMLElement} element the action link element\n * @returns {HTMLElement|undefined}\n */\n _getClosestActionMenuToogler(element) {\n const actionMenu = element.closest(this.selectors.ACTIONMENU);\n if (!actionMenu) {\n return undefined;\n }\n return actionMenu.querySelector(this.selectors.ACTIONMENUTOGGLER);\n }\n}\n"],"names":["directMutations","sectionHide","sectionShow","cmHide","cmShow","cmStealth","cmMoveRight","cmMoveLeft","cmNoGroups","cmSeparateGroups","cmVisibleGroups","BaseComponent","create","name","selectors","ACTIONLINK","SECTIONLINK","CMLINK","SECTIONNODE","MODALTOGGLER","ADDSECTION","CONTENTTREE","ACTIONMENU","ACTIONMENUTOGGLER","OPTIONSRADIO","classes","DISABLED","ITALIC","actions","action","mutationReference","Object","entries","Error","stateReady","state","addEventListener","this","element","_dispatchClick","_checkSectionlist","CourseEvents","sectionRefreshed","getWatchers","watch","handler","event","target","closest","classList","contains","preventDefault","actionName","dataset","methodName","_actionMethodName","undefined","_requestMutationAction","requestName","charAt","toUpperCase","slice","_setAddSectionLocked","course","sectionlist","length","maxsections","_getTargetIds","ids","_target$dataset","id","push","bulkType","_target$dataset2","bulk","reactive","get","enabled","selectedType","selection","sectionIds","pendingModalReady","Pending","editTools","_getClosestActionMenuToogler","data","getExporter","titleText","sectionInfo","sectionid","sectiontitle","title","information","getFormatString","modalParams","body","Templates","render","modal","_modalBodyRenderedPromise","modalBody","getBody","forEach","sectionId","currentElement","querySelector","_disableLink","ContentTree","SECTION","TOGGLER","COLLAPSE","matches","for","getAttribute","dispatch","_destroyModal","resolve","cmIds","exporter","cmInfo","cmid","cmname","cmId","ENTER","sectionnode","toggler","find","collapsibleId","attr","replace","expandNode","collapse","targetSectionId","targetCmId","dropData","cmDraggableData","nextcmid","section","cmlist","some","hassummary","rawtitle","bodyText","count","type","ModalFactory","types","DELETE_CANCEL","getRoot","on","ModalEvents","delete","e","destroy","mutationName","modname","allowstealth","canUseStealth","saveButtonText","SAVE_CANCEL","_setupMutationRadioButtonModal","setButtonDisabled","submitFunction","radio","mutation","value","querySelectorAll","parentNode","checked","dbClickEvent","save","locked","getElements","toggle","setElementLocked","style","pointerEvents","userSelect","add","setAttribute","Promise","reject","then","setRemoveOnClose","bodyRendered","setSaveButtonText","deleteButtonText","setDeleteButtonText","show","catch","hide","pendingDestroy","focus","setTimeout","actionMenu"],"mappings":"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"} \ No newline at end of file diff --git a/course/format/amd/build/local/content/bulkedittools.min.js b/course/format/amd/build/local/content/bulkedittools.min.js index ea2702a0f51..7fe1fc96b77 100644 --- a/course/format/amd/build/local/content/bulkedittools.min.js +++ b/course/format/amd/build/local/content/bulkedittools.min.js @@ -6,6 +6,6 @@ define("core_courseformat/local/content/bulkedittools",["exports","core/reactive * @class core_courseformat/local/content/bulkedittools * @copyright 2023 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_pending=(obj=_pending)&&obj.__esModule?obj:{default:obj},(0,_prefetch.prefetchStrings)("core_courseformat",["bulkselection"]);class Component extends _reactive.BaseComponent{create(){this.name="bulk_editor_tools",this.selectors={ACTIONS:'[data-for="bulkaction"]',ACTIONTOOL:'[data-for="bulkactions"] li',CANCEL:'[data-for="bulkcancel"]',COUNT:"[data-for='bulkcount']",SELECTABLE:"[data-bulkcheckbox][data-is-selectable]",SELECTALL:'[data-for="selectall"]',BULKBTN:'[data-for="enableBulk"]'},this.classes={HIDE:"d-none",DISABLED:"disabled"}}static init(target,selectors){return new this({element:document.querySelector(target),reactive:(0,_courseeditor.getCurrentCourseEditor)(),selectors:selectors})}stateReady(){const cancelBtn=this.getElement(this.selectors.CANCEL);cancelBtn&&this.addEventListener(cancelBtn,"click",this._cancelBulk);const selectAll=this.getElement(this.selectors.SELECTALL);selectAll&&this.addEventListener(selectAll,"click",this._selectAllClick)}getWatchers(){return[{watch:"bulk.enabled:updated",handler:this._refreshEnabled},{watch:"bulk:updated",handler:this._refreshTools}]}_refreshEnabled(_ref){let{element:element}=_ref;element.enabled?(0,_stickyFooter.enableStickyFooter)():(0,_stickyFooter.disableStickyFooter)()}_refreshTools(param){this._refreshSelectCount(param),this._refreshSelectAll(param),this._refreshActions(param)}async _refreshSelectCount(_ref2){let{element:bulk}=_ref2;const stringName=bulk.selection.length>1?"bulkselection_plural":"bulkselection",selectedCount=await(0,_str.get_string)(stringName,"core_courseformat",bulk.selection.length),selectedElement=this.getElement(this.selectors.COUNT);selectedElement&&(selectedElement.innerHTML=selectedCount)}_refreshSelectAll(_ref3){let{element:bulk}=_ref3;const selectall=this.getElement(this.selectors.SELECTALL);if(!selectall)return;selectall.disabled=""===bulk.selectedType;const pending=new _pending.default("courseformat/bulktools:refreshSelectAll");setTimeout((()=>{selectall.checked=(0,_bulkselection.checkAllBulkSelected)(this.reactive),pending.resolve()}),100)}_refreshActions(_ref4){let{element:bulk}=_ref4;const displayType="section"==bulk.selectedType?"section":"cm",enabled=""!==bulk.selectedType;this.getElements(this.selectors.ACTIONS).forEach((action=>{action.classList.toggle(this.classes.DISABLED,!enabled),action.tabIndex=enabled?0:-1;const actionTool=action.closest(this.selectors.ACTIONTOOL),isHidden=action.dataset.bulk!=displayType;null==actionTool||actionTool.classList.toggle(this.classes.HIDE,isHidden)}))}_cancelBulk(){const pending=new _pending.default("courseformat/content:bulktoggle_off");this.reactive.dispatch("bulkEnable",!1),setTimeout((()=>{var _document$querySelect;null===(_document$querySelect=document.querySelector(this.selectors.BULKBTN))||void 0===_document$querySelect||_document$querySelect.focus(),pending.resolve()}),150)}_selectAllClick(event){event.preventDefault(),event.altKey?(0,_bulkselection.switchBulkSelection)(this.reactive):(0,_bulkselection.checkAllBulkSelected)(this.reactive)?this._handleUnselectAll():(0,_bulkselection.selectAllBulk)(this.reactive,!0)}_handleUnselectAll(){const pending=new _pending.default("courseformat/content:bulktUnselectAll");(0,_bulkselection.selectAllBulk)(this.reactive,!1),setTimeout((()=>{var _document$querySelect2;null===(_document$querySelect2=document.querySelector(this.selectors.SELECTABLE))||void 0===_document$querySelect2||_document$querySelect2.focus(),pending.resolve()}),150)}}return _exports.default=Component,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_pending=(obj=_pending)&&obj.__esModule?obj:{default:obj},(0,_prefetch.prefetchStrings)("core_courseformat",["bulkselection"]);class Component extends _reactive.BaseComponent{create(){this.name="bulk_editor_tools",this.selectors={ACTIONS:'[data-for="bulkaction"]',ACTIONTOOL:'[data-for="bulkactions"] li',CANCEL:'[data-for="bulkcancel"]',COUNT:"[data-for='bulkcount']",SELECTABLE:"[data-bulkcheckbox][data-is-selectable]",SELECTALL:'[data-for="selectall"]',BULKBTN:'[data-for="enableBulk"]'},this.classes={HIDE:"d-none",DISABLED:"disabled"}}static init(target,selectors){return new this({element:document.querySelector(target),reactive:(0,_courseeditor.getCurrentCourseEditor)(),selectors:selectors})}stateReady(){const cancelBtn=this.getElement(this.selectors.CANCEL);cancelBtn&&this.addEventListener(cancelBtn,"click",this._cancelBulk);const selectAll=this.getElement(this.selectors.SELECTALL);selectAll&&this.addEventListener(selectAll,"click",this._selectAllClick)}getWatchers(){return[{watch:"bulk.enabled:updated",handler:this._refreshEnabled},{watch:"bulk:updated",handler:this._refreshTools}]}_refreshEnabled(_ref){let{element:element}=_ref;element.enabled?(0,_stickyFooter.enableStickyFooter)():(0,_stickyFooter.disableStickyFooter)()}_refreshTools(param){this._refreshSelectCount(param),this._refreshSelectAll(param),this._refreshActions(param)}async _refreshSelectCount(_ref2){let{element:bulk}=_ref2;const stringName=bulk.selection.length>1?"bulkselection_plural":"bulkselection",selectedCount=await(0,_str.getString)(stringName,"core_courseformat",bulk.selection.length),selectedElement=this.getElement(this.selectors.COUNT);selectedElement&&(selectedElement.innerHTML=selectedCount)}_refreshSelectAll(_ref3){let{element:bulk}=_ref3;const selectall=this.getElement(this.selectors.SELECTALL);if(!selectall)return;selectall.disabled=""===bulk.selectedType;const pending=new _pending.default("courseformat/bulktools:refreshSelectAll");setTimeout((()=>{selectall.checked=(0,_bulkselection.checkAllBulkSelected)(this.reactive),pending.resolve()}),100)}_refreshActions(_ref4){let{element:bulk}=_ref4;const displayType="section"==bulk.selectedType?"section":"cm",enabled=""!==bulk.selectedType;this.getElements(this.selectors.ACTIONS).forEach((action=>{action.classList.toggle(this.classes.DISABLED,!enabled),action.tabIndex=enabled?0:-1;const actionTool=action.closest(this.selectors.ACTIONTOOL),isHidden=action.dataset.bulk!=displayType;null==actionTool||actionTool.classList.toggle(this.classes.HIDE,isHidden)}))}_cancelBulk(){const pending=new _pending.default("courseformat/content:bulktoggle_off");this.reactive.dispatch("bulkEnable",!1),setTimeout((()=>{var _document$querySelect;null===(_document$querySelect=document.querySelector(this.selectors.BULKBTN))||void 0===_document$querySelect||_document$querySelect.focus(),pending.resolve()}),150)}_selectAllClick(event){event.preventDefault(),event.altKey?(0,_bulkselection.switchBulkSelection)(this.reactive):(0,_bulkselection.checkAllBulkSelected)(this.reactive)?this._handleUnselectAll():(0,_bulkselection.selectAllBulk)(this.reactive,!0)}_handleUnselectAll(){const pending=new _pending.default("courseformat/content:bulktUnselectAll");(0,_bulkselection.selectAllBulk)(this.reactive,!1),setTimeout((()=>{var _document$querySelect2;null===(_document$querySelect2=document.querySelector(this.selectors.SELECTABLE))||void 0===_document$querySelect2||_document$querySelect2.focus(),pending.resolve()}),150)}}return _exports.default=Component,_exports.default})); //# sourceMappingURL=bulkedittools.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/content/bulkedittools.min.js.map b/course/format/amd/build/local/content/bulkedittools.min.js.map index fd5d5eca214..548974d3a3b 100644 --- a/course/format/amd/build/local/content/bulkedittools.min.js.map +++ b/course/format/amd/build/local/content/bulkedittools.min.js.map @@ -1 +1 @@ -{"version":3,"file":"bulkedittools.min.js","sources":["../../../src/local/content/bulkedittools.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 * The bulk editor tools bar.\n *\n * @module core_courseformat/local/content/bulkedittools\n * @class core_courseformat/local/content/bulkedittools\n * @copyright 2023 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport {disableStickyFooter, enableStickyFooter} from 'core/sticky-footer';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport {\n selectAllBulk,\n switchBulkSelection,\n checkAllBulkSelected\n} from 'core_courseformat/local/content/actions/bulkselection';\n\n// Load global strings.\nprefetchStrings(\n 'core_courseformat',\n ['bulkselection']\n);\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'bulk_editor_tools';\n // Default query selectors.\n this.selectors = {\n ACTIONS: `[data-for=\"bulkaction\"]`,\n ACTIONTOOL: `[data-for=\"bulkactions\"] li`,\n CANCEL: `[data-for=\"bulkcancel\"]`,\n COUNT: `[data-for='bulkcount']`,\n SELECTABLE: `[data-bulkcheckbox][data-is-selectable]`,\n SELECTALL: `[data-for=\"selectall\"]`,\n BULKBTN: `[data-for=\"enableBulk\"]`,\n };\n // Most classes will be loaded later by DndCmItem.\n this.classes = {\n HIDE: 'd-none',\n DISABLED: 'disabled',\n };\n }\n\n /**\n * Static method to create a component instance from the mustache template.\n *\n * @param {string} target optional altentative DOM main element CSS selector\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new this({\n element: document.querySelector(target),\n reactive: getCurrentCourseEditor(),\n selectors\n });\n }\n\n /**\n * Initial state ready method.\n */\n stateReady() {\n const cancelBtn = this.getElement(this.selectors.CANCEL);\n if (cancelBtn) {\n this.addEventListener(cancelBtn, 'click', this._cancelBulk);\n }\n const selectAll = this.getElement(this.selectors.SELECTALL);\n if (selectAll) {\n this.addEventListener(selectAll, 'click', this._selectAllClick);\n }\n }\n\n /**\n * Component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n {watch: `bulk.enabled:updated`, handler: this._refreshEnabled},\n {watch: `bulk:updated`, handler: this._refreshTools},\n ];\n }\n\n /**\n * Hide and show the bulk edit tools.\n *\n * @param {object} param\n * @param {Object} param.element details the update details (state.bulk in this case).\n */\n _refreshEnabled({element}) {\n if (element.enabled) {\n enableStickyFooter();\n } else {\n disableStickyFooter();\n }\n }\n\n /**\n * Refresh the tools depending on the current selection.\n *\n * @param {object} param the state watcher information\n * @param {Object} param.state the full state data.\n * @param {Object} param.element the affected element (bulk in this case).\n */\n _refreshTools(param) {\n this._refreshSelectCount(param);\n this._refreshSelectAll(param);\n this._refreshActions(param);\n }\n\n /**\n * Refresh the selection count.\n *\n * @param {object} param\n * @param {Object} param.element the affected element (bulk in this case).\n */\n async _refreshSelectCount({element: bulk}) {\n const stringName = (bulk.selection.length > 1) ? 'bulkselection_plural' : 'bulkselection';\n const selectedCount = await getString(stringName, 'core_courseformat', bulk.selection.length);\n const selectedElement = this.getElement(this.selectors.COUNT);\n if (selectedElement) {\n selectedElement.innerHTML = selectedCount;\n }\n }\n\n /**\n * Refresh the select all element.\n *\n * @param {object} param\n * @param {Object} param.element the affected element (bulk in this case).\n */\n _refreshSelectAll({element: bulk}) {\n const selectall = this.getElement(this.selectors.SELECTALL);\n if (!selectall) {\n return;\n }\n selectall.disabled = (bulk.selectedType === '');\n // The changechecker module can prevent the checkbox form changing it's value.\n // To avoid that we leave the sniffer to act before changing the value.\n const pending = new Pending(`courseformat/bulktools:refreshSelectAll`);\n setTimeout(\n () => {\n selectall.checked = checkAllBulkSelected(this.reactive);\n pending.resolve();\n },\n 100\n );\n }\n\n /**\n * Refresh the visible action buttons depending on the selection type.\n *\n * @param {object} param\n * @param {Object} param.element the affected element (bulk in this case).\n */\n _refreshActions({element: bulk}) {\n // By default, we show the cm options.\n const displayType = (bulk.selectedType == 'section') ? 'section' : 'cm';\n const enabled = (bulk.selectedType !== '');\n this.getElements(this.selectors.ACTIONS).forEach(action => {\n action.classList.toggle(this.classes.DISABLED, !enabled);\n action.tabIndex = (enabled) ? 0 : -1;\n\n const actionTool = action.closest(this.selectors.ACTIONTOOL);\n const isHidden = (action.dataset.bulk != displayType);\n actionTool?.classList.toggle(this.classes.HIDE, isHidden);\n });\n }\n\n /**\n * Cancel bulk handler.\n */\n _cancelBulk() {\n const pending = new Pending(`courseformat/content:bulktoggle_off`);\n this.reactive.dispatch('bulkEnable', false);\n // Wait for a while and focus on enable bulk button.\n setTimeout(() => {\n document.querySelector(this.selectors.BULKBTN)?.focus();\n pending.resolve();\n }, 150);\n }\n\n /**\n * Handle special select all cases.\n * @param {Event} event\n */\n _selectAllClick(event) {\n event.preventDefault();\n if (event.altKey) {\n switchBulkSelection(this.reactive);\n return;\n }\n if (checkAllBulkSelected(this.reactive)) {\n this._handleUnselectAll();\n return;\n }\n selectAllBulk(this.reactive, true);\n }\n\n /**\n * Process unselect all elements.\n */\n _handleUnselectAll() {\n const pending = new Pending(`courseformat/content:bulktUnselectAll`);\n selectAllBulk(this.reactive, false);\n // Wait for a while and focus on the first checkbox.\n setTimeout(() => {\n document.querySelector(this.selectors.SELECTABLE)?.focus();\n pending.resolve();\n }, 150);\n }\n}\n"],"names":["Component","BaseComponent","create","name","selectors","ACTIONS","ACTIONTOOL","CANCEL","COUNT","SELECTABLE","SELECTALL","BULKBTN","classes","HIDE","DISABLED","target","this","element","document","querySelector","reactive","stateReady","cancelBtn","getElement","addEventListener","_cancelBulk","selectAll","_selectAllClick","getWatchers","watch","handler","_refreshEnabled","_refreshTools","enabled","param","_refreshSelectCount","_refreshSelectAll","_refreshActions","bulk","stringName","selection","length","selectedCount","selectedElement","innerHTML","selectall","disabled","selectedType","pending","Pending","setTimeout","checked","resolve","displayType","getElements","forEach","action","classList","toggle","tabIndex","actionTool","closest","isHidden","dataset","dispatch","focus","event","preventDefault","altKey","_handleUnselectAll"],"mappings":";;;;;;;;6KAsCI,oBACA,CAAC,wBAGgBA,kBAAkBC,wBAKnCC,cAESC,KAAO,yBAEPC,UAAY,CACbC,kCACAC,yCACAC,iCACAC,+BACAC,qDACAC,mCACAC,wCAGCC,QAAU,CACXC,KAAM,SACNC,SAAU,wBAWNC,OAAQX,kBACT,IAAIY,KAAK,CACZC,QAASC,SAASC,cAAcJ,QAChCK,UAAU,0CACVhB,UAAAA,YAORiB,mBACUC,UAAYN,KAAKO,WAAWP,KAAKZ,UAAUG,QAC7Ce,gBACKE,iBAAiBF,UAAW,QAASN,KAAKS,mBAE7CC,UAAYV,KAAKO,WAAWP,KAAKZ,UAAUM,WAC7CgB,gBACKF,iBAAiBE,UAAW,QAASV,KAAKW,iBASvDC,oBACW,CACH,CAACC,6BAA+BC,QAASd,KAAKe,iBAC9C,CAACF,qBAAuBC,QAASd,KAAKgB,gBAU9CD,0BAAgBd,QAACA,cACTA,QAAQgB,uFAchBD,cAAcE,YACLC,oBAAoBD,YACpBE,kBAAkBF,YAClBG,gBAAgBH,4CASEjB,QAASqB,kBAC1BC,WAAcD,KAAKE,UAAUC,OAAS,EAAK,uBAAyB,gBACpEC,oBAAsB,mBAAUH,WAAY,oBAAqBD,KAAKE,UAAUC,QAChFE,gBAAkB3B,KAAKO,WAAWP,KAAKZ,UAAUI,OACnDmC,kBACAA,gBAAgBC,UAAYF,eAUpCN,6BAAmBnB,QAASqB,kBAClBO,UAAY7B,KAAKO,WAAWP,KAAKZ,UAAUM,eAC5CmC,iBAGLA,UAAUC,SAAkC,KAAtBR,KAAKS,mBAGrBC,QAAU,IAAIC,4DACpBC,YACI,KACIL,UAAUM,SAAU,uCAAqBnC,KAAKI,UAC9C4B,QAAQI,YAEZ,KAURf,2BAAiBpB,QAASqB,kBAEhBe,YAAoC,WAArBf,KAAKS,aAA6B,UAAY,KAC7Dd,QAAiC,KAAtBK,KAAKS,kBACjBO,YAAYtC,KAAKZ,UAAUC,SAASkD,SAAQC,SAC7CA,OAAOC,UAAUC,OAAO1C,KAAKJ,QAAQE,UAAWmB,SAChDuB,OAAOG,SAAY1B,QAAW,GAAK,QAE7B2B,WAAaJ,OAAOK,QAAQ7C,KAAKZ,UAAUE,YAC3CwD,SAAYN,OAAOO,QAAQzB,MAAQe,YACzCO,MAAAA,YAAAA,WAAYH,UAAUC,OAAO1C,KAAKJ,QAAQC,KAAMiD,aAOxDrC,oBACUuB,QAAU,IAAIC,6DACf7B,SAAS4C,SAAS,cAAc,GAErCd,YAAW,6DACPhC,SAASC,cAAcH,KAAKZ,UAAUO,iEAAUsD,QAChDjB,QAAQI,YACT,KAOPzB,gBAAgBuC,OACZA,MAAMC,iBACFD,MAAME,8CACcpD,KAAKI,WAGzB,uCAAqBJ,KAAKI,eACrBiD,sDAGKrD,KAAKI,UAAU,GAMjCiD,2BACUrB,QAAU,IAAIC,2FACNjC,KAAKI,UAAU,GAE7B8B,YAAW,+DACPhC,SAASC,cAAcH,KAAKZ,UAAUK,sEAAawD,QACnDjB,QAAQI,YACT"} \ No newline at end of file +{"version":3,"file":"bulkedittools.min.js","sources":["../../../src/local/content/bulkedittools.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 * The bulk editor tools bar.\n *\n * @module core_courseformat/local/content/bulkedittools\n * @class core_courseformat/local/content/bulkedittools\n * @copyright 2023 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport {disableStickyFooter, enableStickyFooter} from 'core/sticky-footer';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport {getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport {\n selectAllBulk,\n switchBulkSelection,\n checkAllBulkSelected\n} from 'core_courseformat/local/content/actions/bulkselection';\n\n// Load global strings.\nprefetchStrings(\n 'core_courseformat',\n ['bulkselection']\n);\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'bulk_editor_tools';\n // Default query selectors.\n this.selectors = {\n ACTIONS: `[data-for=\"bulkaction\"]`,\n ACTIONTOOL: `[data-for=\"bulkactions\"] li`,\n CANCEL: `[data-for=\"bulkcancel\"]`,\n COUNT: `[data-for='bulkcount']`,\n SELECTABLE: `[data-bulkcheckbox][data-is-selectable]`,\n SELECTALL: `[data-for=\"selectall\"]`,\n BULKBTN: `[data-for=\"enableBulk\"]`,\n };\n // Most classes will be loaded later by DndCmItem.\n this.classes = {\n HIDE: 'd-none',\n DISABLED: 'disabled',\n };\n }\n\n /**\n * Static method to create a component instance from the mustache template.\n *\n * @param {string} target optional altentative DOM main element CSS selector\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new this({\n element: document.querySelector(target),\n reactive: getCurrentCourseEditor(),\n selectors\n });\n }\n\n /**\n * Initial state ready method.\n */\n stateReady() {\n const cancelBtn = this.getElement(this.selectors.CANCEL);\n if (cancelBtn) {\n this.addEventListener(cancelBtn, 'click', this._cancelBulk);\n }\n const selectAll = this.getElement(this.selectors.SELECTALL);\n if (selectAll) {\n this.addEventListener(selectAll, 'click', this._selectAllClick);\n }\n }\n\n /**\n * Component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n {watch: `bulk.enabled:updated`, handler: this._refreshEnabled},\n {watch: `bulk:updated`, handler: this._refreshTools},\n ];\n }\n\n /**\n * Hide and show the bulk edit tools.\n *\n * @param {object} param\n * @param {Object} param.element details the update details (state.bulk in this case).\n */\n _refreshEnabled({element}) {\n if (element.enabled) {\n enableStickyFooter();\n } else {\n disableStickyFooter();\n }\n }\n\n /**\n * Refresh the tools depending on the current selection.\n *\n * @param {object} param the state watcher information\n * @param {Object} param.state the full state data.\n * @param {Object} param.element the affected element (bulk in this case).\n */\n _refreshTools(param) {\n this._refreshSelectCount(param);\n this._refreshSelectAll(param);\n this._refreshActions(param);\n }\n\n /**\n * Refresh the selection count.\n *\n * @param {object} param\n * @param {Object} param.element the affected element (bulk in this case).\n */\n async _refreshSelectCount({element: bulk}) {\n const stringName = (bulk.selection.length > 1) ? 'bulkselection_plural' : 'bulkselection';\n const selectedCount = await getString(stringName, 'core_courseformat', bulk.selection.length);\n const selectedElement = this.getElement(this.selectors.COUNT);\n if (selectedElement) {\n selectedElement.innerHTML = selectedCount;\n }\n }\n\n /**\n * Refresh the select all element.\n *\n * @param {object} param\n * @param {Object} param.element the affected element (bulk in this case).\n */\n _refreshSelectAll({element: bulk}) {\n const selectall = this.getElement(this.selectors.SELECTALL);\n if (!selectall) {\n return;\n }\n selectall.disabled = (bulk.selectedType === '');\n // The changechecker module can prevent the checkbox form changing it's value.\n // To avoid that we leave the sniffer to act before changing the value.\n const pending = new Pending(`courseformat/bulktools:refreshSelectAll`);\n setTimeout(\n () => {\n selectall.checked = checkAllBulkSelected(this.reactive);\n pending.resolve();\n },\n 100\n );\n }\n\n /**\n * Refresh the visible action buttons depending on the selection type.\n *\n * @param {object} param\n * @param {Object} param.element the affected element (bulk in this case).\n */\n _refreshActions({element: bulk}) {\n // By default, we show the cm options.\n const displayType = (bulk.selectedType == 'section') ? 'section' : 'cm';\n const enabled = (bulk.selectedType !== '');\n this.getElements(this.selectors.ACTIONS).forEach(action => {\n action.classList.toggle(this.classes.DISABLED, !enabled);\n action.tabIndex = (enabled) ? 0 : -1;\n\n const actionTool = action.closest(this.selectors.ACTIONTOOL);\n const isHidden = (action.dataset.bulk != displayType);\n actionTool?.classList.toggle(this.classes.HIDE, isHidden);\n });\n }\n\n /**\n * Cancel bulk handler.\n */\n _cancelBulk() {\n const pending = new Pending(`courseformat/content:bulktoggle_off`);\n this.reactive.dispatch('bulkEnable', false);\n // Wait for a while and focus on enable bulk button.\n setTimeout(() => {\n document.querySelector(this.selectors.BULKBTN)?.focus();\n pending.resolve();\n }, 150);\n }\n\n /**\n * Handle special select all cases.\n * @param {Event} event\n */\n _selectAllClick(event) {\n event.preventDefault();\n if (event.altKey) {\n switchBulkSelection(this.reactive);\n return;\n }\n if (checkAllBulkSelected(this.reactive)) {\n this._handleUnselectAll();\n return;\n }\n selectAllBulk(this.reactive, true);\n }\n\n /**\n * Process unselect all elements.\n */\n _handleUnselectAll() {\n const pending = new Pending(`courseformat/content:bulktUnselectAll`);\n selectAllBulk(this.reactive, false);\n // Wait for a while and focus on the first checkbox.\n setTimeout(() => {\n document.querySelector(this.selectors.SELECTABLE)?.focus();\n pending.resolve();\n }, 150);\n }\n}\n"],"names":["Component","BaseComponent","create","name","selectors","ACTIONS","ACTIONTOOL","CANCEL","COUNT","SELECTABLE","SELECTALL","BULKBTN","classes","HIDE","DISABLED","target","this","element","document","querySelector","reactive","stateReady","cancelBtn","getElement","addEventListener","_cancelBulk","selectAll","_selectAllClick","getWatchers","watch","handler","_refreshEnabled","_refreshTools","enabled","param","_refreshSelectCount","_refreshSelectAll","_refreshActions","bulk","stringName","selection","length","selectedCount","selectedElement","innerHTML","selectall","disabled","selectedType","pending","Pending","setTimeout","checked","resolve","displayType","getElements","forEach","action","classList","toggle","tabIndex","actionTool","closest","isHidden","dataset","dispatch","focus","event","preventDefault","altKey","_handleUnselectAll"],"mappings":";;;;;;;;6KAsCI,oBACA,CAAC,wBAGgBA,kBAAkBC,wBAKnCC,cAESC,KAAO,yBAEPC,UAAY,CACbC,kCACAC,yCACAC,iCACAC,+BACAC,qDACAC,mCACAC,wCAGCC,QAAU,CACXC,KAAM,SACNC,SAAU,wBAWNC,OAAQX,kBACT,IAAIY,KAAK,CACZC,QAASC,SAASC,cAAcJ,QAChCK,UAAU,0CACVhB,UAAAA,YAORiB,mBACUC,UAAYN,KAAKO,WAAWP,KAAKZ,UAAUG,QAC7Ce,gBACKE,iBAAiBF,UAAW,QAASN,KAAKS,mBAE7CC,UAAYV,KAAKO,WAAWP,KAAKZ,UAAUM,WAC7CgB,gBACKF,iBAAiBE,UAAW,QAASV,KAAKW,iBASvDC,oBACW,CACH,CAACC,6BAA+BC,QAASd,KAAKe,iBAC9C,CAACF,qBAAuBC,QAASd,KAAKgB,gBAU9CD,0BAAgBd,QAACA,cACTA,QAAQgB,uFAchBD,cAAcE,YACLC,oBAAoBD,YACpBE,kBAAkBF,YAClBG,gBAAgBH,4CASEjB,QAASqB,kBAC1BC,WAAcD,KAAKE,UAAUC,OAAS,EAAK,uBAAyB,gBACpEC,oBAAsB,kBAAUH,WAAY,oBAAqBD,KAAKE,UAAUC,QAChFE,gBAAkB3B,KAAKO,WAAWP,KAAKZ,UAAUI,OACnDmC,kBACAA,gBAAgBC,UAAYF,eAUpCN,6BAAmBnB,QAASqB,kBAClBO,UAAY7B,KAAKO,WAAWP,KAAKZ,UAAUM,eAC5CmC,iBAGLA,UAAUC,SAAkC,KAAtBR,KAAKS,mBAGrBC,QAAU,IAAIC,4DACpBC,YACI,KACIL,UAAUM,SAAU,uCAAqBnC,KAAKI,UAC9C4B,QAAQI,YAEZ,KAURf,2BAAiBpB,QAASqB,kBAEhBe,YAAoC,WAArBf,KAAKS,aAA6B,UAAY,KAC7Dd,QAAiC,KAAtBK,KAAKS,kBACjBO,YAAYtC,KAAKZ,UAAUC,SAASkD,SAAQC,SAC7CA,OAAOC,UAAUC,OAAO1C,KAAKJ,QAAQE,UAAWmB,SAChDuB,OAAOG,SAAY1B,QAAW,GAAK,QAE7B2B,WAAaJ,OAAOK,QAAQ7C,KAAKZ,UAAUE,YAC3CwD,SAAYN,OAAOO,QAAQzB,MAAQe,YACzCO,MAAAA,YAAAA,WAAYH,UAAUC,OAAO1C,KAAKJ,QAAQC,KAAMiD,aAOxDrC,oBACUuB,QAAU,IAAIC,6DACf7B,SAAS4C,SAAS,cAAc,GAErCd,YAAW,6DACPhC,SAASC,cAAcH,KAAKZ,UAAUO,iEAAUsD,QAChDjB,QAAQI,YACT,KAOPzB,gBAAgBuC,OACZA,MAAMC,iBACFD,MAAME,8CACcpD,KAAKI,WAGzB,uCAAqBJ,KAAKI,eACrBiD,sDAGKrD,KAAKI,UAAU,GAMjCiD,2BACUrB,QAAU,IAAIC,2FACNjC,KAAKI,UAAU,GAE7B8B,YAAW,+DACPhC,SAASC,cAAcH,KAAKZ,UAAUK,sEAAawD,QACnDjB,QAAQI,YACT"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/courseeditor.min.js b/course/format/amd/build/local/courseeditor/courseeditor.min.js index 0a8ec432a72..5ea98e85a8c 100644 --- a/course/format/amd/build/local/courseeditor/courseeditor.min.js +++ b/course/format/amd/build/local/courseeditor/courseeditor.min.js @@ -9,6 +9,6 @@ define("core_courseformat/local/courseeditor/courseeditor",["exports","core/str" * @class core_courseformat/local/courseeditor/courseeditor * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_notification=_interopRequireDefault(_notification),_exporter=_interopRequireDefault(_exporter),_log=_interopRequireDefault(_log),_ajax=_interopRequireDefault(_ajax),Storage=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Storage);class _default extends _reactive.Reactive{constructor(){super(...arguments),_defineProperty(this,"stateKey",1),_defineProperty(this,"sectionReturn",0)}async loadCourse(courseId,serverStateKey){if(this.courseId)throw new Error("Cannot load ".concat(courseId,", course already loaded with id ").concat(this.courseId));let stateData;serverStateKey||(serverStateKey="invalidStateKey_".concat(Date.now())),this._editing=!1,this._supportscomponents=!1,this._fileHandlers=null,this.courseId=courseId;const storeStateKey=Storage.get("course/".concat(courseId,"/stateKey"));try{this.isEditing||serverStateKey!=storeStateKey||(stateData=JSON.parse(Storage.get("course/".concat(courseId,"/staticState")))),stateData||(stateData=await this.getServerCourseState())}catch(error){return _log.default.error("EXCEPTION RAISED WHILE INIT COURSE EDITOR"),void _log.default.error(error)}if(stateData.bulk={enabled:!1,selectedType:"",selection:[]},this.setInitialState(stateData),this.isEditing)this.stateKey=null;else{const newState=JSON.stringify(stateData);var _stateData$course$sta,_stateData,_stateData$course;if(Storage.get("course/".concat(courseId,"/staticState"))!==newState||storeStateKey!==serverStateKey)Storage.set("course/".concat(courseId,"/staticState"),newState),Storage.set("course/".concat(courseId,"/stateKey"),null!==(_stateData$course$sta=null===(_stateData=stateData)||void 0===_stateData||null===(_stateData$course=_stateData.course)||void 0===_stateData$course?void 0:_stateData$course.statekey)&&void 0!==_stateData$course$sta?_stateData$course$sta:serverStateKey);this.stateKey=Storage.get("course/".concat(courseId,"/stateKey"))}this._loadFileHandlers()}_loadFileHandlers(){this._fileHandlersPromise=new Promise((resolve=>{if(!this.isEditing)return void resolve([]);const handlersCacheKey="course/".concat(this.courseId,"/fileHandlers"),cacheValue=Storage.get(handlersCacheKey);if(cacheValue)try{const cachedHandlers=JSON.parse(cacheValue);return void resolve(cachedHandlers)}catch(error){_log.default.error("ERROR PARSING CACHED FILE HANDLERS")}_ajax.default.call([{methodname:"core_courseformat_file_handlers",args:{courseid:this.courseId}}])[0].then((handlers=>{Storage.set(handlersCacheKey,JSON.stringify(handlers)),resolve(handlers)})).catch((error=>{_log.default.error(error),resolve([])}))}))}setViewFormat(setup){var _setup$editing,_setup$supportscompon,_setup$overriddenStri;this._editing=null!==(_setup$editing=setup.editing)&&void 0!==_setup$editing&&_setup$editing,this._supportscomponents=null!==(_setup$supportscompon=setup.supportscomponents)&&void 0!==_setup$supportscompon&&_setup$supportscompon;const overriddenStrings=null!==(_setup$overriddenStri=setup.overriddenStrings)&&void 0!==_setup$overriddenStri?_setup$overriddenStri:[];this._overriddenStrings=overriddenStrings.reduce(((indexed,currentValue)=>indexed.set(currentValue.key,currentValue)),new Map)}getFormatString(key,param){if(this._overriddenStrings.has(key)){var _override$component;const override=this._overriddenStrings.get(key);return(0,_str.get_string)(key,null!==(_override$component=override.component)&&void 0!==_override$component?_override$component:"core_courseformat",param)}return(0,_str.get_string)(key,"core_courseformat",param)}async getServerCourseState(){const courseState=await _ajax.default.call([{methodname:"core_courseformat_get_state",args:{courseid:this.courseId}}])[0];return{course:{},section:[],cm:[],...JSON.parse(courseState)}}get isEditing(){var _this$_editing;return null!==(_this$_editing=this._editing)&&void 0!==_this$_editing&&_this$_editing}getExporter(){return new _exporter.default(this)}get supportComponents(){var _this$_supportscompon;return null!==(_this$_supportscompon=this._supportscomponents)&&void 0!==_this$_supportscompon&&_this$_supportscompon}async getFileHandlersPromise(){var _this$_fileHandlersPr;return null!==(_this$_fileHandlersPr=this._fileHandlersPromise)&&void 0!==_this$_fileHandlersPr?_this$_fileHandlersPr:[]}uploadFiles(sectionId,sectionNum,files){return(0,_fileuploader.uploadFilesToCourse)(this.courseId,sectionId,sectionNum,files)}getStorageValue(key){if(this.isEditing||!this.stateKey)return!1;const dataJson=Storage.get("course/".concat(this.courseId,"/").concat(key));if(!dataJson)return!1;try{const data=JSON.parse(dataJson);return(null==data?void 0:data.stateKey)===this.stateKey&&data.value}catch(error){return!1}}setStorageValue(key,value){if(this.isEditing)return!1;const data={stateKey:this.stateKey,value:value};return Storage.set("course/".concat(this.courseId,"/").concat(key),JSON.stringify(data))}getFilesDraggableData(dataTransfer){return this.getExporter().fileDraggableData(this.state,dataTransfer)}async dispatch(){try{await super.dispatch(...arguments)}catch(error){_notification.default.exception(error),super.dispatch("unlockAll")}}}return _exports.default=_default,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_notification=_interopRequireDefault(_notification),_exporter=_interopRequireDefault(_exporter),_log=_interopRequireDefault(_log),_ajax=_interopRequireDefault(_ajax),Storage=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Storage);class _default extends _reactive.Reactive{constructor(){super(...arguments),_defineProperty(this,"stateKey",1),_defineProperty(this,"sectionReturn",0)}async loadCourse(courseId,serverStateKey){if(this.courseId)throw new Error("Cannot load ".concat(courseId,", course already loaded with id ").concat(this.courseId));let stateData;serverStateKey||(serverStateKey="invalidStateKey_".concat(Date.now())),this._editing=!1,this._supportscomponents=!1,this._fileHandlers=null,this.courseId=courseId;const storeStateKey=Storage.get("course/".concat(courseId,"/stateKey"));try{this.isEditing||serverStateKey!=storeStateKey||(stateData=JSON.parse(Storage.get("course/".concat(courseId,"/staticState")))),stateData||(stateData=await this.getServerCourseState())}catch(error){return _log.default.error("EXCEPTION RAISED WHILE INIT COURSE EDITOR"),void _log.default.error(error)}if(stateData.bulk={enabled:!1,selectedType:"",selection:[]},this.setInitialState(stateData),this.isEditing)this.stateKey=null;else{const newState=JSON.stringify(stateData);var _stateData$course$sta,_stateData,_stateData$course;if(Storage.get("course/".concat(courseId,"/staticState"))!==newState||storeStateKey!==serverStateKey)Storage.set("course/".concat(courseId,"/staticState"),newState),Storage.set("course/".concat(courseId,"/stateKey"),null!==(_stateData$course$sta=null===(_stateData=stateData)||void 0===_stateData||null===(_stateData$course=_stateData.course)||void 0===_stateData$course?void 0:_stateData$course.statekey)&&void 0!==_stateData$course$sta?_stateData$course$sta:serverStateKey);this.stateKey=Storage.get("course/".concat(courseId,"/stateKey"))}this._loadFileHandlers()}_loadFileHandlers(){this._fileHandlersPromise=new Promise((resolve=>{if(!this.isEditing)return void resolve([]);const handlersCacheKey="course/".concat(this.courseId,"/fileHandlers"),cacheValue=Storage.get(handlersCacheKey);if(cacheValue)try{const cachedHandlers=JSON.parse(cacheValue);return void resolve(cachedHandlers)}catch(error){_log.default.error("ERROR PARSING CACHED FILE HANDLERS")}_ajax.default.call([{methodname:"core_courseformat_file_handlers",args:{courseid:this.courseId}}])[0].then((handlers=>{Storage.set(handlersCacheKey,JSON.stringify(handlers)),resolve(handlers)})).catch((error=>{_log.default.error(error),resolve([])}))}))}setViewFormat(setup){var _setup$editing,_setup$supportscompon,_setup$overriddenStri;this._editing=null!==(_setup$editing=setup.editing)&&void 0!==_setup$editing&&_setup$editing,this._supportscomponents=null!==(_setup$supportscompon=setup.supportscomponents)&&void 0!==_setup$supportscompon&&_setup$supportscompon;const overriddenStrings=null!==(_setup$overriddenStri=setup.overriddenStrings)&&void 0!==_setup$overriddenStri?_setup$overriddenStri:[];this._overriddenStrings=overriddenStrings.reduce(((indexed,currentValue)=>indexed.set(currentValue.key,currentValue)),new Map)}getFormatString(key,param){if(this._overriddenStrings.has(key)){var _override$component;const override=this._overriddenStrings.get(key);return(0,_str.getString)(key,null!==(_override$component=override.component)&&void 0!==_override$component?_override$component:"core_courseformat",param)}return(0,_str.getString)(key,"core_courseformat",param)}async getServerCourseState(){const courseState=await _ajax.default.call([{methodname:"core_courseformat_get_state",args:{courseid:this.courseId}}])[0];return{course:{},section:[],cm:[],...JSON.parse(courseState)}}get isEditing(){var _this$_editing;return null!==(_this$_editing=this._editing)&&void 0!==_this$_editing&&_this$_editing}getExporter(){return new _exporter.default(this)}get supportComponents(){var _this$_supportscompon;return null!==(_this$_supportscompon=this._supportscomponents)&&void 0!==_this$_supportscompon&&_this$_supportscompon}async getFileHandlersPromise(){var _this$_fileHandlersPr;return null!==(_this$_fileHandlersPr=this._fileHandlersPromise)&&void 0!==_this$_fileHandlersPr?_this$_fileHandlersPr:[]}uploadFiles(sectionId,sectionNum,files){return(0,_fileuploader.uploadFilesToCourse)(this.courseId,sectionId,sectionNum,files)}getStorageValue(key){if(this.isEditing||!this.stateKey)return!1;const dataJson=Storage.get("course/".concat(this.courseId,"/").concat(key));if(!dataJson)return!1;try{const data=JSON.parse(dataJson);return(null==data?void 0:data.stateKey)===this.stateKey&&data.value}catch(error){return!1}}setStorageValue(key,value){if(this.isEditing)return!1;const data={stateKey:this.stateKey,value:value};return Storage.set("course/".concat(this.courseId,"/").concat(key),JSON.stringify(data))}getFilesDraggableData(dataTransfer){return this.getExporter().fileDraggableData(this.state,dataTransfer)}async dispatch(){try{await super.dispatch(...arguments)}catch(error){_notification.default.exception(error),super.dispatch("unlockAll")}}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=courseeditor.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/courseeditor.min.js.map b/course/format/amd/build/local/courseeditor/courseeditor.min.js.map index 7e82947b350..716eb865e59 100644 --- a/course/format/amd/build/local/courseeditor/courseeditor.min.js.map +++ b/course/format/amd/build/local/courseeditor/courseeditor.min.js.map @@ -1 +1 @@ -{"version":3,"file":"courseeditor.min.js","sources":["../../../src/local/courseeditor/courseeditor.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\nimport {get_string as getString} from 'core/str';\nimport {Reactive} from 'core/reactive';\nimport notification from 'core/notification';\nimport Exporter from 'core_courseformat/local/courseeditor/exporter';\nimport log from 'core/log';\nimport ajax from 'core/ajax';\nimport * as Storage from 'core/sessionstorage';\nimport {uploadFilesToCourse} from 'core_courseformat/local/courseeditor/fileuploader';\n\n/**\n * Main course editor module.\n *\n * All formats can register new components on this object to create new reactive\n * UI components that watch the current course state.\n *\n * @module core_courseformat/local/courseeditor/courseeditor\n * @class core_courseformat/local/courseeditor/courseeditor\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class extends Reactive {\n\n /**\n * The current state cache key\n *\n * The state cache is considered dirty if the state changes from the last page or\n * if the page has editing mode on.\n *\n * @attribute stateKey\n * @type number|null\n * @default 1\n * @package\n */\n stateKey = 1;\n\n /**\n * The current page section return\n * @attribute sectionReturn\n * @type number\n * @default 0\n */\n sectionReturn = 0;\n\n /**\n * Set up the course editor when the page is ready.\n *\n * The course can only be loaded once per instance. Otherwise an error is thrown.\n *\n * The backend can inform the module of the current state key. This key changes every time some\n * update in the course affect the current user state. Some examples are:\n * - The course content has been edited\n * - The user marks some activity as completed\n * - The user collapses or uncollapses a section (it is stored as a user preference)\n *\n * @param {number} courseId course id\n * @param {string} serverStateKey the current backend course cache reference\n */\n async loadCourse(courseId, serverStateKey) {\n\n if (this.courseId) {\n throw new Error(`Cannot load ${courseId}, course already loaded with id ${this.courseId}`);\n }\n\n if (!serverStateKey) {\n // The server state key is not provided, we use a invalid statekey to force reloading.\n serverStateKey = `invalidStateKey_${Date.now()}`;\n }\n\n // Default view format setup.\n this._editing = false;\n this._supportscomponents = false;\n this._fileHandlers = null;\n\n this.courseId = courseId;\n\n let stateData;\n\n const storeStateKey = Storage.get(`course/${courseId}/stateKey`);\n try {\n // Check if the backend state key is the same we have in our session storage.\n if (!this.isEditing && serverStateKey == storeStateKey) {\n stateData = JSON.parse(Storage.get(`course/${courseId}/staticState`));\n }\n if (!stateData) {\n stateData = await this.getServerCourseState();\n }\n\n } catch (error) {\n log.error(\"EXCEPTION RAISED WHILE INIT COURSE EDITOR\");\n log.error(error);\n return;\n }\n\n // The bulk editing only applies to the frontend and the state data is not created in the backend.\n stateData.bulk = {\n enabled: false,\n selectedType: '',\n selection: [],\n };\n\n this.setInitialState(stateData);\n\n // In editing mode, the session cache is considered dirty always.\n if (this.isEditing) {\n this.stateKey = null;\n } else {\n // Check if the last state is the same as the cached one.\n const newState = JSON.stringify(stateData);\n const previousState = Storage.get(`course/${courseId}/staticState`);\n if (previousState !== newState || storeStateKey !== serverStateKey) {\n Storage.set(`course/${courseId}/staticState`, newState);\n Storage.set(`course/${courseId}/stateKey`, stateData?.course?.statekey ?? serverStateKey);\n }\n this.stateKey = Storage.get(`course/${courseId}/stateKey`);\n }\n\n this._loadFileHandlers();\n }\n\n /**\n * Load the file hanlders promise.\n */\n _loadFileHandlers() {\n // Load the course file extensions.\n this._fileHandlersPromise = new Promise((resolve) => {\n if (!this.isEditing) {\n resolve([]);\n return;\n }\n // Check the cache.\n const handlersCacheKey = `course/${this.courseId}/fileHandlers`;\n\n const cacheValue = Storage.get(handlersCacheKey);\n if (cacheValue) {\n try {\n const cachedHandlers = JSON.parse(cacheValue);\n resolve(cachedHandlers);\n return;\n } catch (error) {\n log.error(\"ERROR PARSING CACHED FILE HANDLERS\");\n }\n }\n // Call file handlers webservice.\n ajax.call([{\n methodname: 'core_courseformat_file_handlers',\n args: {\n courseid: this.courseId,\n }\n }])[0].then((handlers) => {\n Storage.set(handlersCacheKey, JSON.stringify(handlers));\n resolve(handlers);\n return;\n }).catch(error => {\n log.error(error);\n resolve([]);\n return;\n });\n });\n }\n\n /**\n * Setup the current view settings\n *\n * @param {Object} setup format, page and course settings\n * @param {boolean} setup.editing if the page is in edit mode\n * @param {boolean} setup.supportscomponents if the format supports components for content\n * @param {string} setup.cacherev the backend cached state revision\n * @param {Array} setup.overriddenStrings optional overridden strings\n */\n setViewFormat(setup) {\n this._editing = setup.editing ?? false;\n this._supportscomponents = setup.supportscomponents ?? false;\n const overriddenStrings = setup.overriddenStrings ?? [];\n this._overriddenStrings = overriddenStrings.reduce(\n (indexed, currentValue) => indexed.set(currentValue.key, currentValue),\n new Map()\n );\n }\n\n /**\n * Execute a get string for a possible format overriden editor string.\n *\n * Return the proper getString promise for an editor string using the core_courseformat\n * of the format_PLUGINNAME compoment depending on the current view format setup.\n * @param {String} key the string key\n * @param {string|undefined} param The param for variable expansion in the string.\n * @returns {Promise} a getString promise\n */\n getFormatString(key, param) {\n if (this._overriddenStrings.has(key)) {\n const override = this._overriddenStrings.get(key);\n return getString(key, override.component ?? 'core_courseformat', param);\n }\n // All format overridable strings are from core_courseformat lang file.\n return getString(key, 'core_courseformat', param);\n }\n\n /**\n * Load the current course state from the server.\n *\n * @returns {Object} the current course state\n */\n async getServerCourseState() {\n const courseState = await ajax.call([{\n methodname: 'core_courseformat_get_state',\n args: {\n courseid: this.courseId,\n }\n }])[0];\n\n const stateData = JSON.parse(courseState);\n\n return {\n course: {},\n section: [],\n cm: [],\n ...stateData,\n };\n }\n\n /**\n * Return the current edit mode.\n *\n * Components should use this method to check if edit mode is active.\n *\n * @return {boolean} if edit is enabled\n */\n get isEditing() {\n return this._editing ?? false;\n }\n\n /**\n * Return a data exporter to transform state part into mustache contexts.\n *\n * @return {Exporter} the exporter class\n */\n getExporter() {\n return new Exporter(this);\n }\n\n /**\n * Return if the current course support components to refresh the content.\n *\n * @returns {boolean} if the current content support components\n */\n get supportComponents() {\n return this._supportscomponents ?? false;\n }\n\n /**\n * Return the course file handlers promise.\n * @returns {Promise} the promise for file handlers.\n */\n async getFileHandlersPromise() {\n return this._fileHandlersPromise ?? [];\n }\n\n /**\n * Upload a file list to the course.\n *\n * This method is a wrapper to the course file uploader.\n *\n * @param {number} sectionId the section id\n * @param {number} sectionNum the section number\n * @param {Array} files and array of files\n * @return {Promise} the file queue promise\n */\n uploadFiles(sectionId, sectionNum, files) {\n return uploadFilesToCourse(this.courseId, sectionId, sectionNum, files);\n }\n\n /**\n * Get a value from the course editor static storage if any.\n *\n * The course editor static storage uses the sessionStorage to store values from the\n * components. This is used to prevent unnecesary template loadings on every page. However,\n * the storage does not work if no sessionStorage can be used (in debug mode for example),\n * if the page is in editing mode or if the initial state change from the last page.\n *\n * @param {string} key the key to get\n * @return {boolean|string} the storage value or false if cannot be loaded\n */\n getStorageValue(key) {\n if (this.isEditing || !this.stateKey) {\n return false;\n }\n const dataJson = Storage.get(`course/${this.courseId}/${key}`);\n if (!dataJson) {\n return false;\n }\n // Check the stateKey.\n try {\n const data = JSON.parse(dataJson);\n if (data?.stateKey !== this.stateKey) {\n return false;\n }\n return data.value;\n } catch (error) {\n return false;\n }\n }\n\n /**\n * Stores a value into the course editor static storage if available\n *\n * @param {String} key the key to store\n * @param {*} value the value to store (must be compatible with JSON,stringify)\n * @returns {boolean} true if the value is stored\n */\n setStorageValue(key, value) {\n // Values cannot be stored on edit mode.\n if (this.isEditing) {\n return false;\n }\n const data = {\n stateKey: this.stateKey,\n value,\n };\n return Storage.set(`course/${this.courseId}/${key}`, JSON.stringify(data));\n }\n\n /**\n * Convert a file dragging event into a proper dragging file list.\n * @param {DataTransfer} dataTransfer the event to convert\n * @return {Array} of file list info.\n */\n getFilesDraggableData(dataTransfer) {\n const exporter = this.getExporter();\n return exporter.fileDraggableData(this.state, dataTransfer);\n }\n\n /**\n * Dispatch a change in the state.\n *\n * Usually reactive modules throw an error directly to the components when something\n * goes wrong. However, course editor can directly display a notification.\n *\n * @method dispatch\n * @param {mixed} args any number of params the mutation needs.\n */\n async dispatch(...args) {\n try {\n await super.dispatch(...args);\n } catch (error) {\n // Display error modal.\n notification.exception(error);\n // Force unlock all elements.\n super.dispatch('unlockAll');\n }\n }\n}\n"],"names":["Reactive","courseId","serverStateKey","this","Error","stateData","Date","now","_editing","_supportscomponents","_fileHandlers","storeStateKey","Storage","get","isEditing","JSON","parse","getServerCourseState","error","bulk","enabled","selectedType","selection","setInitialState","stateKey","newState","stringify","set","_stateData","course","_stateData$course","statekey","_loadFileHandlers","_fileHandlersPromise","Promise","resolve","handlersCacheKey","cacheValue","cachedHandlers","call","methodname","args","courseid","then","handlers","catch","setViewFormat","setup","editing","supportscomponents","overriddenStrings","_overriddenStrings","reduce","indexed","currentValue","key","Map","getFormatString","param","has","override","component","courseState","ajax","section","cm","getExporter","Exporter","supportComponents","uploadFiles","sectionId","sectionNum","files","getStorageValue","dataJson","data","value","setStorageValue","getFilesDraggableData","dataTransfer","fileDraggableData","state","super","dispatch","exception"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"courseeditor.min.js","sources":["../../../src/local/courseeditor/courseeditor.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\nimport {getString} from 'core/str';\nimport {Reactive} from 'core/reactive';\nimport notification from 'core/notification';\nimport Exporter from 'core_courseformat/local/courseeditor/exporter';\nimport log from 'core/log';\nimport ajax from 'core/ajax';\nimport * as Storage from 'core/sessionstorage';\nimport {uploadFilesToCourse} from 'core_courseformat/local/courseeditor/fileuploader';\n\n/**\n * Main course editor module.\n *\n * All formats can register new components on this object to create new reactive\n * UI components that watch the current course state.\n *\n * @module core_courseformat/local/courseeditor/courseeditor\n * @class core_courseformat/local/courseeditor/courseeditor\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class extends Reactive {\n\n /**\n * The current state cache key\n *\n * The state cache is considered dirty if the state changes from the last page or\n * if the page has editing mode on.\n *\n * @attribute stateKey\n * @type number|null\n * @default 1\n * @package\n */\n stateKey = 1;\n\n /**\n * The current page section return\n * @attribute sectionReturn\n * @type number\n * @default 0\n */\n sectionReturn = 0;\n\n /**\n * Set up the course editor when the page is ready.\n *\n * The course can only be loaded once per instance. Otherwise an error is thrown.\n *\n * The backend can inform the module of the current state key. This key changes every time some\n * update in the course affect the current user state. Some examples are:\n * - The course content has been edited\n * - The user marks some activity as completed\n * - The user collapses or uncollapses a section (it is stored as a user preference)\n *\n * @param {number} courseId course id\n * @param {string} serverStateKey the current backend course cache reference\n */\n async loadCourse(courseId, serverStateKey) {\n\n if (this.courseId) {\n throw new Error(`Cannot load ${courseId}, course already loaded with id ${this.courseId}`);\n }\n\n if (!serverStateKey) {\n // The server state key is not provided, we use a invalid statekey to force reloading.\n serverStateKey = `invalidStateKey_${Date.now()}`;\n }\n\n // Default view format setup.\n this._editing = false;\n this._supportscomponents = false;\n this._fileHandlers = null;\n\n this.courseId = courseId;\n\n let stateData;\n\n const storeStateKey = Storage.get(`course/${courseId}/stateKey`);\n try {\n // Check if the backend state key is the same we have in our session storage.\n if (!this.isEditing && serverStateKey == storeStateKey) {\n stateData = JSON.parse(Storage.get(`course/${courseId}/staticState`));\n }\n if (!stateData) {\n stateData = await this.getServerCourseState();\n }\n\n } catch (error) {\n log.error(\"EXCEPTION RAISED WHILE INIT COURSE EDITOR\");\n log.error(error);\n return;\n }\n\n // The bulk editing only applies to the frontend and the state data is not created in the backend.\n stateData.bulk = {\n enabled: false,\n selectedType: '',\n selection: [],\n };\n\n this.setInitialState(stateData);\n\n // In editing mode, the session cache is considered dirty always.\n if (this.isEditing) {\n this.stateKey = null;\n } else {\n // Check if the last state is the same as the cached one.\n const newState = JSON.stringify(stateData);\n const previousState = Storage.get(`course/${courseId}/staticState`);\n if (previousState !== newState || storeStateKey !== serverStateKey) {\n Storage.set(`course/${courseId}/staticState`, newState);\n Storage.set(`course/${courseId}/stateKey`, stateData?.course?.statekey ?? serverStateKey);\n }\n this.stateKey = Storage.get(`course/${courseId}/stateKey`);\n }\n\n this._loadFileHandlers();\n }\n\n /**\n * Load the file hanlders promise.\n */\n _loadFileHandlers() {\n // Load the course file extensions.\n this._fileHandlersPromise = new Promise((resolve) => {\n if (!this.isEditing) {\n resolve([]);\n return;\n }\n // Check the cache.\n const handlersCacheKey = `course/${this.courseId}/fileHandlers`;\n\n const cacheValue = Storage.get(handlersCacheKey);\n if (cacheValue) {\n try {\n const cachedHandlers = JSON.parse(cacheValue);\n resolve(cachedHandlers);\n return;\n } catch (error) {\n log.error(\"ERROR PARSING CACHED FILE HANDLERS\");\n }\n }\n // Call file handlers webservice.\n ajax.call([{\n methodname: 'core_courseformat_file_handlers',\n args: {\n courseid: this.courseId,\n }\n }])[0].then((handlers) => {\n Storage.set(handlersCacheKey, JSON.stringify(handlers));\n resolve(handlers);\n return;\n }).catch(error => {\n log.error(error);\n resolve([]);\n return;\n });\n });\n }\n\n /**\n * Setup the current view settings\n *\n * @param {Object} setup format, page and course settings\n * @param {boolean} setup.editing if the page is in edit mode\n * @param {boolean} setup.supportscomponents if the format supports components for content\n * @param {string} setup.cacherev the backend cached state revision\n * @param {Array} setup.overriddenStrings optional overridden strings\n */\n setViewFormat(setup) {\n this._editing = setup.editing ?? false;\n this._supportscomponents = setup.supportscomponents ?? false;\n const overriddenStrings = setup.overriddenStrings ?? [];\n this._overriddenStrings = overriddenStrings.reduce(\n (indexed, currentValue) => indexed.set(currentValue.key, currentValue),\n new Map()\n );\n }\n\n /**\n * Execute a get string for a possible format overriden editor string.\n *\n * Return the proper getString promise for an editor string using the core_courseformat\n * of the format_PLUGINNAME compoment depending on the current view format setup.\n * @param {String} key the string key\n * @param {string|undefined} param The param for variable expansion in the string.\n * @returns {Promise} a getString promise\n */\n getFormatString(key, param) {\n if (this._overriddenStrings.has(key)) {\n const override = this._overriddenStrings.get(key);\n return getString(key, override.component ?? 'core_courseformat', param);\n }\n // All format overridable strings are from core_courseformat lang file.\n return getString(key, 'core_courseformat', param);\n }\n\n /**\n * Load the current course state from the server.\n *\n * @returns {Object} the current course state\n */\n async getServerCourseState() {\n const courseState = await ajax.call([{\n methodname: 'core_courseformat_get_state',\n args: {\n courseid: this.courseId,\n }\n }])[0];\n\n const stateData = JSON.parse(courseState);\n\n return {\n course: {},\n section: [],\n cm: [],\n ...stateData,\n };\n }\n\n /**\n * Return the current edit mode.\n *\n * Components should use this method to check if edit mode is active.\n *\n * @return {boolean} if edit is enabled\n */\n get isEditing() {\n return this._editing ?? false;\n }\n\n /**\n * Return a data exporter to transform state part into mustache contexts.\n *\n * @return {Exporter} the exporter class\n */\n getExporter() {\n return new Exporter(this);\n }\n\n /**\n * Return if the current course support components to refresh the content.\n *\n * @returns {boolean} if the current content support components\n */\n get supportComponents() {\n return this._supportscomponents ?? false;\n }\n\n /**\n * Return the course file handlers promise.\n * @returns {Promise} the promise for file handlers.\n */\n async getFileHandlersPromise() {\n return this._fileHandlersPromise ?? [];\n }\n\n /**\n * Upload a file list to the course.\n *\n * This method is a wrapper to the course file uploader.\n *\n * @param {number} sectionId the section id\n * @param {number} sectionNum the section number\n * @param {Array} files and array of files\n * @return {Promise} the file queue promise\n */\n uploadFiles(sectionId, sectionNum, files) {\n return uploadFilesToCourse(this.courseId, sectionId, sectionNum, files);\n }\n\n /**\n * Get a value from the course editor static storage if any.\n *\n * The course editor static storage uses the sessionStorage to store values from the\n * components. This is used to prevent unnecesary template loadings on every page. However,\n * the storage does not work if no sessionStorage can be used (in debug mode for example),\n * if the page is in editing mode or if the initial state change from the last page.\n *\n * @param {string} key the key to get\n * @return {boolean|string} the storage value or false if cannot be loaded\n */\n getStorageValue(key) {\n if (this.isEditing || !this.stateKey) {\n return false;\n }\n const dataJson = Storage.get(`course/${this.courseId}/${key}`);\n if (!dataJson) {\n return false;\n }\n // Check the stateKey.\n try {\n const data = JSON.parse(dataJson);\n if (data?.stateKey !== this.stateKey) {\n return false;\n }\n return data.value;\n } catch (error) {\n return false;\n }\n }\n\n /**\n * Stores a value into the course editor static storage if available\n *\n * @param {String} key the key to store\n * @param {*} value the value to store (must be compatible with JSON,stringify)\n * @returns {boolean} true if the value is stored\n */\n setStorageValue(key, value) {\n // Values cannot be stored on edit mode.\n if (this.isEditing) {\n return false;\n }\n const data = {\n stateKey: this.stateKey,\n value,\n };\n return Storage.set(`course/${this.courseId}/${key}`, JSON.stringify(data));\n }\n\n /**\n * Convert a file dragging event into a proper dragging file list.\n * @param {DataTransfer} dataTransfer the event to convert\n * @return {Array} of file list info.\n */\n getFilesDraggableData(dataTransfer) {\n const exporter = this.getExporter();\n return exporter.fileDraggableData(this.state, dataTransfer);\n }\n\n /**\n * Dispatch a change in the state.\n *\n * Usually reactive modules throw an error directly to the components when something\n * goes wrong. However, course editor can directly display a notification.\n *\n * @method dispatch\n * @param {mixed} args any number of params the mutation needs.\n */\n async dispatch(...args) {\n try {\n await super.dispatch(...args);\n } catch (error) {\n // Display error modal.\n notification.exception(error);\n // Force unlock all elements.\n super.dispatch('unlockAll');\n }\n }\n}\n"],"names":["Reactive","courseId","serverStateKey","this","Error","stateData","Date","now","_editing","_supportscomponents","_fileHandlers","storeStateKey","Storage","get","isEditing","JSON","parse","getServerCourseState","error","bulk","enabled","selectedType","selection","setInitialState","stateKey","newState","stringify","set","_stateData","course","_stateData$course","statekey","_loadFileHandlers","_fileHandlersPromise","Promise","resolve","handlersCacheKey","cacheValue","cachedHandlers","call","methodname","args","courseid","then","handlers","catch","setViewFormat","setup","editing","supportscomponents","overriddenStrings","_overriddenStrings","reduce","indexed","currentValue","key","Map","getFormatString","param","has","override","component","courseState","ajax","section","cm","getExporter","Exporter","supportComponents","uploadFiles","sectionId","sectionNum","files","getStorageValue","dataJson","data","value","setStorageValue","getFilesDraggableData","dataTransfer","fileDraggableData","state","super","dispatch","exception"],"mappings":"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"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/dndsection.min.js b/course/format/amd/build/local/courseeditor/dndsection.min.js index ddc81925316..dc0320092d2 100644 --- a/course/format/amd/build/local/courseeditor/dndsection.min.js +++ b/course/format/amd/build/local/courseeditor/dndsection.min.js @@ -9,6 +9,6 @@ define("core_courseformat/local/courseeditor/dndsection",["exports","core/reacti * @class core_courseformat/local/courseeditor/dndsection * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj},(0,_prefetch.prefetchStrings)("core",["addfilehere"]);class _default extends _reactive.BaseComponent{configState(state){this.id=this.element.dataset.id,this.section=state.section.get(this.id),this.course=state.course}configDragDrop(sectionitem){this.reactive.isEditing&&this.reactive.supportComponents&&(this.sectionitem=sectionitem,this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.sectionitem&&this.sectionitem.unregister(),void 0!==this.dragdrop&&this.dragdrop.unregister()}getLastCm(){return null}dragStart(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!1)}validateDropData(dropdata){if("files"===(null==dropdata?void 0:dropdata.type))return!0;if("cm"===(null==dropdata?void 0:dropdata.type))return!0;if("section"===(null==dropdata?void 0:dropdata.type)){const sectionzeroid=this.course.sectionlist[0];return(null==dropdata?void 0:dropdata.id)!=this.id&&(null==dropdata?void 0:dropdata.id)!=sectionzeroid&&this.id!=sectionzeroid}return!1}showDropZone(dropdata){var _this$getLastCm;("files"==dropdata.type&&this.addOverlay({content:(0,_str.get_string)("addfilehere","core"),icon:_templates.default.renderPix("t/download","core")}).then((()=>{var _this$dragdrop;null!==(_this$dragdrop=this.dragdrop)&&void 0!==_this$dragdrop&&_this$dragdrop.isDropzoneVisible()||this.removeOverlay()})).catch((error=>{throw error})),"cm"==dropdata.type)&&(null===(_this$getLastCm=this.getLastCm())||void 0===_this$getLastCm||_this$getLastCm.classList.add(this.classes.DROPDOWN));"section"==dropdata.type&&(this.section.number>dropdata.number?(this.element.classList.remove(this.classes.DROPUP),this.element.classList.add(this.classes.DROPDOWN)):(this.element.classList.add(this.classes.DROPUP),this.element.classList.remove(this.classes.DROPDOWN)))}hideDropZone(){var _this$getLastCm2;null===(_this$getLastCm2=this.getLastCm())||void 0===_this$getLastCm2||_this$getLastCm2.classList.remove(this.classes.DROPDOWN),this.element.classList.remove(this.classes.DROPUP),this.element.classList.remove(this.classes.DROPDOWN),this.removeOverlay()}drop(dropdata,event){if("files"!=dropdata.type){if("cm"==dropdata.type){const mutation=event.altKey?"cmDuplicate":"cmMove";this.reactive.dispatch(mutation,[dropdata.id],this.id)}"section"==dropdata.type&&this.reactive.dispatch("sectionMove",[dropdata.id],this.id)}else this.reactive.uploadFiles(this.section.id,this.section.number,dropdata.files)}}return _exports.default=_default,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj},(0,_prefetch.prefetchStrings)("core",["addfilehere"]);class _default extends _reactive.BaseComponent{configState(state){this.id=this.element.dataset.id,this.section=state.section.get(this.id),this.course=state.course}configDragDrop(sectionitem){this.reactive.isEditing&&this.reactive.supportComponents&&(this.sectionitem=sectionitem,this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.sectionitem&&this.sectionitem.unregister(),void 0!==this.dragdrop&&this.dragdrop.unregister()}getLastCm(){return null}dragStart(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!1)}validateDropData(dropdata){if("files"===(null==dropdata?void 0:dropdata.type))return!0;if("cm"===(null==dropdata?void 0:dropdata.type))return!0;if("section"===(null==dropdata?void 0:dropdata.type)){const sectionzeroid=this.course.sectionlist[0];return(null==dropdata?void 0:dropdata.id)!=this.id&&(null==dropdata?void 0:dropdata.id)!=sectionzeroid&&this.id!=sectionzeroid}return!1}showDropZone(dropdata){var _this$getLastCm;("files"==dropdata.type&&this.addOverlay({content:(0,_str.getString)("addfilehere","core"),icon:_templates.default.renderPix("t/download","core")}).then((()=>{var _this$dragdrop;null!==(_this$dragdrop=this.dragdrop)&&void 0!==_this$dragdrop&&_this$dragdrop.isDropzoneVisible()||this.removeOverlay()})).catch((error=>{throw error})),"cm"==dropdata.type)&&(null===(_this$getLastCm=this.getLastCm())||void 0===_this$getLastCm||_this$getLastCm.classList.add(this.classes.DROPDOWN));"section"==dropdata.type&&(this.section.number>dropdata.number?(this.element.classList.remove(this.classes.DROPUP),this.element.classList.add(this.classes.DROPDOWN)):(this.element.classList.add(this.classes.DROPUP),this.element.classList.remove(this.classes.DROPDOWN)))}hideDropZone(){var _this$getLastCm2;null===(_this$getLastCm2=this.getLastCm())||void 0===_this$getLastCm2||_this$getLastCm2.classList.remove(this.classes.DROPDOWN),this.element.classList.remove(this.classes.DROPUP),this.element.classList.remove(this.classes.DROPDOWN),this.removeOverlay()}drop(dropdata,event){if("files"!=dropdata.type){if("cm"==dropdata.type){const mutation=event.altKey?"cmDuplicate":"cmMove";this.reactive.dispatch(mutation,[dropdata.id],this.id)}"section"==dropdata.type&&this.reactive.dispatch("sectionMove",[dropdata.id],this.id)}else this.reactive.uploadFiles(this.section.id,this.section.number,dropdata.files)}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=dndsection.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/dndsection.min.js.map b/course/format/amd/build/local/courseeditor/dndsection.min.js.map index c1a8e81d4e3..8522f3f407a 100644 --- a/course/format/amd/build/local/courseeditor/dndsection.min.js.map +++ b/course/format/amd/build/local/courseeditor/dndsection.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dndsection.min.js","sources":["../../../src/local/courseeditor/dndsection.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 * Course index section component.\n *\n * This component is used to control specific course section interactions like drag and drop\n * in both course index and course content.\n *\n * @module core_courseformat/local/courseeditor/dndsection\n * @class core_courseformat/local/courseeditor/dndsection\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\nimport {get_string as getString} from 'core/str';\nimport {prefetchStrings} from 'core/prefetch';\nimport Templates from 'core/templates';\n\n// Load global strings.\nprefetchStrings('core', ['addfilehere']);\n\nexport default class extends BaseComponent {\n\n /**\n * Save some values form the state.\n *\n * @param {Object} state the current state\n */\n configState(state) {\n this.id = this.element.dataset.id;\n this.section = state.section.get(this.id);\n this.course = state.course;\n }\n\n /**\n * Register state values and the drag and drop subcomponent.\n *\n * @param {BaseComponent} sectionitem section item component\n */\n configDragDrop(sectionitem) {\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init the inner dragable element.\n this.sectionitem = sectionitem;\n // Init the dropzone.\n this.dragdrop = new DragDrop(this);\n // Save dropzone classes.\n this.classes = this.dragdrop.getClasses();\n }\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.sectionitem !== undefined) {\n this.sectionitem.unregister();\n }\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n /**\n * Get the last CM element of that section.\n *\n * @returns {element|null} the las course module element of the section.\n */\n getLastCm() {\n return null;\n }\n\n // Drag and drop methods.\n\n /**\n * The element drop start hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragStart(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], true);\n }\n\n /**\n * The element drop end hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragEnd(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], false);\n }\n\n /**\n * Validate if the drop data can be dropped over the component.\n *\n * @param {Object} dropdata the exported drop data.\n * @returns {boolean}\n */\n validateDropData(dropdata) {\n // We accept files.\n if (dropdata?.type === 'files') {\n return true;\n }\n // We accept any course module.\n if (dropdata?.type === 'cm') {\n return true;\n }\n // We accept any section but the section 0 or ourself\n if (dropdata?.type === 'section') {\n const sectionzeroid = this.course.sectionlist[0];\n return dropdata?.id != this.id && dropdata?.id != sectionzeroid && this.id != sectionzeroid;\n }\n return false;\n }\n\n /**\n * Display the component dropzone.\n *\n * @param {Object} dropdata the accepted drop data\n */\n showDropZone(dropdata) {\n if (dropdata.type == 'files') {\n this.addOverlay({\n content: getString('addfilehere', 'core'),\n icon: Templates.renderPix('t/download', 'core'),\n }).then(() => {\n // Check if we still need the file dropzone.\n if (!this.dragdrop?.isDropzoneVisible()) {\n this.removeOverlay();\n }\n return;\n }).catch((error) => {\n throw error;\n });\n }\n if (dropdata.type == 'cm') {\n this.getLastCm()?.classList.add(this.classes.DROPDOWN);\n }\n if (dropdata.type == 'section') {\n // The relative move of section depends on the section number.\n if (this.section.number > dropdata.number) {\n this.element.classList.remove(this.classes.DROPUP);\n this.element.classList.add(this.classes.DROPDOWN);\n } else {\n this.element.classList.add(this.classes.DROPUP);\n this.element.classList.remove(this.classes.DROPDOWN);\n }\n }\n }\n\n /**\n * Hide the component dropzone.\n */\n hideDropZone() {\n this.getLastCm()?.classList.remove(this.classes.DROPDOWN);\n this.element.classList.remove(this.classes.DROPUP);\n this.element.classList.remove(this.classes.DROPDOWN);\n this.removeOverlay();\n }\n\n /**\n * Drop event handler.\n *\n * @param {Object} dropdata the accepted drop data\n * @param {Event} event the drop event\n */\n drop(dropdata, event) {\n // File handling.\n if (dropdata.type == 'files') {\n this.reactive.uploadFiles(\n this.section.id,\n this.section.number,\n dropdata.files\n );\n return;\n }\n // Call the move mutation.\n if (dropdata.type == 'cm') {\n const mutation = (event.altKey) ? 'cmDuplicate' : 'cmMove';\n this.reactive.dispatch(mutation, [dropdata.id], this.id);\n }\n if (dropdata.type == 'section') {\n this.reactive.dispatch('sectionMove', [dropdata.id], this.id);\n }\n }\n}\n"],"names":["BaseComponent","configState","state","id","this","element","dataset","section","get","course","configDragDrop","sectionitem","reactive","isEditing","supportComponents","dragdrop","DragDrop","classes","getClasses","destroy","undefined","unregister","getLastCm","dragStart","dropdata","dispatch","dragEnd","validateDropData","type","sectionzeroid","sectionlist","showDropZone","addOverlay","content","icon","Templates","renderPix","then","_this$dragdrop","isDropzoneVisible","removeOverlay","catch","error","classList","add","DROPDOWN","number","remove","DROPUP","hideDropZone","drop","event","mutation","altKey","uploadFiles","files"],"mappings":";;;;;;;;;;;iLAiCgB,OAAQ,CAAC,uCAEIA,wBAOzBC,YAAYC,YACHC,GAAKC,KAAKC,QAAQC,QAAQH,QAC1BI,QAAUL,MAAMK,QAAQC,IAAIJ,KAAKD,SACjCM,OAASP,MAAMO,OAQxBC,eAAeC,aAEPP,KAAKQ,SAASC,WAAaT,KAAKQ,SAASE,yBAEpCH,YAAcA,iBAEdI,SAAW,IAAIC,mBAASZ,WAExBa,QAAUb,KAAKW,SAASG,cAOrCC,eAC6BC,IAArBhB,KAAKO,kBACAA,YAAYU,kBAECD,IAAlBhB,KAAKW,eACAA,SAASM,aAStBC,mBACW,KAUXC,UAAUC,eACDZ,SAASa,SAAS,cAAe,CAACD,SAASrB,KAAK,GAQzDuB,QAAQF,eACCZ,SAASa,SAAS,cAAe,CAACD,SAASrB,KAAK,GASzDwB,iBAAiBH,aAEU,WAAnBA,MAAAA,gBAAAA,SAAUI,aACH,KAGY,QAAnBJ,MAAAA,gBAAAA,SAAUI,aACH,KAGY,aAAnBJ,MAAAA,gBAAAA,SAAUI,MAAoB,OACxBC,cAAgBzB,KAAKK,OAAOqB,YAAY,UACvCN,MAAAA,gBAAAA,SAAUrB,KAAMC,KAAKD,KAAMqB,MAAAA,gBAAAA,SAAUrB,KAAM0B,eAAiBzB,KAAKD,IAAM0B,qBAE3E,EAQXE,aAAaP,+BACY,SAAjBA,SAASI,WACJI,WAAW,CACZC,SAAS,mBAAU,cAAe,QAClCC,KAAMC,mBAAUC,UAAU,aAAc,UACzCC,MAAK,+CAECjC,KAAKW,oCAALuB,eAAeC,0BACXC,mBAGVC,OAAOC,cACAA,SAGO,MAAjBlB,SAASI,qCACJN,wDAAaqB,UAAUC,IAAIxC,KAAKa,QAAQ4B,WAE5B,WAAjBrB,SAASI,OAELxB,KAAKG,QAAQuC,OAAStB,SAASsB,aAC1BzC,QAAQsC,UAAUI,OAAO3C,KAAKa,QAAQ+B,aACtC3C,QAAQsC,UAAUC,IAAIxC,KAAKa,QAAQ4B,iBAEnCxC,QAAQsC,UAAUC,IAAIxC,KAAKa,QAAQ+B,aACnC3C,QAAQsC,UAAUI,OAAO3C,KAAKa,QAAQ4B,YAQvDI,kEACS3B,0DAAaqB,UAAUI,OAAO3C,KAAKa,QAAQ4B,eAC3CxC,QAAQsC,UAAUI,OAAO3C,KAAKa,QAAQ+B,aACtC3C,QAAQsC,UAAUI,OAAO3C,KAAKa,QAAQ4B,eACtCL,gBASTU,KAAK1B,SAAU2B,UAEU,SAAjB3B,SAASI,SASQ,MAAjBJ,SAASI,KAAc,OACjBwB,SAAYD,MAAME,OAAU,cAAgB,cAC7CzC,SAASa,SAAS2B,SAAU,CAAC5B,SAASrB,IAAKC,KAAKD,IAEpC,WAAjBqB,SAASI,WACJhB,SAASa,SAAS,cAAe,CAACD,SAASrB,IAAKC,KAAKD,cAbrDS,SAAS0C,YACVlD,KAAKG,QAAQJ,GACbC,KAAKG,QAAQuC,OACbtB,SAAS+B"} \ No newline at end of file +{"version":3,"file":"dndsection.min.js","sources":["../../../src/local/courseeditor/dndsection.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 * Course index section component.\n *\n * This component is used to control specific course section interactions like drag and drop\n * in both course index and course content.\n *\n * @module core_courseformat/local/courseeditor/dndsection\n * @class core_courseformat/local/courseeditor/dndsection\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\nimport {getString} from 'core/str';\nimport {prefetchStrings} from 'core/prefetch';\nimport Templates from 'core/templates';\n\n// Load global strings.\nprefetchStrings('core', ['addfilehere']);\n\nexport default class extends BaseComponent {\n\n /**\n * Save some values form the state.\n *\n * @param {Object} state the current state\n */\n configState(state) {\n this.id = this.element.dataset.id;\n this.section = state.section.get(this.id);\n this.course = state.course;\n }\n\n /**\n * Register state values and the drag and drop subcomponent.\n *\n * @param {BaseComponent} sectionitem section item component\n */\n configDragDrop(sectionitem) {\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init the inner dragable element.\n this.sectionitem = sectionitem;\n // Init the dropzone.\n this.dragdrop = new DragDrop(this);\n // Save dropzone classes.\n this.classes = this.dragdrop.getClasses();\n }\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.sectionitem !== undefined) {\n this.sectionitem.unregister();\n }\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n /**\n * Get the last CM element of that section.\n *\n * @returns {element|null} the las course module element of the section.\n */\n getLastCm() {\n return null;\n }\n\n // Drag and drop methods.\n\n /**\n * The element drop start hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragStart(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], true);\n }\n\n /**\n * The element drop end hook.\n *\n * @param {Object} dropdata the dropdata\n */\n dragEnd(dropdata) {\n this.reactive.dispatch('sectionDrag', [dropdata.id], false);\n }\n\n /**\n * Validate if the drop data can be dropped over the component.\n *\n * @param {Object} dropdata the exported drop data.\n * @returns {boolean}\n */\n validateDropData(dropdata) {\n // We accept files.\n if (dropdata?.type === 'files') {\n return true;\n }\n // We accept any course module.\n if (dropdata?.type === 'cm') {\n return true;\n }\n // We accept any section but the section 0 or ourself\n if (dropdata?.type === 'section') {\n const sectionzeroid = this.course.sectionlist[0];\n return dropdata?.id != this.id && dropdata?.id != sectionzeroid && this.id != sectionzeroid;\n }\n return false;\n }\n\n /**\n * Display the component dropzone.\n *\n * @param {Object} dropdata the accepted drop data\n */\n showDropZone(dropdata) {\n if (dropdata.type == 'files') {\n this.addOverlay({\n content: getString('addfilehere', 'core'),\n icon: Templates.renderPix('t/download', 'core'),\n }).then(() => {\n // Check if we still need the file dropzone.\n if (!this.dragdrop?.isDropzoneVisible()) {\n this.removeOverlay();\n }\n return;\n }).catch((error) => {\n throw error;\n });\n }\n if (dropdata.type == 'cm') {\n this.getLastCm()?.classList.add(this.classes.DROPDOWN);\n }\n if (dropdata.type == 'section') {\n // The relative move of section depends on the section number.\n if (this.section.number > dropdata.number) {\n this.element.classList.remove(this.classes.DROPUP);\n this.element.classList.add(this.classes.DROPDOWN);\n } else {\n this.element.classList.add(this.classes.DROPUP);\n this.element.classList.remove(this.classes.DROPDOWN);\n }\n }\n }\n\n /**\n * Hide the component dropzone.\n */\n hideDropZone() {\n this.getLastCm()?.classList.remove(this.classes.DROPDOWN);\n this.element.classList.remove(this.classes.DROPUP);\n this.element.classList.remove(this.classes.DROPDOWN);\n this.removeOverlay();\n }\n\n /**\n * Drop event handler.\n *\n * @param {Object} dropdata the accepted drop data\n * @param {Event} event the drop event\n */\n drop(dropdata, event) {\n // File handling.\n if (dropdata.type == 'files') {\n this.reactive.uploadFiles(\n this.section.id,\n this.section.number,\n dropdata.files\n );\n return;\n }\n // Call the move mutation.\n if (dropdata.type == 'cm') {\n const mutation = (event.altKey) ? 'cmDuplicate' : 'cmMove';\n this.reactive.dispatch(mutation, [dropdata.id], this.id);\n }\n if (dropdata.type == 'section') {\n this.reactive.dispatch('sectionMove', [dropdata.id], this.id);\n }\n }\n}\n"],"names":["BaseComponent","configState","state","id","this","element","dataset","section","get","course","configDragDrop","sectionitem","reactive","isEditing","supportComponents","dragdrop","DragDrop","classes","getClasses","destroy","undefined","unregister","getLastCm","dragStart","dropdata","dispatch","dragEnd","validateDropData","type","sectionzeroid","sectionlist","showDropZone","addOverlay","content","icon","Templates","renderPix","then","_this$dragdrop","isDropzoneVisible","removeOverlay","catch","error","classList","add","DROPDOWN","number","remove","DROPUP","hideDropZone","drop","event","mutation","altKey","uploadFiles","files"],"mappings":";;;;;;;;;;;iLAiCgB,OAAQ,CAAC,uCAEIA,wBAOzBC,YAAYC,YACHC,GAAKC,KAAKC,QAAQC,QAAQH,QAC1BI,QAAUL,MAAMK,QAAQC,IAAIJ,KAAKD,SACjCM,OAASP,MAAMO,OAQxBC,eAAeC,aAEPP,KAAKQ,SAASC,WAAaT,KAAKQ,SAASE,yBAEpCH,YAAcA,iBAEdI,SAAW,IAAIC,mBAASZ,WAExBa,QAAUb,KAAKW,SAASG,cAOrCC,eAC6BC,IAArBhB,KAAKO,kBACAA,YAAYU,kBAECD,IAAlBhB,KAAKW,eACAA,SAASM,aAStBC,mBACW,KAUXC,UAAUC,eACDZ,SAASa,SAAS,cAAe,CAACD,SAASrB,KAAK,GAQzDuB,QAAQF,eACCZ,SAASa,SAAS,cAAe,CAACD,SAASrB,KAAK,GASzDwB,iBAAiBH,aAEU,WAAnBA,MAAAA,gBAAAA,SAAUI,aACH,KAGY,QAAnBJ,MAAAA,gBAAAA,SAAUI,aACH,KAGY,aAAnBJ,MAAAA,gBAAAA,SAAUI,MAAoB,OACxBC,cAAgBzB,KAAKK,OAAOqB,YAAY,UACvCN,MAAAA,gBAAAA,SAAUrB,KAAMC,KAAKD,KAAMqB,MAAAA,gBAAAA,SAAUrB,KAAM0B,eAAiBzB,KAAKD,IAAM0B,qBAE3E,EAQXE,aAAaP,+BACY,SAAjBA,SAASI,WACJI,WAAW,CACZC,SAAS,kBAAU,cAAe,QAClCC,KAAMC,mBAAUC,UAAU,aAAc,UACzCC,MAAK,+CAECjC,KAAKW,oCAALuB,eAAeC,0BACXC,mBAGVC,OAAOC,cACAA,SAGO,MAAjBlB,SAASI,qCACJN,wDAAaqB,UAAUC,IAAIxC,KAAKa,QAAQ4B,WAE5B,WAAjBrB,SAASI,OAELxB,KAAKG,QAAQuC,OAAStB,SAASsB,aAC1BzC,QAAQsC,UAAUI,OAAO3C,KAAKa,QAAQ+B,aACtC3C,QAAQsC,UAAUC,IAAIxC,KAAKa,QAAQ4B,iBAEnCxC,QAAQsC,UAAUC,IAAIxC,KAAKa,QAAQ+B,aACnC3C,QAAQsC,UAAUI,OAAO3C,KAAKa,QAAQ4B,YAQvDI,kEACS3B,0DAAaqB,UAAUI,OAAO3C,KAAKa,QAAQ4B,eAC3CxC,QAAQsC,UAAUI,OAAO3C,KAAKa,QAAQ+B,aACtC3C,QAAQsC,UAAUI,OAAO3C,KAAKa,QAAQ4B,eACtCL,gBASTU,KAAK1B,SAAU2B,UAEU,SAAjB3B,SAASI,SASQ,MAAjBJ,SAASI,KAAc,OACjBwB,SAAYD,MAAME,OAAU,cAAgB,cAC7CzC,SAASa,SAAS2B,SAAU,CAAC5B,SAASrB,IAAKC,KAAKD,IAEpC,WAAjBqB,SAASI,WACJhB,SAASa,SAAS,cAAe,CAACD,SAASrB,IAAKC,KAAKD,cAbrDS,SAAS0C,YACVlD,KAAKG,QAAQJ,GACbC,KAAKG,QAAQuC,OACbtB,SAAS+B"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/fileuploader.min.js b/course/format/amd/build/local/courseeditor/fileuploader.min.js index 308e86a85c1..d21051f0bf5 100644 --- a/course/format/amd/build/local/courseeditor/fileuploader.min.js +++ b/course/format/amd/build/local/courseeditor/fileuploader.min.js @@ -1,3 +1,3 @@ -define("core_courseformat/local/courseeditor/fileuploader",["exports","core/config","core/modal_factory","core/modal_events","core/templates","core/normalise","core/prefetch","core/str","core_courseformat/courseeditor","core/process_monitor","core/utils"],(function(_exports,_config,_modal_factory,_modal_events,_templates,_normalise,_prefetch,_str,_courseeditor,_process_monitor,_utils){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.uploadFilesToCourse=void 0,_config=_interopRequireDefault(_config),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_templates=_interopRequireDefault(_templates);const UPLOADURL=_config.default.wwwroot+"/course/dndupload.php";let uploadQueue=null,handlerManagers={},courseUpdates=new Map,errors=null;(0,_prefetch.prefetchStrings)("moodle",["addresourceoractivity","upload"]),(0,_prefetch.prefetchStrings)("core_error",["dndmaxbytes","dndread","dndupload","dndunkownfile"]);class FileUploader{constructor(courseId,sectionId,sectionNum,fileInfo,handler){this.courseId=courseId,this.sectionId=sectionId,this.sectionNum=sectionNum,this.fileInfo=fileInfo,this.handler=handler}execute(process){const fileInfo=this.fileInfo,xhr=this._createXhrRequest(process),formData=this._createUploadFormData(),reader=new FileReader;reader.onload=function(){xhr.open("POST",UPLOADURL,!0),xhr.send(formData)},reader.onerror=function(){process.setError(errors.dndread)},fileInfo.size>0?reader.readAsText(fileInfo.slice(0,5)):reader.readAsText(fileInfo)}getExecutionFunction(){return this.execute.bind(this)}_createXhrRequest(process){const xhr=new XMLHttpRequest;return xhr.upload.addEventListener("progress",(event=>{if(event.lengthComputable){const percent=Math.round(100*event.loaded/event.total);process.setPercentage(percent)}}),!1),xhr.onreadystatechange=()=>{if(1==xhr.readyState&&process.setPercentage(1),4==xhr.readyState)if(200==xhr.status){var result=JSON.parse(xhr.responseText);result&&0==result.error?this._finishProcess(process):process.setError(result.error)}else process.setError(errors.dndupload)},xhr}_createUploadFormData(){const formData=new FormData;try{formData.append("repo_upload_file",this.fileInfo)}catch(error){throw Error(error.dndread)}return formData.append("sesskey",_config.default.sesskey),formData.append("course",this.courseId),formData.append("section",this.sectionNum),formData.append("module",this.handler.module),formData.append("type","Files"),formData}_finishProcess(process){!function(courseId,sectionId){let refresh=courseUpdates.get(courseId);refresh||(refresh=new Set);refresh.add(sectionId),courseUpdates.set(courseId,refresh),refreshCourseEditors()}(this.courseId,this.sectionId),process.setPercentage(100),process.finish()}}class HandlerManager{constructor(courseId){var _this$courseEditor$ge,_this$courseEditor$ge2;if(_defineProperty(this,"lastHandlers",{}),_defineProperty(this,"allHandlers",null),this.courseId=courseId,this.lastUploadId=0,this.courseEditor=(0,_courseeditor.getCourseEditor)(courseId),!this.courseEditor)throw Error("Unkown course editor");this.maxbytes=null!==(_this$courseEditor$ge=null===(_this$courseEditor$ge2=this.courseEditor.get("course"))||void 0===_this$courseEditor$ge2?void 0:_this$courseEditor$ge2.maxbytes)&&void 0!==_this$courseEditor$ge?_this$courseEditor$ge:0}async loadHandlers(){this.allHandlers=await this.courseEditor.getFileHandlersPromise()}getFileExtension(fileInfo){let extension="";const dotpos=fileInfo.name.lastIndexOf(".");return-1!=dotpos&&(extension=fileInfo.name.substring(dotpos+1,fileInfo.name.length).toLowerCase()),extension}validateFile(fileInfo){if(-1!==this.maxbytes&&fileInfo.size>this.maxbytes)throw Error(errors.dndmaxbytes)}filterHandlers(fileInfo){const extension=this.getFileExtension(fileInfo);return this.allHandlers.filter((handler=>"*"==handler.extension||handler.extension==extension))}async getFileHandler(fileInfo){const fileHandlers=this.filterHandlers(fileInfo);if(0==fileHandlers.length)throw Error(errors.dndunkownfile);let fileHandler=null;return fileHandler=1==fileHandlers.length?fileHandlers[0]:await this.askHandlerToUser(fileHandlers,fileInfo),fileHandler}async askHandlerToUser(fileHandlers,fileInfo){var _this$lastHandlers$ex;const extension=this.getFileExtension(fileInfo),modalParams={title:(0,_str.get_string)("addresourceoractivity","moodle"),body:_templates.default.render("core_courseformat/fileuploader",this.getModalData(fileHandlers,fileInfo,null!==(_this$lastHandlers$ex=this.lastHandlers[extension])&&void 0!==_this$lastHandlers$ex?_this$lastHandlers$ex:null)),type:_modal_factory.default.types.SAVE_CANCEL,saveButtonText:(0,_str.get_string)("upload","moodle")},modal=await this.modalBodyRenderedPromise(modalParams),selectedHandler=await this.modalUserAnswerPromise(modal,fileHandlers);return null===selectedHandler?null:(this.lastHandlers[extension]=selectedHandler.module,selectedHandler)}getModalData(fileHandlers,fileInfo,defaultModule){const data={filename:fileInfo.name,uploadid:++this.lastUploadId,handlers:[]};let hasDefault=!1;if(fileHandlers.forEach(((handler,index)=>{const isDefault=defaultModule==handler.module;data.handlers.push({...handler,selected:isDefault,labelid:"fileuploader_".concat(data.uploadid),value:index}),hasDefault=hasDefault||isDefault})),!hasDefault&&data.handlers.length>0){const lastHandler=data.handlers.pop();lastHandler.selected=!0,data.handlers.push(lastHandler)}return data}modalUserAnswerPromise(modal,fileHandlers){const modalBody=(0,_normalise.getFirst)(modal.getBody());return new Promise(((resolve,reject)=>{modal.getRoot().on(_modal_events.default.save,(event=>{const index=modalBody.querySelector("input:checked").value;event.preventDefault(),modal.destroy(),fileHandlers[index]||reject("Invalid handler selected"),resolve(fileHandlers[index])})),modal.getRoot().on(_modal_events.default.cancel,(()=>{resolve(null)}))}))}modalBodyRenderedPromise(modalParams){return new Promise(((resolve,reject)=>{_modal_factory.default.create(modalParams).then((modal=>{modal.setRemoveOnClose(!0),modal.getRoot().on(_modal_events.default.bodyRendered,(()=>{resolve(modal)})),void 0!==modalParams.saveButtonText&&modal.setSaveButtonText(modalParams.saveButtonText),modal.show()})).catch((()=>{reject("Cannot load modal content")}))}))}}const refreshCourseEditors=(0,_utils.debounce)((()=>{const refreshes=courseUpdates;courseUpdates=new Map,refreshes.forEach(((sectionIds,courseId)=>{const courseEditor=(0,_courseeditor.getCourseEditor)(courseId);courseEditor&&courseEditor.dispatch("sectionState",[...sectionIds])}))}),500);const queueFileUpload=async function(courseId,sectionId,sectionNum,fileInfo,handlerManager){let handler;uploadQueue=await _process_monitor.processMonitor.createProcessQueue();try{handlerManager.validateFile(fileInfo),handler=await handlerManager.getFileHandler(fileInfo)}catch(error){return void uploadQueue.addError(fileInfo.name,error.message)}if(!handler)return;const fileProcessor=new FileUploader(courseId,sectionId,sectionNum,fileInfo,handler);uploadQueue.addPending(fileInfo.name,fileProcessor.getExecutionFunction())};_exports.uploadFilesToCourse=async function(courseId,sectionId,sectionNum,files){const handlerManager=await async function(courseId){if(void 0!==handlerManagers[courseId])return handlerManagers[courseId];const handlerManager=new HandlerManager(courseId);return await handlerManager.loadHandlers(),handlerManagers[courseId]=handlerManager,handlerManagers[courseId]}(courseId);await async function(courseId){var _courseEditor$get$max,_courseEditor$get;if(null!==errors)return;const maxbytestext=null!==(_courseEditor$get$max=null===(_courseEditor$get=(0,_courseeditor.getCourseEditor)(courseId).get("course"))||void 0===_courseEditor$get?void 0:_courseEditor$get.maxbytestext)&&void 0!==_courseEditor$get$max?_courseEditor$get$max:"0";errors={};const allStrings=[{key:"dndmaxbytes",component:"core_error",param:{size:maxbytestext}},{key:"dndread",component:"core_error"},{key:"dndupload",component:"core_error"},{key:"dndunkownfile",component:"core_error"}];window.console.log(allStrings);const loadedStrings=await(0,_str.get_strings)(allStrings);allStrings.forEach(((_ref,index)=>{let{key:key}=_ref;errors[key]=loadedStrings[index]}))}(courseId);for(let index=0;index0?reader.readAsText(fileInfo.slice(0,5)):reader.readAsText(fileInfo)}getExecutionFunction(){return this.execute.bind(this)}_createXhrRequest(process){const xhr=new XMLHttpRequest;return xhr.upload.addEventListener("progress",(event=>{if(event.lengthComputable){const percent=Math.round(100*event.loaded/event.total);process.setPercentage(percent)}}),!1),xhr.onreadystatechange=()=>{if(1==xhr.readyState&&process.setPercentage(1),4==xhr.readyState)if(200==xhr.status){var result=JSON.parse(xhr.responseText);result&&0==result.error?this._finishProcess(process):process.setError(result.error)}else process.setError(errors.dndupload)},xhr}_createUploadFormData(){const formData=new FormData;try{formData.append("repo_upload_file",this.fileInfo)}catch(error){throw Error(error.dndread)}return formData.append("sesskey",_config.default.sesskey),formData.append("course",this.courseId),formData.append("section",this.sectionNum),formData.append("module",this.handler.module),formData.append("type","Files"),formData}_finishProcess(process){!function(courseId,sectionId){let refresh=courseUpdates.get(courseId);refresh||(refresh=new Set);refresh.add(sectionId),courseUpdates.set(courseId,refresh),refreshCourseEditors()}(this.courseId,this.sectionId),process.setPercentage(100),process.finish()}}class HandlerManager{constructor(courseId){var _this$courseEditor$ge,_this$courseEditor$ge2;if(_defineProperty(this,"lastHandlers",{}),_defineProperty(this,"allHandlers",null),this.courseId=courseId,this.lastUploadId=0,this.courseEditor=(0,_courseeditor.getCourseEditor)(courseId),!this.courseEditor)throw Error("Unkown course editor");this.maxbytes=null!==(_this$courseEditor$ge=null===(_this$courseEditor$ge2=this.courseEditor.get("course"))||void 0===_this$courseEditor$ge2?void 0:_this$courseEditor$ge2.maxbytes)&&void 0!==_this$courseEditor$ge?_this$courseEditor$ge:0}async loadHandlers(){this.allHandlers=await this.courseEditor.getFileHandlersPromise()}getFileExtension(fileInfo){let extension="";const dotpos=fileInfo.name.lastIndexOf(".");return-1!=dotpos&&(extension=fileInfo.name.substring(dotpos+1,fileInfo.name.length).toLowerCase()),extension}validateFile(fileInfo){if(-1!==this.maxbytes&&fileInfo.size>this.maxbytes)throw Error(errors.dndmaxbytes)}filterHandlers(fileInfo){const extension=this.getFileExtension(fileInfo);return this.allHandlers.filter((handler=>"*"==handler.extension||handler.extension==extension))}async getFileHandler(fileInfo){const fileHandlers=this.filterHandlers(fileInfo);if(0==fileHandlers.length)throw Error(errors.dndunkownfile);let fileHandler=null;return fileHandler=1==fileHandlers.length?fileHandlers[0]:await this.askHandlerToUser(fileHandlers,fileInfo),fileHandler}async askHandlerToUser(fileHandlers,fileInfo){var _this$lastHandlers$ex;const extension=this.getFileExtension(fileInfo),modalParams={title:(0,_str.getString)("addresourceoractivity","moodle"),body:_templates.default.render("core_courseformat/fileuploader",this.getModalData(fileHandlers,fileInfo,null!==(_this$lastHandlers$ex=this.lastHandlers[extension])&&void 0!==_this$lastHandlers$ex?_this$lastHandlers$ex:null)),type:_modal_factory.default.types.SAVE_CANCEL,saveButtonText:(0,_str.getString)("upload","moodle")},modal=await this.modalBodyRenderedPromise(modalParams),selectedHandler=await this.modalUserAnswerPromise(modal,fileHandlers);return null===selectedHandler?null:(this.lastHandlers[extension]=selectedHandler.module,selectedHandler)}getModalData(fileHandlers,fileInfo,defaultModule){const data={filename:fileInfo.name,uploadid:++this.lastUploadId,handlers:[]};let hasDefault=!1;if(fileHandlers.forEach(((handler,index)=>{const isDefault=defaultModule==handler.module;data.handlers.push({...handler,selected:isDefault,labelid:"fileuploader_".concat(data.uploadid),value:index}),hasDefault=hasDefault||isDefault})),!hasDefault&&data.handlers.length>0){const lastHandler=data.handlers.pop();lastHandler.selected=!0,data.handlers.push(lastHandler)}return data}modalUserAnswerPromise(modal,fileHandlers){const modalBody=(0,_normalise.getFirst)(modal.getBody());return new Promise(((resolve,reject)=>{modal.getRoot().on(_modal_events.default.save,(event=>{const index=modalBody.querySelector("input:checked").value;event.preventDefault(),modal.destroy(),fileHandlers[index]||reject("Invalid handler selected"),resolve(fileHandlers[index])})),modal.getRoot().on(_modal_events.default.cancel,(()=>{resolve(null)}))}))}modalBodyRenderedPromise(modalParams){return new Promise(((resolve,reject)=>{_modal_factory.default.create(modalParams).then((modal=>{modal.setRemoveOnClose(!0),modal.getRoot().on(_modal_events.default.bodyRendered,(()=>{resolve(modal)})),void 0!==modalParams.saveButtonText&&modal.setSaveButtonText(modalParams.saveButtonText),modal.show()})).catch((()=>{reject("Cannot load modal content")}))}))}}const refreshCourseEditors=(0,_utils.debounce)((()=>{const refreshes=courseUpdates;courseUpdates=new Map,refreshes.forEach(((sectionIds,courseId)=>{const courseEditor=(0,_courseeditor.getCourseEditor)(courseId);courseEditor&&courseEditor.dispatch("sectionState",[...sectionIds])}))}),500);const queueFileUpload=async function(courseId,sectionId,sectionNum,fileInfo,handlerManager){let handler;uploadQueue=await _process_monitor.processMonitor.createProcessQueue();try{handlerManager.validateFile(fileInfo),handler=await handlerManager.getFileHandler(fileInfo)}catch(error){return void uploadQueue.addError(fileInfo.name,error.message)}if(!handler)return;const fileProcessor=new FileUploader(courseId,sectionId,sectionNum,fileInfo,handler);uploadQueue.addPending(fileInfo.name,fileProcessor.getExecutionFunction())};_exports.uploadFilesToCourse=async function(courseId,sectionId,sectionNum,files){const handlerManager=await async function(courseId){if(void 0!==handlerManagers[courseId])return handlerManagers[courseId];const handlerManager=new HandlerManager(courseId);return await handlerManager.loadHandlers(),handlerManagers[courseId]=handlerManager,handlerManagers[courseId]}(courseId);await async function(courseId){var _courseEditor$get$max,_courseEditor$get;if(null!==errors)return;const maxbytestext=null!==(_courseEditor$get$max=null===(_courseEditor$get=(0,_courseeditor.getCourseEditor)(courseId).get("course"))||void 0===_courseEditor$get?void 0:_courseEditor$get.maxbytestext)&&void 0!==_courseEditor$get$max?_courseEditor$get$max:"0";errors={};const allStrings=[{key:"dndmaxbytes",component:"core_error",param:{size:maxbytestext}},{key:"dndread",component:"core_error"},{key:"dndupload",component:"core_error"},{key:"dndunkownfile",component:"core_error"}];window.console.log(allStrings);const loadedStrings=await(0,_str.getStrings)(allStrings);allStrings.forEach(((_ref,index)=>{let{key:key}=_ref;errors[key]=loadedStrings[index]}))}(courseId);for(let index=0;index.\n\n/**\n * The course file uploader.\n *\n * This module is used to upload files directly into the course.\n *\n * @module core_courseformat/local/courseeditor/fileuploader\n * @copyright 2022 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * @typedef {Object} Handler\n * @property {String} extension the handled extension or * for any\n * @property {String} message the handler message\n * @property {String} module the module name\n */\n\nimport Config from 'core/config';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Templates from 'core/templates';\nimport {getFirst} from 'core/normalise';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString, get_strings as getStrings} from 'core/str';\nimport {getCourseEditor} from 'core_courseformat/courseeditor';\nimport {processMonitor} from 'core/process_monitor';\nimport {debounce} from 'core/utils';\n\n// Uploading url.\nconst UPLOADURL = Config.wwwroot + '/course/dndupload.php';\nconst DEBOUNCETIMER = 500;\nconst USERCANIGNOREFILESIZELIMITS = -1;\n\n/** @var {ProcessQueue} uploadQueue the internal uploadQueue instance. */\nlet uploadQueue = null;\n/** @var {Object} handlerManagers the courseId indexed loaded handler managers. */\nlet handlerManagers = {};\n/** @var {Map} courseUpdates the pending course sections updates. */\nlet courseUpdates = new Map();\n/** @var {Object} errors the error messages. */\nlet errors = null;\n\n// Load global strings.\nprefetchStrings('moodle', ['addresourceoractivity', 'upload']);\nprefetchStrings('core_error', ['dndmaxbytes', 'dndread', 'dndupload', 'dndunkownfile']);\n\n/**\n * Class to upload a file into the course.\n * @private\n */\nclass FileUploader {\n /**\n * Class constructor.\n *\n * @param {number} courseId the course id\n * @param {number} sectionId the section id\n * @param {number} sectionNum the section number\n * @param {File} fileInfo the file information object\n * @param {Handler} handler the file selected file handler\n */\n constructor(courseId, sectionId, sectionNum, fileInfo, handler) {\n this.courseId = courseId;\n this.sectionId = sectionId;\n this.sectionNum = sectionNum;\n this.fileInfo = fileInfo;\n this.handler = handler;\n }\n\n /**\n * Execute the file upload and update the state in the given process.\n *\n * @param {LoadingProcess} process the process to store the upload result\n */\n execute(process) {\n const fileInfo = this.fileInfo;\n const xhr = this._createXhrRequest(process);\n const formData = this._createUploadFormData();\n\n // Try reading the file to check it is not a folder, before sending it to the server.\n const reader = new FileReader();\n reader.onload = function() {\n // File was read OK - send it to the server.\n xhr.open(\"POST\", UPLOADURL, true);\n xhr.send(formData);\n };\n reader.onerror = function() {\n // Unable to read the file (it is probably a folder) - display an error message.\n process.setError(errors.dndread);\n };\n if (fileInfo.size > 0) {\n // If this is a non-empty file, try reading the first few bytes.\n // This will trigger reader.onerror() for folders and reader.onload() for ordinary, readable files.\n reader.readAsText(fileInfo.slice(0, 5));\n } else {\n // If you call slice() on a 0-byte folder, before calling readAsText, then Firefox triggers reader.onload(),\n // instead of reader.onerror().\n // So, for 0-byte files, just call readAsText on the whole file (and it will trigger load/error functions as expected).\n reader.readAsText(fileInfo);\n }\n }\n\n /**\n * Returns the bind version of execute function.\n *\n * This method is used to queue the process into a ProcessQueue instance.\n *\n * @returns {Function} the bind function to execute the process\n */\n getExecutionFunction() {\n return this.execute.bind(this);\n }\n\n /**\n * Generate a upload XHR file request.\n *\n * @param {LoadingProcess} process the current process\n * @return {XMLHttpRequest} the XHR request\n */\n _createXhrRequest(process) {\n const xhr = new XMLHttpRequest();\n // Update the progress bar as the file is uploaded.\n xhr.upload.addEventListener(\n 'progress',\n (event) => {\n if (event.lengthComputable) {\n const percent = Math.round((event.loaded * 100) / event.total);\n process.setPercentage(percent);\n }\n },\n false\n );\n // Wait for the AJAX call to complete.\n xhr.onreadystatechange = () => {\n if (xhr.readyState == 1) {\n // Add a 1% just to indicate that it is uploading.\n process.setPercentage(1);\n }\n // State 4 is DONE. Otherwise the connection is still ongoing.\n if (xhr.readyState != 4) {\n return;\n }\n if (xhr.status == 200) {\n var result = JSON.parse(xhr.responseText);\n if (result && result.error == 0) {\n // All OK.\n this._finishProcess(process);\n } else {\n process.setError(result.error);\n }\n } else {\n process.setError(errors.dndupload);\n }\n };\n return xhr;\n }\n\n /**\n * Upload a file into the course.\n *\n * @return {FormData|null} the new form data object\n */\n _createUploadFormData() {\n const formData = new FormData();\n try {\n formData.append('repo_upload_file', this.fileInfo);\n } catch (error) {\n throw Error(error.dndread);\n }\n formData.append('sesskey', Config.sesskey);\n formData.append('course', this.courseId);\n formData.append('section', this.sectionNum);\n formData.append('module', this.handler.module);\n formData.append('type', 'Files');\n return formData;\n }\n\n /**\n * Finishes the current process.\n * @param {LoadingProcess} process the process\n */\n _finishProcess(process) {\n addRefreshSection(this.courseId, this.sectionId);\n process.setPercentage(100);\n process.finish();\n }\n}\n\n/**\n * The file handler manager class.\n *\n * @private\n */\nclass HandlerManager {\n\n /** @var {Object} lastHandlers the last handlers selected per each file extension. */\n lastHandlers = {};\n\n /** @var {Handler[]|null} allHandlers all the available handlers. */\n allHandlers = null;\n\n /**\n * Class constructor.\n *\n * @param {Number} courseId\n */\n constructor(courseId) {\n this.courseId = courseId;\n this.lastUploadId = 0;\n this.courseEditor = getCourseEditor(courseId);\n if (!this.courseEditor) {\n throw Error('Unkown course editor');\n }\n this.maxbytes = this.courseEditor.get('course')?.maxbytes ?? 0;\n }\n\n /**\n * Load the course file handlers.\n */\n async loadHandlers() {\n this.allHandlers = await this.courseEditor.getFileHandlersPromise();\n }\n\n /**\n * Extract the file extension from a fileInfo.\n *\n * @param {File} fileInfo\n * @returns {String} the file extension or an empty string.\n */\n getFileExtension(fileInfo) {\n let extension = '';\n const dotpos = fileInfo.name.lastIndexOf('.');\n if (dotpos != -1) {\n extension = fileInfo.name.substring(dotpos + 1, fileInfo.name.length).toLowerCase();\n }\n return extension;\n }\n\n /**\n * Check if the file is valid.\n *\n * @param {File} fileInfo the file info\n */\n validateFile(fileInfo) {\n if (this.maxbytes !== USERCANIGNOREFILESIZELIMITS && fileInfo.size > this.maxbytes) {\n throw Error(errors.dndmaxbytes);\n }\n }\n\n /**\n * Get the file handlers of an specific file.\n *\n * @param {File} fileInfo the file indo\n * @return {Array} Array of handlers\n */\n filterHandlers(fileInfo) {\n const extension = this.getFileExtension(fileInfo);\n return this.allHandlers.filter(handler => handler.extension == '*' || handler.extension == extension);\n }\n\n /**\n * Get the Handler to upload a specific file.\n *\n * It will ask the used if more than one handler is available.\n *\n * @param {File} fileInfo the file info\n * @returns {Promise} the selected handler or null if the user cancel\n */\n async getFileHandler(fileInfo) {\n const fileHandlers = this.filterHandlers(fileInfo);\n if (fileHandlers.length == 0) {\n throw Error(errors.dndunkownfile);\n }\n let fileHandler = null;\n if (fileHandlers.length == 1) {\n fileHandler = fileHandlers[0];\n } else {\n fileHandler = await this.askHandlerToUser(fileHandlers, fileInfo);\n }\n return fileHandler;\n }\n\n /**\n * Ask the user to select a specific handler.\n *\n * @param {Handler[]} fileHandlers\n * @param {File} fileInfo the file info\n * @return {Promise} the selected handler\n */\n async askHandlerToUser(fileHandlers, fileInfo) {\n const extension = this.getFileExtension(fileInfo);\n // Build the modal parameters from the event data.\n const modalParams = {\n title: getString('addresourceoractivity', 'moodle'),\n body: Templates.render(\n 'core_courseformat/fileuploader',\n this.getModalData(\n fileHandlers,\n fileInfo,\n this.lastHandlers[extension] ?? null\n )\n ),\n type: ModalFactory.types.SAVE_CANCEL,\n saveButtonText: getString('upload', 'moodle'),\n };\n // Create the modal.\n const modal = await this.modalBodyRenderedPromise(modalParams);\n const selectedHandler = await this.modalUserAnswerPromise(modal, fileHandlers);\n // Cancel action.\n if (selectedHandler === null) {\n return null;\n }\n // Save last selected handler.\n this.lastHandlers[extension] = selectedHandler.module;\n return selectedHandler;\n }\n\n /**\n * Generated the modal template data.\n *\n * @param {Handler[]} fileHandlers\n * @param {File} fileInfo the file info\n * @param {String|null} defaultModule the default module if any\n * @return {Object} the modal template data.\n */\n getModalData(fileHandlers, fileInfo, defaultModule) {\n const data = {\n filename: fileInfo.name,\n uploadid: ++this.lastUploadId,\n handlers: [],\n };\n let hasDefault = false;\n fileHandlers.forEach((handler, index) => {\n const isDefault = (defaultModule == handler.module);\n data.handlers.push({\n ...handler,\n selected: isDefault,\n labelid: `fileuploader_${data.uploadid}`,\n value: index,\n });\n hasDefault = hasDefault || isDefault;\n });\n if (!hasDefault && data.handlers.length > 0) {\n const lastHandler = data.handlers.pop();\n lastHandler.selected = true;\n data.handlers.push(lastHandler);\n }\n return data;\n }\n\n /**\n * Get the user handler choice.\n *\n * Wait for the user answer in the modal and resolve with the selected index.\n *\n * @param {Modal} modal the modal instance\n * @param {Handler[]} fileHandlers the availabvle file handlers\n * @return {Promise} with the option selected by the user.\n */\n modalUserAnswerPromise(modal, fileHandlers) {\n const modalBody = getFirst(modal.getBody());\n return new Promise((resolve, reject) => {\n modal.getRoot().on(\n ModalEvents.save,\n event => {\n // Get the selected option.\n const index = modalBody.querySelector('input:checked').value;\n event.preventDefault();\n modal.destroy();\n if (!fileHandlers[index]) {\n reject('Invalid handler selected');\n }\n resolve(fileHandlers[index]);\n\n }\n );\n modal.getRoot().on(\n ModalEvents.cancel,\n () => {\n resolve(null);\n }\n );\n });\n }\n\n /**\n * Create a new modal and return a Promise to the body rendered.\n *\n * @param {Object} modalParams the modal params\n * @returns {Promise} the modal body rendered promise\n */\n modalBodyRenderedPromise(modalParams) {\n return new Promise((resolve, reject) => {\n ModalFactory.create(modalParams).then((modal) => {\n modal.setRemoveOnClose(true);\n // Handle body loading event.\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n resolve(modal);\n });\n // Configure some extra modal params.\n if (modalParams.saveButtonText !== undefined) {\n modal.setSaveButtonText(modalParams.saveButtonText);\n }\n modal.show();\n return;\n }).catch(() => {\n reject(`Cannot load modal content`);\n });\n });\n }\n}\n\n/**\n * Add a section to refresh.\n *\n * @param {number} courseId the course id\n * @param {number} sectionId the seciton id\n */\nfunction addRefreshSection(courseId, sectionId) {\n let refresh = courseUpdates.get(courseId);\n if (!refresh) {\n refresh = new Set();\n }\n refresh.add(sectionId);\n courseUpdates.set(courseId, refresh);\n refreshCourseEditors();\n}\n\n/**\n * Debounced processing all pending course refreshes.\n * @private\n */\nconst refreshCourseEditors = debounce(\n () => {\n const refreshes = courseUpdates;\n courseUpdates = new Map();\n refreshes.forEach((sectionIds, courseId) => {\n const courseEditor = getCourseEditor(courseId);\n if (!courseEditor) {\n return;\n }\n courseEditor.dispatch('sectionState', [...sectionIds]);\n });\n },\n DEBOUNCETIMER\n);\n\n/**\n * Load and return the course handler manager instance.\n *\n * @param {Number} courseId the course Id to load\n * @returns {Promise} promise of the the loaded handleManager\n */\nasync function loadCourseHandlerManager(courseId) {\n if (handlerManagers[courseId] !== undefined) {\n return handlerManagers[courseId];\n }\n const handlerManager = new HandlerManager(courseId);\n await handlerManager.loadHandlers();\n handlerManagers[courseId] = handlerManager;\n return handlerManagers[courseId];\n}\n\n/**\n * Load all the erros messages at once in the module \"errors\" variable.\n * @param {Number} courseId the course id\n */\nasync function loadErrorStrings(courseId) {\n if (errors !== null) {\n return;\n }\n const courseEditor = getCourseEditor(courseId);\n const maxbytestext = courseEditor.get('course')?.maxbytestext ?? '0';\n\n errors = {};\n const allStrings = [\n {key: 'dndmaxbytes', component: 'core_error', param: {size: maxbytestext}},\n {key: 'dndread', component: 'core_error'},\n {key: 'dndupload', component: 'core_error'},\n {key: 'dndunkownfile', component: 'core_error'},\n ];\n window.console.log(allStrings);\n const loadedStrings = await getStrings(allStrings);\n allStrings.forEach(({key}, index) => {\n errors[key] = loadedStrings[index];\n });\n}\n\n/**\n * Start a batch file uploading into the course.\n *\n * @private\n * @param {number} courseId the course id.\n * @param {number} sectionId the section id.\n * @param {number} sectionNum the section number.\n * @param {File} fileInfo the file information object\n * @param {HandlerManager} handlerManager the course handler manager\n */\nconst queueFileUpload = async function(courseId, sectionId, sectionNum, fileInfo, handlerManager) {\n let handler;\n uploadQueue = await processMonitor.createProcessQueue();\n try {\n handlerManager.validateFile(fileInfo);\n handler = await handlerManager.getFileHandler(fileInfo);\n } catch (error) {\n uploadQueue.addError(fileInfo.name, error.message);\n return;\n }\n // If we don't have a handler means the user cancel the upload.\n if (!handler) {\n return;\n }\n const fileProcessor = new FileUploader(courseId, sectionId, sectionNum, fileInfo, handler);\n uploadQueue.addPending(fileInfo.name, fileProcessor.getExecutionFunction());\n};\n\n/**\n * Upload a file to the course.\n *\n * This method will show any necesary modal to handle the request.\n *\n * @param {number} courseId the course id\n * @param {number} sectionId the section id\n * @param {number} sectionNum the section number\n * @param {Array} files and array of files\n */\nexport const uploadFilesToCourse = async function(courseId, sectionId, sectionNum, files) {\n // Get the course handlers.\n const handlerManager = await loadCourseHandlerManager(courseId);\n await loadErrorStrings(courseId);\n for (let index = 0; index < files.length; index++) {\n const fileInfo = files[index];\n await queueFileUpload(courseId, sectionId, sectionNum, fileInfo, handlerManager);\n }\n};\n"],"names":["UPLOADURL","Config","wwwroot","uploadQueue","handlerManagers","courseUpdates","Map","errors","FileUploader","constructor","courseId","sectionId","sectionNum","fileInfo","handler","execute","process","this","xhr","_createXhrRequest","formData","_createUploadFormData","reader","FileReader","onload","open","send","onerror","setError","dndread","size","readAsText","slice","getExecutionFunction","bind","XMLHttpRequest","upload","addEventListener","event","lengthComputable","percent","Math","round","loaded","total","setPercentage","onreadystatechange","readyState","status","result","JSON","parse","responseText","error","_finishProcess","dndupload","FormData","append","Error","sesskey","module","refresh","get","Set","add","set","refreshCourseEditors","addRefreshSection","finish","HandlerManager","lastUploadId","courseEditor","maxbytes","_this$courseEditor$ge2","allHandlers","getFileHandlersPromise","getFileExtension","extension","dotpos","name","lastIndexOf","substring","length","toLowerCase","validateFile","dndmaxbytes","filterHandlers","filter","fileHandlers","dndunkownfile","fileHandler","askHandlerToUser","modalParams","title","body","Templates","render","getModalData","lastHandlers","type","ModalFactory","types","SAVE_CANCEL","saveButtonText","modal","modalBodyRenderedPromise","selectedHandler","modalUserAnswerPromise","defaultModule","data","filename","uploadid","handlers","hasDefault","forEach","index","isDefault","push","selected","labelid","value","lastHandler","pop","modalBody","getBody","Promise","resolve","reject","getRoot","on","ModalEvents","save","querySelector","preventDefault","destroy","cancel","create","then","setRemoveOnClose","bodyRendered","undefined","setSaveButtonText","show","catch","refreshes","sectionIds","dispatch","queueFileUpload","async","handlerManager","processMonitor","createProcessQueue","getFileHandler","addError","message","fileProcessor","addPending","files","loadHandlers","loadCourseHandlerManager","maxbytestext","_courseEditor$get","allStrings","key","component","param","window","console","log","loadedStrings","loadErrorStrings"],"mappings":"45BA4CMA,UAAYC,gBAAOC,QAAU,4BAK/BC,YAAc,KAEdC,gBAAkB,GAElBC,cAAgB,IAAIC,IAEpBC,OAAS,mCAGG,SAAU,CAAC,wBAAyB,yCACpC,aAAc,CAAC,cAAe,UAAW,YAAa,wBAMhEC,aAUFC,YAAYC,SAAUC,UAAWC,WAAYC,SAAUC,cAC9CJ,SAAWA,cACXC,UAAYA,eACZC,WAAaA,gBACbC,SAAWA,cACXC,QAAUA,QAQnBC,QAAQC,eACEH,SAAWI,KAAKJ,SAChBK,IAAMD,KAAKE,kBAAkBH,SAC7BI,SAAWH,KAAKI,wBAGhBC,OAAS,IAAIC,WACnBD,OAAOE,OAAS,WAEZN,IAAIO,KAAK,OAAQzB,WAAW,GAC5BkB,IAAIQ,KAAKN,WAEbE,OAAOK,QAAU,WAEbX,QAAQY,SAASrB,OAAOsB,UAExBhB,SAASiB,KAAO,EAGhBR,OAAOS,WAAWlB,SAASmB,MAAM,EAAG,IAKpCV,OAAOS,WAAWlB,UAW1BoB,8BACWhB,KAAKF,QAAQmB,KAAKjB,MAS7BE,kBAAkBH,eACRE,IAAM,IAAIiB,sBAEhBjB,IAAIkB,OAAOC,iBACP,YACCC,WACOA,MAAMC,iBAAkB,OAClBC,QAAUC,KAAKC,MAAsB,IAAfJ,MAAMK,OAAgBL,MAAMM,OACxD5B,QAAQ6B,cAAcL,aAG9B,GAGJtB,IAAI4B,mBAAqB,QACC,GAAlB5B,IAAI6B,YAEJ/B,QAAQ6B,cAAc,GAGJ,GAAlB3B,IAAI6B,cAGU,KAAd7B,IAAI8B,OAAe,KACfC,OAASC,KAAKC,MAAMjC,IAAIkC,cACxBH,QAA0B,GAAhBA,OAAOI,WAEZC,eAAetC,SAEpBA,QAAQY,SAASqB,OAAOI,YAG5BrC,QAAQY,SAASrB,OAAOgD,YAGzBrC,IAQXG,8BACUD,SAAW,IAAIoC,aAEjBpC,SAASqC,OAAO,mBAAoBxC,KAAKJ,UAC3C,MAAOwC,aACCK,MAAML,MAAMxB,gBAEtBT,SAASqC,OAAO,UAAWxD,gBAAO0D,SAClCvC,SAASqC,OAAO,SAAUxC,KAAKP,UAC/BU,SAASqC,OAAO,UAAWxC,KAAKL,YAChCQ,SAASqC,OAAO,SAAUxC,KAAKH,QAAQ8C,QACvCxC,SAASqC,OAAO,OAAQ,SACjBrC,SAOXkC,eAAetC,mBA6OQN,SAAUC,eAC7BkD,QAAUxD,cAAcyD,IAAIpD,UAC3BmD,UACDA,QAAU,IAAIE,KAElBF,QAAQG,IAAIrD,WACZN,cAAc4D,IAAIvD,SAAUmD,SAC5BK,uBAnPIC,CAAkBlD,KAAKP,SAAUO,KAAKN,WACtCK,QAAQ6B,cAAc,KACtB7B,QAAQoD,gBASVC,eAaF5D,YAAYC,kGAVG,uCAGD,WAQLA,SAAWA,cACX4D,aAAe,OACfC,cAAe,iCAAgB7D,WAC/BO,KAAKsD,mBACAb,MAAM,6BAEXc,sEAAWvD,KAAKsD,aAAaT,IAAI,mDAAtBW,uBAAiCD,gEAAY,4BAOxDE,kBAAoBzD,KAAKsD,aAAaI,yBAS/CC,iBAAiB/D,cACTgE,UAAY,SACVC,OAASjE,SAASkE,KAAKC,YAAY,YAC1B,GAAXF,SACAD,UAAYhE,SAASkE,KAAKE,UAAUH,OAAS,EAAGjE,SAASkE,KAAKG,QAAQC,eAEnEN,UAQXO,aAAavE,cAnNmB,IAoNxBI,KAAKuD,UAA4C3D,SAASiB,KAAOb,KAAKuD,eAChEd,MAAMnD,OAAO8E,aAU3BC,eAAezE,gBACLgE,UAAY5D,KAAK2D,iBAAiB/D,iBACjCI,KAAKyD,YAAYa,QAAOzE,SAAgC,KAArBA,QAAQ+D,WAAoB/D,QAAQ+D,WAAaA,iCAW1EhE,gBACX2E,aAAevE,KAAKqE,eAAezE,aACd,GAAvB2E,aAAaN,aACPxB,MAAMnD,OAAOkF,mBAEnBC,YAAc,YAEdA,YADuB,GAAvBF,aAAaN,OACCM,aAAa,SAEPvE,KAAK0E,iBAAiBH,aAAc3E,UAErD6E,mCAUYF,aAAc3E,0CAC3BgE,UAAY5D,KAAK2D,iBAAiB/D,UAElC+E,YAAc,CAChBC,OAAO,mBAAU,wBAAyB,UAC1CC,KAAMC,mBAAUC,OACZ,iCACA/E,KAAKgF,aACDT,aACA3E,uCACAI,KAAKiF,aAAarB,kEAAc,OAGxCsB,KAAMC,uBAAaC,MAAMC,YACzBC,gBAAgB,mBAAU,SAAU,WAGlCC,YAAcvF,KAAKwF,yBAAyBb,aAC5Cc,sBAAwBzF,KAAK0F,uBAAuBH,MAAOhB,qBAEzC,OAApBkB,gBACO,WAGNR,aAAarB,WAAa6B,gBAAgB9C,OACxC8C,iBAWXT,aAAaT,aAAc3E,SAAU+F,qBAC3BC,KAAO,CACTC,SAAUjG,SAASkE,KACnBgC,WAAY9F,KAAKqD,aACjB0C,SAAU,QAEVC,YAAa,KACjBzB,aAAa0B,SAAQ,CAACpG,QAASqG,eACrBC,UAAaR,eAAiB9F,QAAQ8C,OAC5CiD,KAAKG,SAASK,KAAK,IACZvG,QACHwG,SAAUF,UACVG,+BAAyBV,KAAKE,UAC9BS,MAAOL,QAEXF,WAAaA,YAAcG,cAE1BH,YAAcJ,KAAKG,SAAS9B,OAAS,EAAG,OACnCuC,YAAcZ,KAAKG,SAASU,MAClCD,YAAYH,UAAW,EACvBT,KAAKG,SAASK,KAAKI,oBAEhBZ,KAYXF,uBAAuBH,MAAOhB,oBACpBmC,WAAY,uBAASnB,MAAMoB,kBAC1B,IAAIC,SAAQ,CAACC,QAASC,UACzBvB,MAAMwB,UAAUC,GACZC,sBAAYC,MACZ7F,cAEU6E,MAAQQ,UAAUS,cAAc,iBAAiBZ,MACvDlF,MAAM+F,iBACN7B,MAAM8B,UACD9C,aAAa2B,QACdY,OAAO,4BAEXD,QAAQtC,aAAa2B,WAI7BX,MAAMwB,UAAUC,GACZC,sBAAYK,QACZ,KACIT,QAAQ,YAYxBrB,yBAAyBb,oBACd,IAAIiC,SAAQ,CAACC,QAASC,iCACZS,OAAO5C,aAAa6C,MAAMjC,QACnCA,MAAMkC,kBAAiB,GAEvBlC,MAAMwB,UAAUC,GAAGC,sBAAYS,cAAc,KACzCb,QAAQtB,eAGuBoC,IAA/BhD,YAAYW,gBACZC,MAAMqC,kBAAkBjD,YAAYW,gBAExCC,MAAMsC,UAEPC,OAAM,KACLhB,iDA0BV7D,sBAAuB,oBACzB,WACU8E,UAAY3I,cAClBA,cAAgB,IAAIC,IACpB0I,UAAU9B,SAAQ,CAAC+B,WAAYvI,kBACrB6D,cAAe,iCAAgB7D,UAChC6D,cAGLA,aAAa2E,SAAS,eAAgB,IAAID,kBA1ZhC,WAmdhBE,gBAAkBC,eAAe1I,SAAUC,UAAWC,WAAYC,SAAUwI,oBAC1EvI,QACJX,kBAAoBmJ,gCAAeC,yBAE/BF,eAAejE,aAAavE,UAC5BC,cAAgBuI,eAAeG,eAAe3I,UAChD,MAAOwC,mBACLlD,YAAYsJ,SAAS5I,SAASkE,KAAM1B,MAAMqG,aAIzC5I,qBAGC6I,cAAgB,IAAInJ,aAAaE,SAAUC,UAAWC,WAAYC,SAAUC,SAClFX,YAAYyJ,WAAW/I,SAASkE,KAAM4E,cAAc1H,sDAarBmH,eAAe1I,SAAUC,UAAWC,WAAYiJ,aAEzER,oCA3E8B3I,kBACFkI,IAA9BxI,gBAAgBM,iBACTN,gBAAgBM,gBAErB2I,eAAiB,IAAIhF,eAAe3D,uBACpC2I,eAAeS,eACrB1J,gBAAgBM,UAAY2I,eACrBjJ,gBAAgBM,UAoEMqJ,CAAyBrJ,+BA7D1BA,yDACb,OAAXH,oBAIEyJ,sEADe,iCAAgBtJ,UACHoD,IAAI,8CAAjBmG,kBAA4BD,oEAAgB,IAEjEzJ,OAAS,SACH2J,WAAa,CACf,CAACC,IAAK,cAAeC,UAAW,aAAcC,MAAO,CAACvI,KAAMkI,eAC5D,CAACG,IAAK,UAAWC,UAAW,cAC5B,CAACD,IAAK,YAAaC,UAAW,cAC9B,CAACD,IAAK,gBAAiBC,UAAW,eAEtCE,OAAOC,QAAQC,IAAIN,kBACbO,oBAAsB,oBAAWP,YACvCA,WAAWhD,SAAQ,MAAQC,aAAPgD,IAACA,UACjB5J,OAAO4J,KAAOM,cAActD,UA6C1BuD,CAAiBhK,cAClB,IAAIyG,MAAQ,EAAGA,MAAQ0C,MAAM3E,OAAQiC,QAAS,OACzCtG,SAAWgJ,MAAM1C,aACjBgC,gBAAgBzI,SAAUC,UAAWC,WAAYC,SAAUwI"} \ No newline at end of file +{"version":3,"file":"fileuploader.min.js","sources":["../../../src/local/courseeditor/fileuploader.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 * The course file uploader.\n *\n * This module is used to upload files directly into the course.\n *\n * @module core_courseformat/local/courseeditor/fileuploader\n * @copyright 2022 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * @typedef {Object} Handler\n * @property {String} extension the handled extension or * for any\n * @property {String} message the handler message\n * @property {String} module the module name\n */\n\nimport Config from 'core/config';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Templates from 'core/templates';\nimport {getFirst} from 'core/normalise';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString, getStrings} from 'core/str';\nimport {getCourseEditor} from 'core_courseformat/courseeditor';\nimport {processMonitor} from 'core/process_monitor';\nimport {debounce} from 'core/utils';\n\n// Uploading url.\nconst UPLOADURL = Config.wwwroot + '/course/dndupload.php';\nconst DEBOUNCETIMER = 500;\nconst USERCANIGNOREFILESIZELIMITS = -1;\n\n/** @var {ProcessQueue} uploadQueue the internal uploadQueue instance. */\nlet uploadQueue = null;\n/** @var {Object} handlerManagers the courseId indexed loaded handler managers. */\nlet handlerManagers = {};\n/** @var {Map} courseUpdates the pending course sections updates. */\nlet courseUpdates = new Map();\n/** @var {Object} errors the error messages. */\nlet errors = null;\n\n// Load global strings.\nprefetchStrings('moodle', ['addresourceoractivity', 'upload']);\nprefetchStrings('core_error', ['dndmaxbytes', 'dndread', 'dndupload', 'dndunkownfile']);\n\n/**\n * Class to upload a file into the course.\n * @private\n */\nclass FileUploader {\n /**\n * Class constructor.\n *\n * @param {number} courseId the course id\n * @param {number} sectionId the section id\n * @param {number} sectionNum the section number\n * @param {File} fileInfo the file information object\n * @param {Handler} handler the file selected file handler\n */\n constructor(courseId, sectionId, sectionNum, fileInfo, handler) {\n this.courseId = courseId;\n this.sectionId = sectionId;\n this.sectionNum = sectionNum;\n this.fileInfo = fileInfo;\n this.handler = handler;\n }\n\n /**\n * Execute the file upload and update the state in the given process.\n *\n * @param {LoadingProcess} process the process to store the upload result\n */\n execute(process) {\n const fileInfo = this.fileInfo;\n const xhr = this._createXhrRequest(process);\n const formData = this._createUploadFormData();\n\n // Try reading the file to check it is not a folder, before sending it to the server.\n const reader = new FileReader();\n reader.onload = function() {\n // File was read OK - send it to the server.\n xhr.open(\"POST\", UPLOADURL, true);\n xhr.send(formData);\n };\n reader.onerror = function() {\n // Unable to read the file (it is probably a folder) - display an error message.\n process.setError(errors.dndread);\n };\n if (fileInfo.size > 0) {\n // If this is a non-empty file, try reading the first few bytes.\n // This will trigger reader.onerror() for folders and reader.onload() for ordinary, readable files.\n reader.readAsText(fileInfo.slice(0, 5));\n } else {\n // If you call slice() on a 0-byte folder, before calling readAsText, then Firefox triggers reader.onload(),\n // instead of reader.onerror().\n // So, for 0-byte files, just call readAsText on the whole file (and it will trigger load/error functions as expected).\n reader.readAsText(fileInfo);\n }\n }\n\n /**\n * Returns the bind version of execute function.\n *\n * This method is used to queue the process into a ProcessQueue instance.\n *\n * @returns {Function} the bind function to execute the process\n */\n getExecutionFunction() {\n return this.execute.bind(this);\n }\n\n /**\n * Generate a upload XHR file request.\n *\n * @param {LoadingProcess} process the current process\n * @return {XMLHttpRequest} the XHR request\n */\n _createXhrRequest(process) {\n const xhr = new XMLHttpRequest();\n // Update the progress bar as the file is uploaded.\n xhr.upload.addEventListener(\n 'progress',\n (event) => {\n if (event.lengthComputable) {\n const percent = Math.round((event.loaded * 100) / event.total);\n process.setPercentage(percent);\n }\n },\n false\n );\n // Wait for the AJAX call to complete.\n xhr.onreadystatechange = () => {\n if (xhr.readyState == 1) {\n // Add a 1% just to indicate that it is uploading.\n process.setPercentage(1);\n }\n // State 4 is DONE. Otherwise the connection is still ongoing.\n if (xhr.readyState != 4) {\n return;\n }\n if (xhr.status == 200) {\n var result = JSON.parse(xhr.responseText);\n if (result && result.error == 0) {\n // All OK.\n this._finishProcess(process);\n } else {\n process.setError(result.error);\n }\n } else {\n process.setError(errors.dndupload);\n }\n };\n return xhr;\n }\n\n /**\n * Upload a file into the course.\n *\n * @return {FormData|null} the new form data object\n */\n _createUploadFormData() {\n const formData = new FormData();\n try {\n formData.append('repo_upload_file', this.fileInfo);\n } catch (error) {\n throw Error(error.dndread);\n }\n formData.append('sesskey', Config.sesskey);\n formData.append('course', this.courseId);\n formData.append('section', this.sectionNum);\n formData.append('module', this.handler.module);\n formData.append('type', 'Files');\n return formData;\n }\n\n /**\n * Finishes the current process.\n * @param {LoadingProcess} process the process\n */\n _finishProcess(process) {\n addRefreshSection(this.courseId, this.sectionId);\n process.setPercentage(100);\n process.finish();\n }\n}\n\n/**\n * The file handler manager class.\n *\n * @private\n */\nclass HandlerManager {\n\n /** @var {Object} lastHandlers the last handlers selected per each file extension. */\n lastHandlers = {};\n\n /** @var {Handler[]|null} allHandlers all the available handlers. */\n allHandlers = null;\n\n /**\n * Class constructor.\n *\n * @param {Number} courseId\n */\n constructor(courseId) {\n this.courseId = courseId;\n this.lastUploadId = 0;\n this.courseEditor = getCourseEditor(courseId);\n if (!this.courseEditor) {\n throw Error('Unkown course editor');\n }\n this.maxbytes = this.courseEditor.get('course')?.maxbytes ?? 0;\n }\n\n /**\n * Load the course file handlers.\n */\n async loadHandlers() {\n this.allHandlers = await this.courseEditor.getFileHandlersPromise();\n }\n\n /**\n * Extract the file extension from a fileInfo.\n *\n * @param {File} fileInfo\n * @returns {String} the file extension or an empty string.\n */\n getFileExtension(fileInfo) {\n let extension = '';\n const dotpos = fileInfo.name.lastIndexOf('.');\n if (dotpos != -1) {\n extension = fileInfo.name.substring(dotpos + 1, fileInfo.name.length).toLowerCase();\n }\n return extension;\n }\n\n /**\n * Check if the file is valid.\n *\n * @param {File} fileInfo the file info\n */\n validateFile(fileInfo) {\n if (this.maxbytes !== USERCANIGNOREFILESIZELIMITS && fileInfo.size > this.maxbytes) {\n throw Error(errors.dndmaxbytes);\n }\n }\n\n /**\n * Get the file handlers of an specific file.\n *\n * @param {File} fileInfo the file indo\n * @return {Array} Array of handlers\n */\n filterHandlers(fileInfo) {\n const extension = this.getFileExtension(fileInfo);\n return this.allHandlers.filter(handler => handler.extension == '*' || handler.extension == extension);\n }\n\n /**\n * Get the Handler to upload a specific file.\n *\n * It will ask the used if more than one handler is available.\n *\n * @param {File} fileInfo the file info\n * @returns {Promise} the selected handler or null if the user cancel\n */\n async getFileHandler(fileInfo) {\n const fileHandlers = this.filterHandlers(fileInfo);\n if (fileHandlers.length == 0) {\n throw Error(errors.dndunkownfile);\n }\n let fileHandler = null;\n if (fileHandlers.length == 1) {\n fileHandler = fileHandlers[0];\n } else {\n fileHandler = await this.askHandlerToUser(fileHandlers, fileInfo);\n }\n return fileHandler;\n }\n\n /**\n * Ask the user to select a specific handler.\n *\n * @param {Handler[]} fileHandlers\n * @param {File} fileInfo the file info\n * @return {Promise} the selected handler\n */\n async askHandlerToUser(fileHandlers, fileInfo) {\n const extension = this.getFileExtension(fileInfo);\n // Build the modal parameters from the event data.\n const modalParams = {\n title: getString('addresourceoractivity', 'moodle'),\n body: Templates.render(\n 'core_courseformat/fileuploader',\n this.getModalData(\n fileHandlers,\n fileInfo,\n this.lastHandlers[extension] ?? null\n )\n ),\n type: ModalFactory.types.SAVE_CANCEL,\n saveButtonText: getString('upload', 'moodle'),\n };\n // Create the modal.\n const modal = await this.modalBodyRenderedPromise(modalParams);\n const selectedHandler = await this.modalUserAnswerPromise(modal, fileHandlers);\n // Cancel action.\n if (selectedHandler === null) {\n return null;\n }\n // Save last selected handler.\n this.lastHandlers[extension] = selectedHandler.module;\n return selectedHandler;\n }\n\n /**\n * Generated the modal template data.\n *\n * @param {Handler[]} fileHandlers\n * @param {File} fileInfo the file info\n * @param {String|null} defaultModule the default module if any\n * @return {Object} the modal template data.\n */\n getModalData(fileHandlers, fileInfo, defaultModule) {\n const data = {\n filename: fileInfo.name,\n uploadid: ++this.lastUploadId,\n handlers: [],\n };\n let hasDefault = false;\n fileHandlers.forEach((handler, index) => {\n const isDefault = (defaultModule == handler.module);\n data.handlers.push({\n ...handler,\n selected: isDefault,\n labelid: `fileuploader_${data.uploadid}`,\n value: index,\n });\n hasDefault = hasDefault || isDefault;\n });\n if (!hasDefault && data.handlers.length > 0) {\n const lastHandler = data.handlers.pop();\n lastHandler.selected = true;\n data.handlers.push(lastHandler);\n }\n return data;\n }\n\n /**\n * Get the user handler choice.\n *\n * Wait for the user answer in the modal and resolve with the selected index.\n *\n * @param {Modal} modal the modal instance\n * @param {Handler[]} fileHandlers the availabvle file handlers\n * @return {Promise} with the option selected by the user.\n */\n modalUserAnswerPromise(modal, fileHandlers) {\n const modalBody = getFirst(modal.getBody());\n return new Promise((resolve, reject) => {\n modal.getRoot().on(\n ModalEvents.save,\n event => {\n // Get the selected option.\n const index = modalBody.querySelector('input:checked').value;\n event.preventDefault();\n modal.destroy();\n if (!fileHandlers[index]) {\n reject('Invalid handler selected');\n }\n resolve(fileHandlers[index]);\n\n }\n );\n modal.getRoot().on(\n ModalEvents.cancel,\n () => {\n resolve(null);\n }\n );\n });\n }\n\n /**\n * Create a new modal and return a Promise to the body rendered.\n *\n * @param {Object} modalParams the modal params\n * @returns {Promise} the modal body rendered promise\n */\n modalBodyRenderedPromise(modalParams) {\n return new Promise((resolve, reject) => {\n ModalFactory.create(modalParams).then((modal) => {\n modal.setRemoveOnClose(true);\n // Handle body loading event.\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n resolve(modal);\n });\n // Configure some extra modal params.\n if (modalParams.saveButtonText !== undefined) {\n modal.setSaveButtonText(modalParams.saveButtonText);\n }\n modal.show();\n return;\n }).catch(() => {\n reject(`Cannot load modal content`);\n });\n });\n }\n}\n\n/**\n * Add a section to refresh.\n *\n * @param {number} courseId the course id\n * @param {number} sectionId the seciton id\n */\nfunction addRefreshSection(courseId, sectionId) {\n let refresh = courseUpdates.get(courseId);\n if (!refresh) {\n refresh = new Set();\n }\n refresh.add(sectionId);\n courseUpdates.set(courseId, refresh);\n refreshCourseEditors();\n}\n\n/**\n * Debounced processing all pending course refreshes.\n * @private\n */\nconst refreshCourseEditors = debounce(\n () => {\n const refreshes = courseUpdates;\n courseUpdates = new Map();\n refreshes.forEach((sectionIds, courseId) => {\n const courseEditor = getCourseEditor(courseId);\n if (!courseEditor) {\n return;\n }\n courseEditor.dispatch('sectionState', [...sectionIds]);\n });\n },\n DEBOUNCETIMER\n);\n\n/**\n * Load and return the course handler manager instance.\n *\n * @param {Number} courseId the course Id to load\n * @returns {Promise} promise of the the loaded handleManager\n */\nasync function loadCourseHandlerManager(courseId) {\n if (handlerManagers[courseId] !== undefined) {\n return handlerManagers[courseId];\n }\n const handlerManager = new HandlerManager(courseId);\n await handlerManager.loadHandlers();\n handlerManagers[courseId] = handlerManager;\n return handlerManagers[courseId];\n}\n\n/**\n * Load all the erros messages at once in the module \"errors\" variable.\n * @param {Number} courseId the course id\n */\nasync function loadErrorStrings(courseId) {\n if (errors !== null) {\n return;\n }\n const courseEditor = getCourseEditor(courseId);\n const maxbytestext = courseEditor.get('course')?.maxbytestext ?? '0';\n\n errors = {};\n const allStrings = [\n {key: 'dndmaxbytes', component: 'core_error', param: {size: maxbytestext}},\n {key: 'dndread', component: 'core_error'},\n {key: 'dndupload', component: 'core_error'},\n {key: 'dndunkownfile', component: 'core_error'},\n ];\n window.console.log(allStrings);\n const loadedStrings = await getStrings(allStrings);\n allStrings.forEach(({key}, index) => {\n errors[key] = loadedStrings[index];\n });\n}\n\n/**\n * Start a batch file uploading into the course.\n *\n * @private\n * @param {number} courseId the course id.\n * @param {number} sectionId the section id.\n * @param {number} sectionNum the section number.\n * @param {File} fileInfo the file information object\n * @param {HandlerManager} handlerManager the course handler manager\n */\nconst queueFileUpload = async function(courseId, sectionId, sectionNum, fileInfo, handlerManager) {\n let handler;\n uploadQueue = await processMonitor.createProcessQueue();\n try {\n handlerManager.validateFile(fileInfo);\n handler = await handlerManager.getFileHandler(fileInfo);\n } catch (error) {\n uploadQueue.addError(fileInfo.name, error.message);\n return;\n }\n // If we don't have a handler means the user cancel the upload.\n if (!handler) {\n return;\n }\n const fileProcessor = new FileUploader(courseId, sectionId, sectionNum, fileInfo, handler);\n uploadQueue.addPending(fileInfo.name, fileProcessor.getExecutionFunction());\n};\n\n/**\n * Upload a file to the course.\n *\n * This method will show any necesary modal to handle the request.\n *\n * @param {number} courseId the course id\n * @param {number} sectionId the section id\n * @param {number} sectionNum the section number\n * @param {Array} files and array of files\n */\nexport const uploadFilesToCourse = async function(courseId, sectionId, sectionNum, files) {\n // Get the course handlers.\n const handlerManager = await loadCourseHandlerManager(courseId);\n await loadErrorStrings(courseId);\n for (let index = 0; index < files.length; index++) {\n const fileInfo = files[index];\n await queueFileUpload(courseId, sectionId, sectionNum, fileInfo, handlerManager);\n }\n};\n"],"names":["UPLOADURL","Config","wwwroot","uploadQueue","handlerManagers","courseUpdates","Map","errors","FileUploader","constructor","courseId","sectionId","sectionNum","fileInfo","handler","execute","process","this","xhr","_createXhrRequest","formData","_createUploadFormData","reader","FileReader","onload","open","send","onerror","setError","dndread","size","readAsText","slice","getExecutionFunction","bind","XMLHttpRequest","upload","addEventListener","event","lengthComputable","percent","Math","round","loaded","total","setPercentage","onreadystatechange","readyState","status","result","JSON","parse","responseText","error","_finishProcess","dndupload","FormData","append","Error","sesskey","module","refresh","get","Set","add","set","refreshCourseEditors","addRefreshSection","finish","HandlerManager","lastUploadId","courseEditor","maxbytes","_this$courseEditor$ge2","allHandlers","getFileHandlersPromise","getFileExtension","extension","dotpos","name","lastIndexOf","substring","length","toLowerCase","validateFile","dndmaxbytes","filterHandlers","filter","fileHandlers","dndunkownfile","fileHandler","askHandlerToUser","modalParams","title","body","Templates","render","getModalData","lastHandlers","type","ModalFactory","types","SAVE_CANCEL","saveButtonText","modal","modalBodyRenderedPromise","selectedHandler","modalUserAnswerPromise","defaultModule","data","filename","uploadid","handlers","hasDefault","forEach","index","isDefault","push","selected","labelid","value","lastHandler","pop","modalBody","getBody","Promise","resolve","reject","getRoot","on","ModalEvents","save","querySelector","preventDefault","destroy","cancel","create","then","setRemoveOnClose","bodyRendered","undefined","setSaveButtonText","show","catch","refreshes","sectionIds","dispatch","queueFileUpload","async","handlerManager","processMonitor","createProcessQueue","getFileHandler","addError","message","fileProcessor","addPending","files","loadHandlers","loadCourseHandlerManager","maxbytestext","_courseEditor$get","allStrings","key","component","param","window","console","log","loadedStrings","loadErrorStrings"],"mappings":"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"} \ No newline at end of file diff --git a/course/format/amd/src/local/content/actions.js b/course/format/amd/src/local/content/actions.js index 239ce065e56..49dfb4f3135 100644 --- a/course/format/amd/src/local/content/actions.js +++ b/course/format/amd/src/local/content/actions.js @@ -30,7 +30,7 @@ import ModalFactory from 'core/modal_factory'; import ModalEvents from 'core/modal_events'; import Templates from 'core/templates'; import {prefetchStrings} from 'core/prefetch'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {getFirst} from 'core/normalise'; import {toggleBulkSelectionAction} from 'core_courseformat/local/content/actions/bulkselection'; import * as CourseEvents from 'core_course/events'; diff --git a/course/format/amd/src/local/content/bulkedittools.js b/course/format/amd/src/local/content/bulkedittools.js index b9e3dcc7065..7a50e9b2503 100644 --- a/course/format/amd/src/local/content/bulkedittools.js +++ b/course/format/amd/src/local/content/bulkedittools.js @@ -25,7 +25,7 @@ import {BaseComponent} from 'core/reactive'; import {disableStickyFooter, enableStickyFooter} from 'core/sticky-footer'; import {getCurrentCourseEditor} from 'core_courseformat/courseeditor'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Pending from 'core/pending'; import {prefetchStrings} from 'core/prefetch'; import { diff --git a/course/format/amd/src/local/courseeditor/courseeditor.js b/course/format/amd/src/local/courseeditor/courseeditor.js index fe826db9db9..ffa041aff70 100644 --- a/course/format/amd/src/local/courseeditor/courseeditor.js +++ b/course/format/amd/src/local/courseeditor/courseeditor.js @@ -13,7 +13,7 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {Reactive} from 'core/reactive'; import notification from 'core/notification'; import Exporter from 'core_courseformat/local/courseeditor/exporter'; diff --git a/course/format/amd/src/local/courseeditor/dndsection.js b/course/format/amd/src/local/courseeditor/dndsection.js index fec0ad3443f..f1839dae5bc 100644 --- a/course/format/amd/src/local/courseeditor/dndsection.js +++ b/course/format/amd/src/local/courseeditor/dndsection.js @@ -26,7 +26,7 @@ */ import {BaseComponent, DragDrop} from 'core/reactive'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {prefetchStrings} from 'core/prefetch'; import Templates from 'core/templates'; diff --git a/course/format/amd/src/local/courseeditor/fileuploader.js b/course/format/amd/src/local/courseeditor/fileuploader.js index 278e2dd4e49..3daefb6a1aa 100644 --- a/course/format/amd/src/local/courseeditor/fileuploader.js +++ b/course/format/amd/src/local/courseeditor/fileuploader.js @@ -36,7 +36,7 @@ import ModalEvents from 'core/modal_events'; import Templates from 'core/templates'; import {getFirst} from 'core/normalise'; import {prefetchStrings} from 'core/prefetch'; -import {get_string as getString, get_strings as getStrings} from 'core/str'; +import {getString, getStrings} from 'core/str'; import {getCourseEditor} from 'core_courseformat/courseeditor'; import {processMonitor} from 'core/process_monitor'; import {debounce} from 'core/utils'; diff --git a/customfield/amd/build/form.min.js b/customfield/amd/build/form.min.js index 4764b3f5f68..0b037753a91 100644 --- a/customfield/amd/build/form.min.js +++ b/customfield/amd/build/form.min.js @@ -5,6 +5,6 @@ define("core_customfield/form",["exports","core/inplace_editable","core/ajax","c * @module core_customfield/form * @copyright 2018 Toni Barbera * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_sortable_list=_interopRequireDefault(_sortable_list),_templates=_interopRequireDefault(_templates),_jquery=_interopRequireDefault(_jquery);const confirmDelete=(id,type,component,area,itemid)=>{const pendingPromise=new _pending.default("core_customfield/form:confirmDelete");(0,_str.get_strings)([{key:"confirm"},{key:"confirmdelete"+type,component:"core_customfield"},{key:"yes"},{key:"no"}]).then((strings=>_notification.default.confirm(strings[0],strings[1],strings[2],strings[3],(function(){const pendingDeletePromise=new _pending.default("core_customfield/form:confirmDelete");(0,_ajax.call)([{methodname:"field"===type?"core_customfield_delete_field":"core_customfield_delete_category",args:{id:id}},{methodname:"core_customfield_reload_template",args:{component:component,area:area,itemid:itemid}}])[1].then((response=>_templates.default.render("core_customfield/list",response))).then(((html,js)=>_templates.default.replaceNode((0,_jquery.default)('[data-region="list-page"]'),html,js))).then(pendingDeletePromise.resolve).catch(_notification.default.exception)})))).then(pendingPromise.resolve).catch(_notification.default.exception)},getCategoryNameFor=nodeElement=>nodeElement.closest("[data-category-id]").find("[data-inplaceeditable][data-itemtype=category][data-component=core_customfield]").attr("data-value");_exports.init=()=>{const rootNode=document.querySelector("#customfield_catlist"),component=rootNode.dataset.component,area=rootNode.dataset.area,itemid=rootNode.dataset.itemid;rootNode.addEventListener("click",(e=>{const roleHolder=e.target.closest("[data-role]");if(roleHolder)return"deletefield"===roleHolder.dataset.role?(e.preventDefault(),void confirmDelete(roleHolder.dataset.id,"field",component,area,itemid)):"deletecategory"===roleHolder.dataset.role?(e.preventDefault(),void confirmDelete(roleHolder.dataset.id,"category",component,area,itemid)):"addnewcategory"===roleHolder.dataset.role?(e.preventDefault(),void((component,area,itemid)=>{const pendingPromise=new _pending.default("core_customfield/form:createNewCategory");(0,_ajax.call)([{methodname:"core_customfield_create_category",args:{component:component,area:area,itemid:itemid}},{methodname:"core_customfield_reload_template",args:{component:component,area:area,itemid:itemid}}])[1].then((response=>_templates.default.render("core_customfield/list",response))).then(((html,js)=>_templates.default.replaceNode((0,_jquery.default)('[data-region="list-page"]'),html,js))).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)})(component,area,itemid)):"addfield"===roleHolder.dataset.role?(e.preventDefault(),void((element,component,area,itemid)=>{const pendingPromise=new _pending.default("core_customfield/form:createNewField"),returnFocus=element.closest(".action-menu").querySelector(".dropdown-toggle"),form=new _modalform.default({formClass:"core_customfield\\field_config_form",args:{categoryid:element.getAttribute("data-categoryid"),type:element.getAttribute("data-type")},modalConfig:{title:(0,_str.get_string)("addingnewcustomfield","core_customfield",element.getAttribute("data-typename"))},returnFocus:returnFocus});form.addEventListener(form.events.FORM_SUBMITTED,(()=>{const pendingCreatedPromise=new _pending.default("core_customfield/form:createdNewField");(0,_ajax.call)([{methodname:"core_customfield_reload_template",args:{component:component,area:area,itemid:itemid}}])[0].then((response=>_templates.default.render("core_customfield/list",response))).then(((html,js)=>_templates.default.replaceNode((0,_jquery.default)('[data-region="list-page"]'),html,js))).then((()=>pendingCreatedPromise.resolve())).catch((()=>window.location.reload()))})),form.show(),pendingPromise.resolve()})(roleHolder,component,area,itemid)):"editfield"===roleHolder.dataset.role?(e.preventDefault(),void((element,component,area,itemid)=>{const pendingPromise=new _pending.default("core_customfield/form:editField"),form=new _modalform.default({formClass:"core_customfield\\field_config_form",args:{id:element.getAttribute("data-id")},modalConfig:{title:(0,_str.get_string)("editingfield","core_customfield",element.getAttribute("data-name"))},returnFocus:element});form.addEventListener(form.events.FORM_SUBMITTED,(()=>{const pendingCreatedPromise=new _pending.default("core_customfield/form:createdNewField");(0,_ajax.call)([{methodname:"core_customfield_reload_template",args:{component:component,area:area,itemid:itemid}}])[0].then((response=>_templates.default.render("core_customfield/list",response))).then(((html,js)=>_templates.default.replaceNode((0,_jquery.default)('[data-region="list-page"]'),html,js))).then((()=>pendingCreatedPromise.resolve())).catch((()=>window.location.reload()))})),form.show(),pendingPromise.resolve()})(roleHolder,component,area,itemid)):void 0})),(rootNode=>{new _sortable_list.default("#customfield_catlist .categorieslist",{moveHandlerSelector:".movecategory [data-drag-type=move]"}).getElementName=nodeElement=>Promise.resolve(getCategoryNameFor(nodeElement)),(0,_jquery.default)("[data-category-id]").on(_sortable_list.default.EVENTS.DROP,((evt,info)=>{if(info.positionChanged){const pendingPromise=new _pending.default("core_customfield/form:categoryid:on:sortablelist-drop");(0,_ajax.call)([{methodname:"core_customfield_move_category",args:{id:info.element.data("category-id"),beforeid:info.targetNextElement.data("category-id")}}])[0].then(pendingPromise.resolve).catch(_notification.default.exception)}evt.stopPropagation()})),new _sortable_list.default("#customfield_catlist .fieldslist tbody",{moveHandlerSelector:".movefield [data-drag-type=move]"}).getDestinationName=(parentElement,afterElement)=>afterElement.length?afterElement.attr("data-field-name")?(0,_str.get_string)("afterfield","customfield",afterElement.attr("data-field-name")):Promise.resolve(""):(0,_str.get_string)("totopofcategory","customfield",getCategoryNameFor(parentElement)),(0,_jquery.default)("[data-field-name]").on(_sortable_list.default.EVENTS.DROP,((evt,info)=>{if(info.positionChanged){const pendingPromise=new _pending.default("core_customfield/form:fieldname:on:sortablelist-drop");(0,_ajax.call)([{methodname:"core_customfield_move_field",args:{id:info.element.data("field-id"),beforeid:info.targetNextElement.data("field-id"),categoryid:Number(info.targetList.closest("[data-category-id]").attr("data-category-id"))}}])[0].then(pendingPromise.resolve).catch(_notification.default.exception)}evt.stopPropagation()})),(0,_jquery.default)("[data-field-name]").on(_sortable_list.default.EVENTS.DRAG,(evt=>{var pendingPromise=new _pending.default("core_customfield/form:fieldname:on:sortablelist-drag");evt.stopPropagation(),_templates.default.render("core_customfield/nofields",{}).then((html=>{rootNode.querySelectorAll(".categorieslist > *").forEach((category=>{const fields=category.querySelectorAll(".field:not(.sortable-list-is-dragged)"),noFields=category.querySelector(".nofields");fields.length||noFields?fields.length&&noFields&&noFields.remove():category.querySelector("tbody").innerHTML=html}))})).then(pendingPromise.resolve).catch(_notification.default.exception)})),(0,_jquery.default)("[data-category-id], [data-field-name]").on(_sortable_list.default.EVENTS.DRAGSTART,((evt,info)=>{setTimeout((()=>{(0,_jquery.default)(".sortable-list-is-dragged").width(info.element.width())}),501)}))})(rootNode)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_sortable_list=_interopRequireDefault(_sortable_list),_templates=_interopRequireDefault(_templates),_jquery=_interopRequireDefault(_jquery);const confirmDelete=(id,type,component,area,itemid)=>{const pendingPromise=new _pending.default("core_customfield/form:confirmDelete");(0,_str.getStrings)([{key:"confirm"},{key:"confirmdelete"+type,component:"core_customfield"},{key:"yes"},{key:"no"}]).then((strings=>_notification.default.confirm(strings[0],strings[1],strings[2],strings[3],(function(){const pendingDeletePromise=new _pending.default("core_customfield/form:confirmDelete");(0,_ajax.call)([{methodname:"field"===type?"core_customfield_delete_field":"core_customfield_delete_category",args:{id:id}},{methodname:"core_customfield_reload_template",args:{component:component,area:area,itemid:itemid}}])[1].then((response=>_templates.default.render("core_customfield/list",response))).then(((html,js)=>_templates.default.replaceNode((0,_jquery.default)('[data-region="list-page"]'),html,js))).then(pendingDeletePromise.resolve).catch(_notification.default.exception)})))).then(pendingPromise.resolve).catch(_notification.default.exception)},getCategoryNameFor=nodeElement=>nodeElement.closest("[data-category-id]").find("[data-inplaceeditable][data-itemtype=category][data-component=core_customfield]").attr("data-value");_exports.init=()=>{const rootNode=document.querySelector("#customfield_catlist"),component=rootNode.dataset.component,area=rootNode.dataset.area,itemid=rootNode.dataset.itemid;rootNode.addEventListener("click",(e=>{const roleHolder=e.target.closest("[data-role]");if(roleHolder)return"deletefield"===roleHolder.dataset.role?(e.preventDefault(),void confirmDelete(roleHolder.dataset.id,"field",component,area,itemid)):"deletecategory"===roleHolder.dataset.role?(e.preventDefault(),void confirmDelete(roleHolder.dataset.id,"category",component,area,itemid)):"addnewcategory"===roleHolder.dataset.role?(e.preventDefault(),void((component,area,itemid)=>{const pendingPromise=new _pending.default("core_customfield/form:createNewCategory");(0,_ajax.call)([{methodname:"core_customfield_create_category",args:{component:component,area:area,itemid:itemid}},{methodname:"core_customfield_reload_template",args:{component:component,area:area,itemid:itemid}}])[1].then((response=>_templates.default.render("core_customfield/list",response))).then(((html,js)=>_templates.default.replaceNode((0,_jquery.default)('[data-region="list-page"]'),html,js))).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)})(component,area,itemid)):"addfield"===roleHolder.dataset.role?(e.preventDefault(),void((element,component,area,itemid)=>{const pendingPromise=new _pending.default("core_customfield/form:createNewField"),returnFocus=element.closest(".action-menu").querySelector(".dropdown-toggle"),form=new _modalform.default({formClass:"core_customfield\\field_config_form",args:{categoryid:element.getAttribute("data-categoryid"),type:element.getAttribute("data-type")},modalConfig:{title:(0,_str.getString)("addingnewcustomfield","core_customfield",element.getAttribute("data-typename"))},returnFocus:returnFocus});form.addEventListener(form.events.FORM_SUBMITTED,(()=>{const pendingCreatedPromise=new _pending.default("core_customfield/form:createdNewField");(0,_ajax.call)([{methodname:"core_customfield_reload_template",args:{component:component,area:area,itemid:itemid}}])[0].then((response=>_templates.default.render("core_customfield/list",response))).then(((html,js)=>_templates.default.replaceNode((0,_jquery.default)('[data-region="list-page"]'),html,js))).then((()=>pendingCreatedPromise.resolve())).catch((()=>window.location.reload()))})),form.show(),pendingPromise.resolve()})(roleHolder,component,area,itemid)):"editfield"===roleHolder.dataset.role?(e.preventDefault(),void((element,component,area,itemid)=>{const pendingPromise=new _pending.default("core_customfield/form:editField"),form=new _modalform.default({formClass:"core_customfield\\field_config_form",args:{id:element.getAttribute("data-id")},modalConfig:{title:(0,_str.getString)("editingfield","core_customfield",element.getAttribute("data-name"))},returnFocus:element});form.addEventListener(form.events.FORM_SUBMITTED,(()=>{const pendingCreatedPromise=new _pending.default("core_customfield/form:createdNewField");(0,_ajax.call)([{methodname:"core_customfield_reload_template",args:{component:component,area:area,itemid:itemid}}])[0].then((response=>_templates.default.render("core_customfield/list",response))).then(((html,js)=>_templates.default.replaceNode((0,_jquery.default)('[data-region="list-page"]'),html,js))).then((()=>pendingCreatedPromise.resolve())).catch((()=>window.location.reload()))})),form.show(),pendingPromise.resolve()})(roleHolder,component,area,itemid)):void 0})),(rootNode=>{new _sortable_list.default("#customfield_catlist .categorieslist",{moveHandlerSelector:".movecategory [data-drag-type=move]"}).getElementName=nodeElement=>Promise.resolve(getCategoryNameFor(nodeElement)),(0,_jquery.default)("[data-category-id]").on(_sortable_list.default.EVENTS.DROP,((evt,info)=>{if(info.positionChanged){const pendingPromise=new _pending.default("core_customfield/form:categoryid:on:sortablelist-drop");(0,_ajax.call)([{methodname:"core_customfield_move_category",args:{id:info.element.data("category-id"),beforeid:info.targetNextElement.data("category-id")}}])[0].then(pendingPromise.resolve).catch(_notification.default.exception)}evt.stopPropagation()})),new _sortable_list.default("#customfield_catlist .fieldslist tbody",{moveHandlerSelector:".movefield [data-drag-type=move]"}).getDestinationName=(parentElement,afterElement)=>afterElement.length?afterElement.attr("data-field-name")?(0,_str.getString)("afterfield","customfield",afterElement.attr("data-field-name")):Promise.resolve(""):(0,_str.getString)("totopofcategory","customfield",getCategoryNameFor(parentElement)),(0,_jquery.default)("[data-field-name]").on(_sortable_list.default.EVENTS.DROP,((evt,info)=>{if(info.positionChanged){const pendingPromise=new _pending.default("core_customfield/form:fieldname:on:sortablelist-drop");(0,_ajax.call)([{methodname:"core_customfield_move_field",args:{id:info.element.data("field-id"),beforeid:info.targetNextElement.data("field-id"),categoryid:Number(info.targetList.closest("[data-category-id]").attr("data-category-id"))}}])[0].then(pendingPromise.resolve).catch(_notification.default.exception)}evt.stopPropagation()})),(0,_jquery.default)("[data-field-name]").on(_sortable_list.default.EVENTS.DRAG,(evt=>{var pendingPromise=new _pending.default("core_customfield/form:fieldname:on:sortablelist-drag");evt.stopPropagation(),_templates.default.render("core_customfield/nofields",{}).then((html=>{rootNode.querySelectorAll(".categorieslist > *").forEach((category=>{const fields=category.querySelectorAll(".field:not(.sortable-list-is-dragged)"),noFields=category.querySelector(".nofields");fields.length||noFields?fields.length&&noFields&&noFields.remove():category.querySelector("tbody").innerHTML=html}))})).then(pendingPromise.resolve).catch(_notification.default.exception)})),(0,_jquery.default)("[data-category-id], [data-field-name]").on(_sortable_list.default.EVENTS.DRAGSTART,((evt,info)=>{setTimeout((()=>{(0,_jquery.default)(".sortable-list-is-dragged").width(info.element.width())}),501)}))})(rootNode)}})); //# sourceMappingURL=form.min.js.map \ No newline at end of file diff --git a/customfield/amd/build/form.min.js.map b/customfield/amd/build/form.min.js.map index 44cb1dc236b..6c09edb203d 100644 --- a/customfield/amd/build/form.min.js.map +++ b/customfield/amd/build/form.min.js.map @@ -1 +1 @@ -{"version":3,"file":"form.min.js","sources":["../src/form.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Custom Field interaction management for Moodle.\n *\n * @module core_customfield/form\n * @copyright 2018 Toni Barbera\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport 'core/inplace_editable';\nimport {call as fetchMany} from 'core/ajax';\nimport {\n get_string as getString,\n get_strings as getStrings,\n} from 'core/str';\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport SortableList from 'core/sortable_list';\nimport Templates from 'core/templates';\nimport jQuery from 'jquery';\n\n/**\n * Display confirmation dialogue\n *\n * @param {Number} id\n * @param {String} type\n * @param {String} component\n * @param {String} area\n * @param {Number} itemid\n */\nconst confirmDelete = (id, type, component, area, itemid) => {\n const pendingPromise = new Pending('core_customfield/form:confirmDelete');\n\n getStrings([\n {'key': 'confirm'},\n {'key': 'confirmdelete' + type, component: 'core_customfield'},\n {'key': 'yes'},\n {'key': 'no'},\n ])\n .then(strings => {\n return Notification.confirm(strings[0], strings[1], strings[2], strings[3], function() {\n const pendingDeletePromise = new Pending('core_customfield/form:confirmDelete');\n fetchMany([\n {\n methodname: (type === 'field') ? 'core_customfield_delete_field' : 'core_customfield_delete_category',\n args: {id},\n },\n {methodname: 'core_customfield_reload_template', args: {component, area, itemid}}\n ])[1]\n .then(response => Templates.render('core_customfield/list', response))\n .then((html, js) => Templates.replaceNode(jQuery('[data-region=\"list-page\"]'), html, js))\n .then(pendingDeletePromise.resolve)\n .catch(Notification.exception);\n });\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n};\n\n\n/**\n * Creates a new custom fields category with default name and updates the list\n *\n * @param {String} component\n * @param {String} area\n * @param {Number} itemid\n */\nconst createNewCategory = (component, area, itemid) => {\n const pendingPromise = new Pending('core_customfield/form:createNewCategory');\n const promises = fetchMany([\n {methodname: 'core_customfield_create_category', args: {component, area, itemid}},\n {methodname: 'core_customfield_reload_template', args: {component, area, itemid}}\n ]);\n\n promises[1].then(response => Templates.render('core_customfield/list', response))\n .then((html, js) => Templates.replaceNode(jQuery('[data-region=\"list-page\"]'), html, js))\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n};\n\n/**\n * Create new custom field\n *\n * @param {HTMLElement} element\n * @param {String} component\n * @param {String} area\n * @param {Number} itemid\n */\nconst createNewField = (element, component, area, itemid) => {\n const pendingPromise = new Pending('core_customfield/form:createNewField');\n\n const returnFocus = element.closest(\".action-menu\").querySelector(\".dropdown-toggle\");\n const form = new ModalForm({\n formClass: \"core_customfield\\\\field_config_form\",\n args: {\n categoryid: element.getAttribute('data-categoryid'),\n type: element.getAttribute('data-type'),\n },\n modalConfig: {\n title: getString('addingnewcustomfield', 'core_customfield', element.getAttribute('data-typename')),\n },\n returnFocus,\n });\n\n form.addEventListener(form.events.FORM_SUBMITTED, () => {\n const pendingCreatedPromise = new Pending('core_customfield/form:createdNewField');\n const promises = fetchMany([\n {methodname: 'core_customfield_reload_template', args: {component: component, area: area, itemid: itemid}}\n ]);\n\n promises[0].then(response => Templates.render('core_customfield/list', response))\n .then((html, js) => Templates.replaceNode(jQuery('[data-region=\"list-page\"]'), html, js))\n .then(() => pendingCreatedPromise.resolve())\n .catch(() => window.location.reload());\n });\n\n form.show();\n\n pendingPromise.resolve();\n};\n\n/**\n * Edit custom field\n *\n * @param {HTMLElement} element\n * @param {String} component\n * @param {String} area\n * @param {Number} itemid\n */\nconst editField = (element, component, area, itemid) => {\n const pendingPromise = new Pending('core_customfield/form:editField');\n\n const form = new ModalForm({\n formClass: \"core_customfield\\\\field_config_form\",\n args: {\n id: element.getAttribute('data-id'),\n },\n modalConfig: {\n title: getString('editingfield', 'core_customfield', element.getAttribute('data-name')),\n },\n returnFocus: element,\n });\n\n form.addEventListener(form.events.FORM_SUBMITTED, () => {\n const pendingCreatedPromise = new Pending('core_customfield/form:createdNewField');\n const promises = fetchMany([\n {methodname: 'core_customfield_reload_template', args: {component: component, area: area, itemid: itemid}}\n ]);\n\n promises[0].then(response => Templates.render('core_customfield/list', response))\n .then((html, js) => Templates.replaceNode(jQuery('[data-region=\"list-page\"]'), html, js))\n .then(() => pendingCreatedPromise.resolve())\n .catch(() => window.location.reload());\n });\n\n form.show();\n\n pendingPromise.resolve();\n};\n\n/**\n * Fetch the category name from an inplace editable, given a child node of that field.\n *\n * @param {NodeElement} nodeElement\n * @returns {String}\n */\nconst getCategoryNameFor = nodeElement => nodeElement\n .closest('[data-category-id]')\n .find('[data-inplaceeditable][data-itemtype=category][data-component=core_customfield]')\n .attr('data-value');\n\nconst setupSortableLists = rootNode => {\n // Sort category.\n const sortCat = new SortableList(\n '#customfield_catlist .categorieslist',\n {\n moveHandlerSelector: '.movecategory [data-drag-type=move]',\n }\n );\n sortCat.getElementName = nodeElement => Promise.resolve(getCategoryNameFor(nodeElement));\n\n // Note: The sortable list currently uses jQuery events.\n jQuery('[data-category-id]').on(SortableList.EVENTS.DROP, (evt, info) => {\n if (info.positionChanged) {\n const pendingPromise = new Pending('core_customfield/form:categoryid:on:sortablelist-drop');\n fetchMany([{\n methodname: 'core_customfield_move_category',\n args: {\n id: info.element.data('category-id'),\n beforeid: info.targetNextElement.data('category-id')\n }\n\n }])[0]\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n }\n evt.stopPropagation(); // Important for nested lists to prevent multiple targets.\n });\n\n // Sort fields.\n var sort = new SortableList(\n '#customfield_catlist .fieldslist tbody',\n {\n moveHandlerSelector: '.movefield [data-drag-type=move]',\n }\n );\n\n sort.getDestinationName = (parentElement, afterElement) => {\n if (!afterElement.length) {\n return getString('totopofcategory', 'customfield', getCategoryNameFor(parentElement));\n } else if (afterElement.attr('data-field-name')) {\n return getString('afterfield', 'customfield', afterElement.attr('data-field-name'));\n } else {\n return Promise.resolve('');\n }\n };\n\n jQuery('[data-field-name]').on(SortableList.EVENTS.DROP, (evt, info) => {\n if (info.positionChanged) {\n const pendingPromise = new Pending('core_customfield/form:fieldname:on:sortablelist-drop');\n fetchMany([{\n methodname: 'core_customfield_move_field',\n args: {\n id: info.element.data('field-id'),\n beforeid: info.targetNextElement.data('field-id'),\n categoryid: Number(info.targetList.closest('[data-category-id]').attr('data-category-id'))\n },\n }])[0]\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n }\n evt.stopPropagation(); // Important for nested lists to prevent multiple targets.\n });\n\n jQuery('[data-field-name]').on(SortableList.EVENTS.DRAG, evt => {\n var pendingPromise = new Pending('core_customfield/form:fieldname:on:sortablelist-drag');\n\n evt.stopPropagation(); // Important for nested lists to prevent multiple targets.\n\n // Refreshing fields tables.\n Templates.render('core_customfield/nofields', {})\n .then(html => {\n rootNode.querySelectorAll('.categorieslist > *')\n .forEach(category => {\n const fields = category.querySelectorAll('.field:not(.sortable-list-is-dragged)');\n const noFields = category.querySelector('.nofields');\n\n if (!fields.length && !noFields) {\n category.querySelector('tbody').innerHTML = html;\n } else if (fields.length && noFields) {\n noFields.remove();\n }\n });\n return;\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n });\n\n jQuery('[data-category-id], [data-field-name]').on(SortableList.EVENTS.DRAGSTART, (evt, info) => {\n setTimeout(() => {\n jQuery('.sortable-list-is-dragged').width(info.element.width());\n }, 501);\n });\n};\n\n/**\n * Initialise the custom fields manager.\n */\nexport const init = () => {\n const rootNode = document.querySelector('#customfield_catlist');\n\n const component = rootNode.dataset.component;\n const area = rootNode.dataset.area;\n const itemid = rootNode.dataset.itemid;\n\n rootNode.addEventListener('click', e => {\n const roleHolder = e.target.closest('[data-role]');\n if (!roleHolder) {\n return;\n }\n\n if (roleHolder.dataset.role === 'deletefield') {\n e.preventDefault();\n\n confirmDelete(roleHolder.dataset.id, 'field', component, area, itemid);\n return;\n }\n\n if (roleHolder.dataset.role === 'deletecategory') {\n e.preventDefault();\n\n confirmDelete(roleHolder.dataset.id, 'category', component, area, itemid);\n return;\n }\n\n if (roleHolder.dataset.role === 'addnewcategory') {\n e.preventDefault();\n createNewCategory(component, area, itemid);\n\n return;\n }\n\n if (roleHolder.dataset.role === 'addfield') {\n e.preventDefault();\n createNewField(roleHolder, component, area, itemid);\n\n return;\n }\n\n if (roleHolder.dataset.role === 'editfield') {\n e.preventDefault();\n editField(roleHolder, component, area, itemid);\n\n return;\n }\n });\n\n setupSortableLists(rootNode, component, area, itemid);\n};\n"],"names":["confirmDelete","id","type","component","area","itemid","pendingPromise","Pending","then","strings","Notification","confirm","pendingDeletePromise","methodname","args","response","Templates","render","html","js","replaceNode","resolve","catch","exception","getCategoryNameFor","nodeElement","closest","find","attr","rootNode","document","querySelector","dataset","addEventListener","e","roleHolder","target","role","preventDefault","createNewCategory","element","returnFocus","form","ModalForm","formClass","categoryid","getAttribute","modalConfig","title","events","FORM_SUBMITTED","pendingCreatedPromise","window","location","reload","show","createNewField","editField","SortableList","moveHandlerSelector","getElementName","Promise","on","EVENTS","DROP","evt","info","positionChanged","data","beforeid","targetNextElement","stopPropagation","getDestinationName","parentElement","afterElement","length","Number","targetList","DRAG","querySelectorAll","forEach","category","fields","noFields","remove","innerHTML","DRAGSTART","setTimeout","width","setupSortableLists"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"form.min.js","sources":["../src/form.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Custom Field interaction management for Moodle.\n *\n * @module core_customfield/form\n * @copyright 2018 Toni Barbera\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport 'core/inplace_editable';\nimport {call as fetchMany} from 'core/ajax';\nimport {\n getString,\n getStrings,\n} from 'core/str';\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport SortableList from 'core/sortable_list';\nimport Templates from 'core/templates';\nimport jQuery from 'jquery';\n\n/**\n * Display confirmation dialogue\n *\n * @param {Number} id\n * @param {String} type\n * @param {String} component\n * @param {String} area\n * @param {Number} itemid\n */\nconst confirmDelete = (id, type, component, area, itemid) => {\n const pendingPromise = new Pending('core_customfield/form:confirmDelete');\n\n getStrings([\n {'key': 'confirm'},\n {'key': 'confirmdelete' + type, component: 'core_customfield'},\n {'key': 'yes'},\n {'key': 'no'},\n ])\n .then(strings => {\n return Notification.confirm(strings[0], strings[1], strings[2], strings[3], function() {\n const pendingDeletePromise = new Pending('core_customfield/form:confirmDelete');\n fetchMany([\n {\n methodname: (type === 'field') ? 'core_customfield_delete_field' : 'core_customfield_delete_category',\n args: {id},\n },\n {methodname: 'core_customfield_reload_template', args: {component, area, itemid}}\n ])[1]\n .then(response => Templates.render('core_customfield/list', response))\n .then((html, js) => Templates.replaceNode(jQuery('[data-region=\"list-page\"]'), html, js))\n .then(pendingDeletePromise.resolve)\n .catch(Notification.exception);\n });\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n};\n\n\n/**\n * Creates a new custom fields category with default name and updates the list\n *\n * @param {String} component\n * @param {String} area\n * @param {Number} itemid\n */\nconst createNewCategory = (component, area, itemid) => {\n const pendingPromise = new Pending('core_customfield/form:createNewCategory');\n const promises = fetchMany([\n {methodname: 'core_customfield_create_category', args: {component, area, itemid}},\n {methodname: 'core_customfield_reload_template', args: {component, area, itemid}}\n ]);\n\n promises[1].then(response => Templates.render('core_customfield/list', response))\n .then((html, js) => Templates.replaceNode(jQuery('[data-region=\"list-page\"]'), html, js))\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n};\n\n/**\n * Create new custom field\n *\n * @param {HTMLElement} element\n * @param {String} component\n * @param {String} area\n * @param {Number} itemid\n */\nconst createNewField = (element, component, area, itemid) => {\n const pendingPromise = new Pending('core_customfield/form:createNewField');\n\n const returnFocus = element.closest(\".action-menu\").querySelector(\".dropdown-toggle\");\n const form = new ModalForm({\n formClass: \"core_customfield\\\\field_config_form\",\n args: {\n categoryid: element.getAttribute('data-categoryid'),\n type: element.getAttribute('data-type'),\n },\n modalConfig: {\n title: getString('addingnewcustomfield', 'core_customfield', element.getAttribute('data-typename')),\n },\n returnFocus,\n });\n\n form.addEventListener(form.events.FORM_SUBMITTED, () => {\n const pendingCreatedPromise = new Pending('core_customfield/form:createdNewField');\n const promises = fetchMany([\n {methodname: 'core_customfield_reload_template', args: {component: component, area: area, itemid: itemid}}\n ]);\n\n promises[0].then(response => Templates.render('core_customfield/list', response))\n .then((html, js) => Templates.replaceNode(jQuery('[data-region=\"list-page\"]'), html, js))\n .then(() => pendingCreatedPromise.resolve())\n .catch(() => window.location.reload());\n });\n\n form.show();\n\n pendingPromise.resolve();\n};\n\n/**\n * Edit custom field\n *\n * @param {HTMLElement} element\n * @param {String} component\n * @param {String} area\n * @param {Number} itemid\n */\nconst editField = (element, component, area, itemid) => {\n const pendingPromise = new Pending('core_customfield/form:editField');\n\n const form = new ModalForm({\n formClass: \"core_customfield\\\\field_config_form\",\n args: {\n id: element.getAttribute('data-id'),\n },\n modalConfig: {\n title: getString('editingfield', 'core_customfield', element.getAttribute('data-name')),\n },\n returnFocus: element,\n });\n\n form.addEventListener(form.events.FORM_SUBMITTED, () => {\n const pendingCreatedPromise = new Pending('core_customfield/form:createdNewField');\n const promises = fetchMany([\n {methodname: 'core_customfield_reload_template', args: {component: component, area: area, itemid: itemid}}\n ]);\n\n promises[0].then(response => Templates.render('core_customfield/list', response))\n .then((html, js) => Templates.replaceNode(jQuery('[data-region=\"list-page\"]'), html, js))\n .then(() => pendingCreatedPromise.resolve())\n .catch(() => window.location.reload());\n });\n\n form.show();\n\n pendingPromise.resolve();\n};\n\n/**\n * Fetch the category name from an inplace editable, given a child node of that field.\n *\n * @param {NodeElement} nodeElement\n * @returns {String}\n */\nconst getCategoryNameFor = nodeElement => nodeElement\n .closest('[data-category-id]')\n .find('[data-inplaceeditable][data-itemtype=category][data-component=core_customfield]')\n .attr('data-value');\n\nconst setupSortableLists = rootNode => {\n // Sort category.\n const sortCat = new SortableList(\n '#customfield_catlist .categorieslist',\n {\n moveHandlerSelector: '.movecategory [data-drag-type=move]',\n }\n );\n sortCat.getElementName = nodeElement => Promise.resolve(getCategoryNameFor(nodeElement));\n\n // Note: The sortable list currently uses jQuery events.\n jQuery('[data-category-id]').on(SortableList.EVENTS.DROP, (evt, info) => {\n if (info.positionChanged) {\n const pendingPromise = new Pending('core_customfield/form:categoryid:on:sortablelist-drop');\n fetchMany([{\n methodname: 'core_customfield_move_category',\n args: {\n id: info.element.data('category-id'),\n beforeid: info.targetNextElement.data('category-id')\n }\n\n }])[0]\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n }\n evt.stopPropagation(); // Important for nested lists to prevent multiple targets.\n });\n\n // Sort fields.\n var sort = new SortableList(\n '#customfield_catlist .fieldslist tbody',\n {\n moveHandlerSelector: '.movefield [data-drag-type=move]',\n }\n );\n\n sort.getDestinationName = (parentElement, afterElement) => {\n if (!afterElement.length) {\n return getString('totopofcategory', 'customfield', getCategoryNameFor(parentElement));\n } else if (afterElement.attr('data-field-name')) {\n return getString('afterfield', 'customfield', afterElement.attr('data-field-name'));\n } else {\n return Promise.resolve('');\n }\n };\n\n jQuery('[data-field-name]').on(SortableList.EVENTS.DROP, (evt, info) => {\n if (info.positionChanged) {\n const pendingPromise = new Pending('core_customfield/form:fieldname:on:sortablelist-drop');\n fetchMany([{\n methodname: 'core_customfield_move_field',\n args: {\n id: info.element.data('field-id'),\n beforeid: info.targetNextElement.data('field-id'),\n categoryid: Number(info.targetList.closest('[data-category-id]').attr('data-category-id'))\n },\n }])[0]\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n }\n evt.stopPropagation(); // Important for nested lists to prevent multiple targets.\n });\n\n jQuery('[data-field-name]').on(SortableList.EVENTS.DRAG, evt => {\n var pendingPromise = new Pending('core_customfield/form:fieldname:on:sortablelist-drag');\n\n evt.stopPropagation(); // Important for nested lists to prevent multiple targets.\n\n // Refreshing fields tables.\n Templates.render('core_customfield/nofields', {})\n .then(html => {\n rootNode.querySelectorAll('.categorieslist > *')\n .forEach(category => {\n const fields = category.querySelectorAll('.field:not(.sortable-list-is-dragged)');\n const noFields = category.querySelector('.nofields');\n\n if (!fields.length && !noFields) {\n category.querySelector('tbody').innerHTML = html;\n } else if (fields.length && noFields) {\n noFields.remove();\n }\n });\n return;\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n });\n\n jQuery('[data-category-id], [data-field-name]').on(SortableList.EVENTS.DRAGSTART, (evt, info) => {\n setTimeout(() => {\n jQuery('.sortable-list-is-dragged').width(info.element.width());\n }, 501);\n });\n};\n\n/**\n * Initialise the custom fields manager.\n */\nexport const init = () => {\n const rootNode = document.querySelector('#customfield_catlist');\n\n const component = rootNode.dataset.component;\n const area = rootNode.dataset.area;\n const itemid = rootNode.dataset.itemid;\n\n rootNode.addEventListener('click', e => {\n const roleHolder = e.target.closest('[data-role]');\n if (!roleHolder) {\n return;\n }\n\n if (roleHolder.dataset.role === 'deletefield') {\n e.preventDefault();\n\n confirmDelete(roleHolder.dataset.id, 'field', component, area, itemid);\n return;\n }\n\n if (roleHolder.dataset.role === 'deletecategory') {\n e.preventDefault();\n\n confirmDelete(roleHolder.dataset.id, 'category', component, area, itemid);\n return;\n }\n\n if (roleHolder.dataset.role === 'addnewcategory') {\n e.preventDefault();\n createNewCategory(component, area, itemid);\n\n return;\n }\n\n if (roleHolder.dataset.role === 'addfield') {\n e.preventDefault();\n createNewField(roleHolder, component, area, itemid);\n\n return;\n }\n\n if (roleHolder.dataset.role === 'editfield') {\n e.preventDefault();\n editField(roleHolder, component, area, itemid);\n\n return;\n }\n });\n\n setupSortableLists(rootNode, component, area, itemid);\n};\n"],"names":["confirmDelete","id","type","component","area","itemid","pendingPromise","Pending","then","strings","Notification","confirm","pendingDeletePromise","methodname","args","response","Templates","render","html","js","replaceNode","resolve","catch","exception","getCategoryNameFor","nodeElement","closest","find","attr","rootNode","document","querySelector","dataset","addEventListener","e","roleHolder","target","role","preventDefault","createNewCategory","element","returnFocus","form","ModalForm","formClass","categoryid","getAttribute","modalConfig","title","events","FORM_SUBMITTED","pendingCreatedPromise","window","location","reload","show","createNewField","editField","SortableList","moveHandlerSelector","getElementName","Promise","on","EVENTS","DROP","evt","info","positionChanged","data","beforeid","targetNextElement","stopPropagation","getDestinationName","parentElement","afterElement","length","Number","targetList","DRAG","querySelectorAll","forEach","category","fields","noFields","remove","innerHTML","DRAGSTART","setTimeout","width","setupSortableLists"],"mappings":"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"} \ No newline at end of file diff --git a/customfield/amd/src/form.js b/customfield/amd/src/form.js index 68c3d75157c..f0bcb279886 100644 --- a/customfield/amd/src/form.js +++ b/customfield/amd/src/form.js @@ -24,8 +24,8 @@ import 'core/inplace_editable'; import {call as fetchMany} from 'core/ajax'; import { - get_string as getString, - get_strings as getStrings, + getString, + getStrings, } from 'core/str'; import ModalForm from 'core_form/modalform'; import Notification from 'core/notification'; diff --git a/grade/amd/build/gradebooksetup_forms.min.js b/grade/amd/build/gradebooksetup_forms.min.js index 503f56d75fe..b01ba8e52c4 100644 --- a/grade/amd/build/gradebooksetup_forms.min.js +++ b/grade/amd/build/gradebooksetup_forms.min.js @@ -5,6 +5,6 @@ define("core_grades/gradebooksetup_forms",["exports","core_form/modalform","core * @module core_grades * @copyright 2023 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU Public License - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification),FormChangeChecker=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(FormChangeChecker);const Selectors_advancedFormLink="a.showadvancedform";_exports.init=()=>{document.addEventListener("click",(event=>{const args={};let formClass=null,title=null,trigger=null;if(event.target.closest('[data-trigger="add-item-form"]')?(event.preventDefault(),trigger=event.target.closest('[data-trigger="add-item-form"]'),formClass="core_grades\\form\\add_item",title="-1"===trigger.getAttribute("data-itemid")?(0,_str.get_string)("newitem","core_grades"):(0,_str.get_string)("itemsedit","core_grades"),args.itemid=trigger.getAttribute("data-itemid")):event.target.closest('[data-trigger="add-category-form"]')?(event.preventDefault(),trigger=event.target.closest('[data-trigger="add-category-form"]'),formClass="core_grades\\form\\add_category",title="-1"===trigger.getAttribute("data-category")?(0,_str.get_string)("newcategory","core_grades"):(0,_str.get_string)("categoryedit","core_grades"),args.category=trigger.getAttribute("data-category")):event.target.closest('[data-trigger="add-outcome-form"]')&&(event.preventDefault(),trigger=event.target.closest('[data-trigger="add-outcome-form"]'),formClass="core_grades\\form\\add_outcome",title="-1"===trigger.getAttribute("data-itemid")?(0,_str.get_string)("newoutcomeitem","core_grades"):(0,_str.get_string)("outcomeitemsedit","core_grades"),args.itemid=trigger.getAttribute("data-itemid")),trigger){args.courseid=trigger.getAttribute("data-courseid"),args.gpr_plugin=trigger.getAttribute("data-gprplugin");const modalForm=new _modalform.default({modalConfig:{title:title},formClass:formClass,args:args,saveButtonText:(0,_str.get_string)("save","core"),returnFocus:trigger});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.assign(event.detail.url):_notification.default.addNotification({type:"error",message:(0,_str.get_string)("saving_failed","core_grades")})})),modalForm.show()}const showAdvancedForm=event.target.closest(Selectors_advancedFormLink);if(showAdvancedForm){event.preventDefault();const form=event.target.closest("form");form.action=showAdvancedForm.href,FormChangeChecker.disableAllChecks(),form.submit()}}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification),FormChangeChecker=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(FormChangeChecker);const Selectors_advancedFormLink="a.showadvancedform";_exports.init=()=>{document.addEventListener("click",(event=>{const args={};let formClass=null,title=null,trigger=null;if(event.target.closest('[data-trigger="add-item-form"]')?(event.preventDefault(),trigger=event.target.closest('[data-trigger="add-item-form"]'),formClass="core_grades\\form\\add_item",title="-1"===trigger.getAttribute("data-itemid")?(0,_str.getString)("newitem","core_grades"):(0,_str.getString)("itemsedit","core_grades"),args.itemid=trigger.getAttribute("data-itemid")):event.target.closest('[data-trigger="add-category-form"]')?(event.preventDefault(),trigger=event.target.closest('[data-trigger="add-category-form"]'),formClass="core_grades\\form\\add_category",title="-1"===trigger.getAttribute("data-category")?(0,_str.getString)("newcategory","core_grades"):(0,_str.getString)("categoryedit","core_grades"),args.category=trigger.getAttribute("data-category")):event.target.closest('[data-trigger="add-outcome-form"]')&&(event.preventDefault(),trigger=event.target.closest('[data-trigger="add-outcome-form"]'),formClass="core_grades\\form\\add_outcome",title="-1"===trigger.getAttribute("data-itemid")?(0,_str.getString)("newoutcomeitem","core_grades"):(0,_str.getString)("outcomeitemsedit","core_grades"),args.itemid=trigger.getAttribute("data-itemid")),trigger){args.courseid=trigger.getAttribute("data-courseid"),args.gpr_plugin=trigger.getAttribute("data-gprplugin");const modalForm=new _modalform.default({modalConfig:{title:title},formClass:formClass,args:args,saveButtonText:(0,_str.getString)("save","core"),returnFocus:trigger});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.assign(event.detail.url):_notification.default.addNotification({type:"error",message:(0,_str.getString)("saving_failed","core_grades")})})),modalForm.show()}const showAdvancedForm=event.target.closest(Selectors_advancedFormLink);if(showAdvancedForm){event.preventDefault();const form=event.target.closest("form");form.action=showAdvancedForm.href,FormChangeChecker.disableAllChecks(),form.submit()}}))}})); //# sourceMappingURL=gradebooksetup_forms.min.js.map \ No newline at end of file diff --git a/grade/amd/build/gradebooksetup_forms.min.js.map b/grade/amd/build/gradebooksetup_forms.min.js.map index 0077e712020..779b03bcab3 100644 --- a/grade/amd/build/gradebooksetup_forms.min.js.map +++ b/grade/amd/build/gradebooksetup_forms.min.js.map @@ -1 +1 @@ -{"version":3,"file":"gradebooksetup_forms.min.js","sources":["../src/gradebooksetup_forms.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 * Prints the add item gradebook form\n *\n * @module core_grades\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU Public License\n */\n\nimport ModalForm from 'core_form/modalform';\nimport {get_string as getString} from 'core/str';\nimport Notification from 'core/notification';\nimport * as FormChangeChecker from 'core_form/changechecker';\n\nconst Selectors = {\n advancedFormLink: 'a.showadvancedform'\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n // Sometimes the trigger does not exist, so lets conditionally add it.\n document.addEventListener('click', event => {\n const args = {};\n\n let formClass = null;\n let title = null;\n let trigger = null;\n if (event.target.closest('[data-trigger=\"add-item-form\"]')) {\n event.preventDefault();\n trigger = event.target.closest('[data-trigger=\"add-item-form\"]');\n formClass = 'core_grades\\\\form\\\\add_item';\n title = trigger.getAttribute('data-itemid') === '-1' ?\n getString('newitem', 'core_grades') : getString('itemsedit', 'core_grades');\n args.itemid = trigger.getAttribute('data-itemid');\n } else if (event.target.closest('[data-trigger=\"add-category-form\"]')) {\n event.preventDefault();\n trigger = event.target.closest('[data-trigger=\"add-category-form\"]');\n formClass = 'core_grades\\\\form\\\\add_category';\n title = trigger.getAttribute('data-category') === '-1' ?\n getString('newcategory', 'core_grades') : getString('categoryedit', 'core_grades');\n args.category = trigger.getAttribute('data-category');\n } else if (event.target.closest('[data-trigger=\"add-outcome-form\"]')) {\n event.preventDefault();\n trigger = event.target.closest('[data-trigger=\"add-outcome-form\"]');\n formClass = 'core_grades\\\\form\\\\add_outcome';\n title = trigger.getAttribute('data-itemid') === '-1' ?\n getString('newoutcomeitem', 'core_grades') : getString('outcomeitemsedit', 'core_grades');\n args.itemid = trigger.getAttribute('data-itemid');\n }\n\n if (trigger) {\n args.courseid = trigger.getAttribute('data-courseid');\n args.gpr_plugin = trigger.getAttribute('data-gprplugin');\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: title,\n },\n formClass: formClass,\n args: args,\n saveButtonText: getString('save', 'core'),\n returnFocus: trigger,\n });\n\n // Show a toast notification when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.assign(event.detail.url);\n } else {\n Notification.addNotification({\n type: 'error',\n message: getString('saving_failed', 'core_grades')\n });\n }\n });\n\n modalForm.show();\n }\n\n const showAdvancedForm = event.target.closest(Selectors.advancedFormLink);\n if (showAdvancedForm) { // Navigate to the advanced form page and cary over any entered data.\n event.preventDefault();\n const form = event.target.closest('form');\n form.action = showAdvancedForm.href;\n // Disable the form change checker as we are going to carry over the data to the advanced form.\n FormChangeChecker.disableAllChecks();\n form.submit();\n }\n });\n};\n"],"names":["Selectors","document","addEventListener","event","args","formClass","title","trigger","target","closest","preventDefault","getAttribute","itemid","category","courseid","gpr_plugin","modalForm","ModalForm","modalConfig","saveButtonText","returnFocus","events","FORM_SUBMITTED","detail","result","window","location","assign","url","addNotification","type","message","show","showAdvancedForm","form","action","href","FormChangeChecker","disableAllChecks","submit"],"mappings":";;;;;;;42BA4BMA,2BACgB,mCAMF,KAEhBC,SAASC,iBAAiB,SAASC,cACzBC,KAAO,OAETC,UAAY,KACZC,MAAQ,KACRC,QAAU,QACVJ,MAAMK,OAAOC,QAAQ,mCACrBN,MAAMO,iBACNH,QAAUJ,MAAMK,OAAOC,QAAQ,kCAC/BJ,UAAY,8BACZC,MAAgD,OAAxCC,QAAQI,aAAa,gBACzB,mBAAU,UAAW,gBAAiB,mBAAU,YAAa,eACjEP,KAAKQ,OAASL,QAAQI,aAAa,gBAC5BR,MAAMK,OAAOC,QAAQ,uCAC5BN,MAAMO,iBACNH,QAAUJ,MAAMK,OAAOC,QAAQ,sCAC/BJ,UAAY,kCACZC,MAAkD,OAA1CC,QAAQI,aAAa,kBACzB,mBAAU,cAAe,gBAAiB,mBAAU,eAAgB,eACxEP,KAAKS,SAAWN,QAAQI,aAAa,kBAC9BR,MAAMK,OAAOC,QAAQ,uCAC5BN,MAAMO,iBACNH,QAAUJ,MAAMK,OAAOC,QAAQ,qCAC/BJ,UAAY,iCACZC,MAAgD,OAAxCC,QAAQI,aAAa,gBACzB,mBAAU,iBAAkB,gBAAiB,mBAAU,mBAAoB,eAC/EP,KAAKQ,OAASL,QAAQI,aAAa,gBAGnCJ,QAAS,CACTH,KAAKU,SAAWP,QAAQI,aAAa,iBACrCP,KAAKW,WAAaR,QAAQI,aAAa,wBAEjCK,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTZ,MAAOA,OAEXD,UAAWA,UACXD,KAAMA,KACNe,gBAAgB,mBAAU,OAAQ,QAClCC,YAAab,UAIjBS,UAAUd,iBAAiBc,UAAUK,OAAOC,gBAAgBnB,QACpDA,MAAMoB,OAAOC,OACbC,OAAOC,SAASC,OAAOxB,MAAMoB,OAAOK,2BAEvBC,gBAAgB,CACzBC,KAAM,QACNC,SAAS,mBAAU,gBAAiB,oBAKhDf,UAAUgB,aAGRC,iBAAmB9B,MAAMK,OAAOC,QAAQT,+BAC1CiC,iBAAkB,CAClB9B,MAAMO,uBACAwB,KAAO/B,MAAMK,OAAOC,QAAQ,QAClCyB,KAAKC,OAASF,iBAAiBG,KAE/BC,kBAAkBC,mBAClBJ,KAAKK"} \ No newline at end of file +{"version":3,"file":"gradebooksetup_forms.min.js","sources":["../src/gradebooksetup_forms.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 * Prints the add item gradebook form\n *\n * @module core_grades\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU Public License\n */\n\nimport ModalForm from 'core_form/modalform';\nimport {getString} from 'core/str';\nimport Notification from 'core/notification';\nimport * as FormChangeChecker from 'core_form/changechecker';\n\nconst Selectors = {\n advancedFormLink: 'a.showadvancedform'\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n // Sometimes the trigger does not exist, so lets conditionally add it.\n document.addEventListener('click', event => {\n const args = {};\n\n let formClass = null;\n let title = null;\n let trigger = null;\n if (event.target.closest('[data-trigger=\"add-item-form\"]')) {\n event.preventDefault();\n trigger = event.target.closest('[data-trigger=\"add-item-form\"]');\n formClass = 'core_grades\\\\form\\\\add_item';\n title = trigger.getAttribute('data-itemid') === '-1' ?\n getString('newitem', 'core_grades') : getString('itemsedit', 'core_grades');\n args.itemid = trigger.getAttribute('data-itemid');\n } else if (event.target.closest('[data-trigger=\"add-category-form\"]')) {\n event.preventDefault();\n trigger = event.target.closest('[data-trigger=\"add-category-form\"]');\n formClass = 'core_grades\\\\form\\\\add_category';\n title = trigger.getAttribute('data-category') === '-1' ?\n getString('newcategory', 'core_grades') : getString('categoryedit', 'core_grades');\n args.category = trigger.getAttribute('data-category');\n } else if (event.target.closest('[data-trigger=\"add-outcome-form\"]')) {\n event.preventDefault();\n trigger = event.target.closest('[data-trigger=\"add-outcome-form\"]');\n formClass = 'core_grades\\\\form\\\\add_outcome';\n title = trigger.getAttribute('data-itemid') === '-1' ?\n getString('newoutcomeitem', 'core_grades') : getString('outcomeitemsedit', 'core_grades');\n args.itemid = trigger.getAttribute('data-itemid');\n }\n\n if (trigger) {\n args.courseid = trigger.getAttribute('data-courseid');\n args.gpr_plugin = trigger.getAttribute('data-gprplugin');\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: title,\n },\n formClass: formClass,\n args: args,\n saveButtonText: getString('save', 'core'),\n returnFocus: trigger,\n });\n\n // Show a toast notification when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.assign(event.detail.url);\n } else {\n Notification.addNotification({\n type: 'error',\n message: getString('saving_failed', 'core_grades')\n });\n }\n });\n\n modalForm.show();\n }\n\n const showAdvancedForm = event.target.closest(Selectors.advancedFormLink);\n if (showAdvancedForm) { // Navigate to the advanced form page and cary over any entered data.\n event.preventDefault();\n const form = event.target.closest('form');\n form.action = showAdvancedForm.href;\n // Disable the form change checker as we are going to carry over the data to the advanced form.\n FormChangeChecker.disableAllChecks();\n form.submit();\n }\n });\n};\n"],"names":["Selectors","document","addEventListener","event","args","formClass","title","trigger","target","closest","preventDefault","getAttribute","itemid","category","courseid","gpr_plugin","modalForm","ModalForm","modalConfig","saveButtonText","returnFocus","events","FORM_SUBMITTED","detail","result","window","location","assign","url","addNotification","type","message","show","showAdvancedForm","form","action","href","FormChangeChecker","disableAllChecks","submit"],"mappings":";;;;;;;42BA4BMA,2BACgB,mCAMF,KAEhBC,SAASC,iBAAiB,SAASC,cACzBC,KAAO,OAETC,UAAY,KACZC,MAAQ,KACRC,QAAU,QACVJ,MAAMK,OAAOC,QAAQ,mCACrBN,MAAMO,iBACNH,QAAUJ,MAAMK,OAAOC,QAAQ,kCAC/BJ,UAAY,8BACZC,MAAgD,OAAxCC,QAAQI,aAAa,gBACzB,kBAAU,UAAW,gBAAiB,kBAAU,YAAa,eACjEP,KAAKQ,OAASL,QAAQI,aAAa,gBAC5BR,MAAMK,OAAOC,QAAQ,uCAC5BN,MAAMO,iBACNH,QAAUJ,MAAMK,OAAOC,QAAQ,sCAC/BJ,UAAY,kCACZC,MAAkD,OAA1CC,QAAQI,aAAa,kBACzB,kBAAU,cAAe,gBAAiB,kBAAU,eAAgB,eACxEP,KAAKS,SAAWN,QAAQI,aAAa,kBAC9BR,MAAMK,OAAOC,QAAQ,uCAC5BN,MAAMO,iBACNH,QAAUJ,MAAMK,OAAOC,QAAQ,qCAC/BJ,UAAY,iCACZC,MAAgD,OAAxCC,QAAQI,aAAa,gBACzB,kBAAU,iBAAkB,gBAAiB,kBAAU,mBAAoB,eAC/EP,KAAKQ,OAASL,QAAQI,aAAa,gBAGnCJ,QAAS,CACTH,KAAKU,SAAWP,QAAQI,aAAa,iBACrCP,KAAKW,WAAaR,QAAQI,aAAa,wBAEjCK,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTZ,MAAOA,OAEXD,UAAWA,UACXD,KAAMA,KACNe,gBAAgB,kBAAU,OAAQ,QAClCC,YAAab,UAIjBS,UAAUd,iBAAiBc,UAAUK,OAAOC,gBAAgBnB,QACpDA,MAAMoB,OAAOC,OACbC,OAAOC,SAASC,OAAOxB,MAAMoB,OAAOK,2BAEvBC,gBAAgB,CACzBC,KAAM,QACNC,SAAS,kBAAU,gBAAiB,oBAKhDf,UAAUgB,aAGRC,iBAAmB9B,MAAMK,OAAOC,QAAQT,+BAC1CiC,iBAAkB,CAClB9B,MAAMO,uBACAwB,KAAO/B,MAAMK,OAAOC,QAAQ,QAClCyB,KAAKC,OAASF,iBAAiBG,KAE/BC,kBAAkBC,mBAClBJ,KAAKK"} \ No newline at end of file diff --git a/grade/amd/src/gradebooksetup_forms.js b/grade/amd/src/gradebooksetup_forms.js index ed2aa1b4b4f..ea61fe7847e 100644 --- a/grade/amd/src/gradebooksetup_forms.js +++ b/grade/amd/src/gradebooksetup_forms.js @@ -22,7 +22,7 @@ */ import ModalForm from 'core_form/modalform'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Notification from 'core/notification'; import * as FormChangeChecker from 'core_form/changechecker'; diff --git a/grade/report/grader/amd/build/collapse.min.js b/grade/report/grader/amd/build/collapse.min.js index ec930c3634a..110c74fd401 100644 --- a/grade/report/grader/amd/build/collapse.min.js +++ b/grade/report/grader/amd/build/collapse.min.js @@ -1,3 +1,3 @@ -define("gradereport_grader/collapse",["exports","gradereport_grader/collapse/repository","core/comboboxsearch/search_combobox","core/templates","core/utils","jquery","core/str","core/custom_interaction_events","core/localstorage","core/loadingicon","core/notification","core/pending"],(function(_exports,Repository,_search_combobox,_templates,_utils,_jquery,_str,_custom_interaction_events,_localstorage,_loadingicon,_notification,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Repository=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Repository),_search_combobox=_interopRequireDefault(_search_combobox),_jquery=_interopRequireDefault(_jquery),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_localstorage=_interopRequireDefault(_localstorage),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const selectors_component=".collapse-columns",selectors_formDropdown=".columnsdropdownform",selectors_formItems={cancel:"cancel",save:"save",checked:'input[type="checkbox"]:checked',currentlyUnchecked:'input[type="checkbox"]:not([data-action="selectall"])'},selectors_hider="hide",selectors_expand="expand",selectors_colVal="[data-col]",selectors_itemVal="[data-itemid]",selectors_content='[data-collapse="content"]',selectors_sort='[data-collapse="sort"]',selectors_expandbutton='[data-collapse="expandbutton"]',selectors_rangerowcell='[data-collapse="rangerowcell"]',selectors_avgrowcell='[data-collapse="avgrowcell"]',selectors_menu='[data-collapse="menu"]',selectors_icons=".data-collapse_gradeicons",selectors_count='[data-collapse="count"]',selectors_placeholder='.collapsecolumndropdown [data-region="placeholder"]',selectors_fullDropdown=".collapsecolumndropdown",countIndicator=document.querySelector(selectors_count);class ColumnSearch extends _search_combobox.default{static init(userID,courseID,defaultSort){return new ColumnSearch(userID,courseID,defaultSort)}constructor(userID,courseID,defaultSort){super(),_defineProperty(this,"userID",-1),_defineProperty(this,"courseID",null),_defineProperty(this,"defaultSort",""),_defineProperty(this,"nodes",[]),_defineProperty(this,"gradeStrings",null),_defineProperty(this,"userStrings",null),_defineProperty(this,"stringMap",[]),this.userID=userID,this.courseID=courseID,this.defaultSort=defaultSort,this.component=document.querySelector(selectors_component);const pendingPromise=new _pending.default;(0,_loadingicon.addIconToContainer)(document.querySelector(".gradeparent")).then((loader=>{setTimeout((()=>{this.getDataset().forEach((item=>{this.nodesUpdate(item)})),this.renderDefault(),loader.remove(),document.querySelector(".gradereport-grader-table").classList.remove("d-none")}),10)})).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)}componentSelector(){return".collapse-columns"}dropdownSelector(){return".searchresultitemscontainer"}triggerSelector(){return".collapsecolumn"}getDataset(){if(!this.dataset){const cols=this.fetchDataset();this.dataset=JSON.parse(cols)?JSON.parse(cols).split(","):[]}return this.datasetSize=this.dataset.length,this.dataset}fetchDataset(){return _localstorage.default.get("gradereport_grader_collapseditems_".concat(this.courseID,"_").concat(this.userID))}setPreferences(){_localstorage.default.set("gradereport_grader_collapseditems_".concat(this.courseID,"_").concat(this.userID),JSON.stringify(this.getDataset().join(",")))}registerClickHandlers(){this.component.addEventListener("click",this.clickHandler.bind(this)),document.addEventListener("click",this.docClickHandler.bind(this))}clickHandler(e){super.clickHandler(e),e.target.closest(selectors_fullDropdown)&&e.stopPropagation()}async docClickHandler(e){var _e$target$closest3;if(e.target.dataset.hider===selectors_hider){var _e$target$closest,_e$target$closest2;e.preventDefault();const desiredToHide=e.target.closest(selectors_colVal)?null===(_e$target$closest=e.target.closest(selectors_colVal))||void 0===_e$target$closest?void 0:_e$target$closest.dataset.col:null===(_e$target$closest2=e.target.closest(selectors_itemVal))||void 0===_e$target$closest2?void 0:_e$target$closest2.dataset.itemid;-1===this.getDataset().indexOf(desiredToHide)&&this.getDataset().push(desiredToHide),await this.prefcountpipe(),this.nodesUpdate(desiredToHide)}if((null===(_e$target$closest3=e.target.closest("button"))||void 0===_e$target$closest3?void 0:_e$target$closest3.dataset.hider)===selectors_expand){var _e$target$closest4,_e$target$closest5,_e$target$closest6,_e$target$closest7;e.preventDefault();const desiredToHide=e.target.closest(selectors_colVal)?null===(_e$target$closest4=e.target.closest(selectors_colVal))||void 0===_e$target$closest4?void 0:_e$target$closest4.dataset.col:null===(_e$target$closest5=e.target.closest(selectors_itemVal))||void 0===_e$target$closest5?void 0:_e$target$closest5.dataset.itemid,idx=this.getDataset().indexOf(desiredToHide);this.getDataset().splice(idx,1),await this.prefcountpipe(),this.nodesUpdate(null===(_e$target$closest6=e.target.closest(selectors_colVal))||void 0===_e$target$closest6?void 0:_e$target$closest6.dataset.col),this.nodesUpdate(null===(_e$target$closest7=e.target.closest(selectors_colVal))||void 0===_e$target$closest7?void 0:_e$target$closest7.dataset.itemid)}}async keyHandler(e){if(super.keyHandler(e),"Tab"===e.key)e.target.closest(this.selectors.input)&&(e.preventDefault(),this.clearSearchButton.focus({preventScroll:!0}))}registerInputEvents(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{this.setSearchTerms(this.searchInput.value),""===this.searchInput.value?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none"),await this.filterrenderpipe()}),300))}registerFormEvents(){const form=this.component.querySelector(selectors_formDropdown),events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events);const selectall=form.querySelector('[data-action="selectall"]');events.forEach((event=>{const submitBtn=form.querySelector('[data-action="'.concat(selectors_formItems.save,'"'));form.addEventListener(event,(e=>{e.stopPropagation();const input=e.target.closest("input");if(input){selectall.checked&&!input.checked&&(selectall.checked=!1);const checkedCount=Array.from(form.querySelectorAll(selectors_formItems.checked)).length;submitBtn.disabled=checkedCount<=0}}),!1),this.searchInput.addEventListener(event,(e=>e.stopPropagation())),this.clearSearchButton.addEventListener(event,(async e=>{e.stopPropagation(),this.searchInput.value="",this.setSearchTerms(this.searchInput.value),await this.filterrenderpipe()})),selectall.addEventListener(event,(e=>{if(e.stopPropagation(),selectall.checked){Array.from(form.querySelectorAll(selectors_formItems.currentlyUnchecked)).forEach((item=>{item.checked=!0})),submitBtn.disabled=!1}else{Array.from(form.querySelectorAll(selectors_formItems.checked)).forEach((item=>{item.checked=!1})),submitBtn.disabled=!0}}))})),form.addEventListener("submit",(async e=>{if(e.preventDefault(),e.submitter.dataset.action===selectors_formItems.cancel)return void(0,_jquery.default)(this.component).dropdown("toggle");[...form.elements].filter((item=>item.checked)).forEach((item=>{const idx=this.getDataset().indexOf(item.dataset.collapse);this.getDataset().splice(idx,1),this.nodesUpdate(item.dataset.collapse)})),selectall.checked=!1,e.submitter.disabled=!0,await this.prefcountpipe()}))}nodesUpdate(item){const colNodesToHide=[...document.querySelectorAll('[data-col="'.concat(item,'"]'))],itemIDNodesToHide=[...document.querySelectorAll('[data-itemid="'.concat(item,'"]'))];this.nodes=[...colNodesToHide,...itemIDNodesToHide],this.updateDisplay()}async prefcountpipe(){this.setPreferences(),this.countUpdate(),await this.filterrenderpipe()}async filterDataset(filterableData){const stringUserMap=await this.fetchRequiredUserStrings(),stringGradeMap=await this.fetchRequiredGradeStrings(),customFieldMap=this.fetchCustomFieldValues();this.stringMap=new Map([...stringGradeMap,...stringUserMap,...customFieldMap]);const searching=filterableData.map((s=>{var _mapObj$itemname,_mapObj$category;const mapObj=this.stringMap.get(s);return void 0===mapObj?{key:s,string:s}:{key:s,string:null!==(_mapObj$itemname=mapObj.itemname)&&void 0!==_mapObj$itemname?_mapObj$itemname:this.stringMap.get(s),category:null!==(_mapObj$category=mapObj.category)&&void 0!==_mapObj$category?_mapObj$category:""}}));return""===this.getPreppedSearchTerm()?searching:searching.filter((col=>col.string.toString().toLowerCase().includes(this.getPreppedSearchTerm())))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((column=>{var _column$string,_column$category;return{name:column.key,displayName:null!==(_column$string=column.string)&&void 0!==_column$string?_column$string:column.key,category:null!==(_column$category=column.category)&&void 0!==_column$category?_column$category:""}})))}updateDisplay(){this.nodes.forEach((element=>{const content=element.querySelector(selectors_content),sort=element.querySelector(selectors_sort),expandButton=element.querySelector(selectors_expandbutton),rangeRowCell=element.querySelector(selectors_rangerowcell),avgRowCell=element.querySelector(selectors_avgrowcell),nodeSet=[element.querySelector(selectors_menu),element.querySelector(selectors_icons),content];if(element.classList.contains("cell"))if(null!==sort&&(window.location=this.defaultSort),null===content){const rowCell=null!=avgRowCell?avgRowCell:rangeRowCell;null==rowCell||rowCell.classList.toggle("d-none"),null==rowCell||rowCell.setAttribute("aria-hidden",null!=rowCell&&rowCell.classList.contains("d-none")?"true":"false")}else content.classList.contains("d-none")?(element.classList.remove("collapsed"),content.childNodes.length>1&&content.classList.add("d-flex"),nodeSet.forEach((node=>{null==node||node.classList.remove("d-none"),null==node||node.setAttribute("aria-hidden","false")})),null==expandButton||expandButton.classList.add("d-none"),null==expandButton||expandButton.setAttribute("aria-hidden","true")):(element.classList.add("collapsed"),content.classList.remove("d-flex"),nodeSet.forEach((node=>{null==node||node.classList.add("d-none"),null==node||node.setAttribute("aria-hidden","true")})),null==expandButton||expandButton.classList.remove("d-none"),null==expandButton||expandButton.setAttribute("aria-hidden","false"))}))}countUpdate(){countIndicator.textContent=this.getDatasetSize(),this.getDatasetSize()>0?(this.component.parentElement.classList.add("d-flex"),this.component.parentElement.classList.remove("d-none")):(this.component.parentElement.classList.remove("d-flex"),this.component.parentElement.classList.add("d-none"))}async renderDefault(){this.setMatchedResults(await this.filterDataset(this.getDataset())),this.filterMatchDataset(),this.countUpdate();const{html:html,js:js}=await(0,_templates.renderForPromise)("gradereport_grader/collapse/collapsebody",{results:this.getMatchedResults(),userid:this.userID});(0,_templates.replaceNode)(selectors_placeholder,html,js),this.updateNodes(),this.registerFormEvents(),this.registerInputEvents(),this.$component.on("shown.bs.dropdown",(()=>{this.searchInput.focus({preventScroll:!0})}))}async renderDropdown(){const selectall=this.component.querySelector(selectors_formDropdown).querySelector('[data-action="selectall"]'),{html:html,js:js}=await(0,_templates.renderForPromise)("gradereport_grader/collapse/collapseresults",{results:this.getMatchedResults(),searchTerm:this.getSearchTerm()});selectall.disabled=0===this.getMatchedResults().length,(0,_templates.replaceNodeContents)(this.getHTMLElements().searchDropdown,html,js)}fetchCustomFieldValues(){return[...document.querySelectorAll("[data-collapse-name]")].map((field=>[field.parentElement.dataset.col,field.dataset.collapseName]))}fetchRequiredUserStrings(){if(!this.userStrings){const requiredStrings=["username","firstname","lastname","email","city","country","department","institution","idnumber","phone1","phone2"];this.userStrings=(0,_str.get_strings)(requiredStrings.map((key=>({key:key})))).then((stringArray=>new Map(requiredStrings.map(((key,index)=>[key,stringArray[index]])))))}return this.userStrings}fetchRequiredGradeStrings(){return this.gradeStrings||(this.gradeStrings=Repository.gradeItems(this.courseID).then((result=>new Map(result.gradeItems.map((key=>[key.id,key])))))),this.gradeStrings}}return _exports.default=ColumnSearch,_exports.default})); +define("gradereport_grader/collapse",["exports","gradereport_grader/collapse/repository","core/comboboxsearch/search_combobox","core/templates","core/utils","jquery","core/str","core/custom_interaction_events","core/localstorage","core/loadingicon","core/notification","core/pending"],(function(_exports,Repository,_search_combobox,_templates,_utils,_jquery,_str,_custom_interaction_events,_localstorage,_loadingicon,_notification,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Repository=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Repository),_search_combobox=_interopRequireDefault(_search_combobox),_jquery=_interopRequireDefault(_jquery),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_localstorage=_interopRequireDefault(_localstorage),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const selectors_component=".collapse-columns",selectors_formDropdown=".columnsdropdownform",selectors_formItems={cancel:"cancel",save:"save",checked:'input[type="checkbox"]:checked',currentlyUnchecked:'input[type="checkbox"]:not([data-action="selectall"])'},selectors_hider="hide",selectors_expand="expand",selectors_colVal="[data-col]",selectors_itemVal="[data-itemid]",selectors_content='[data-collapse="content"]',selectors_sort='[data-collapse="sort"]',selectors_expandbutton='[data-collapse="expandbutton"]',selectors_rangerowcell='[data-collapse="rangerowcell"]',selectors_avgrowcell='[data-collapse="avgrowcell"]',selectors_menu='[data-collapse="menu"]',selectors_icons=".data-collapse_gradeicons",selectors_count='[data-collapse="count"]',selectors_placeholder='.collapsecolumndropdown [data-region="placeholder"]',selectors_fullDropdown=".collapsecolumndropdown",countIndicator=document.querySelector(selectors_count);class ColumnSearch extends _search_combobox.default{static init(userID,courseID,defaultSort){return new ColumnSearch(userID,courseID,defaultSort)}constructor(userID,courseID,defaultSort){super(),_defineProperty(this,"userID",-1),_defineProperty(this,"courseID",null),_defineProperty(this,"defaultSort",""),_defineProperty(this,"nodes",[]),_defineProperty(this,"gradeStrings",null),_defineProperty(this,"userStrings",null),_defineProperty(this,"stringMap",[]),this.userID=userID,this.courseID=courseID,this.defaultSort=defaultSort,this.component=document.querySelector(selectors_component);const pendingPromise=new _pending.default;(0,_loadingicon.addIconToContainer)(document.querySelector(".gradeparent")).then((loader=>{setTimeout((()=>{this.getDataset().forEach((item=>{this.nodesUpdate(item)})),this.renderDefault(),loader.remove(),document.querySelector(".gradereport-grader-table").classList.remove("d-none")}),10)})).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)}componentSelector(){return".collapse-columns"}dropdownSelector(){return".searchresultitemscontainer"}triggerSelector(){return".collapsecolumn"}getDataset(){if(!this.dataset){const cols=this.fetchDataset();this.dataset=JSON.parse(cols)?JSON.parse(cols).split(","):[]}return this.datasetSize=this.dataset.length,this.dataset}fetchDataset(){return _localstorage.default.get("gradereport_grader_collapseditems_".concat(this.courseID,"_").concat(this.userID))}setPreferences(){_localstorage.default.set("gradereport_grader_collapseditems_".concat(this.courseID,"_").concat(this.userID),JSON.stringify(this.getDataset().join(",")))}registerClickHandlers(){this.component.addEventListener("click",this.clickHandler.bind(this)),document.addEventListener("click",this.docClickHandler.bind(this))}clickHandler(e){super.clickHandler(e),e.target.closest(selectors_fullDropdown)&&e.stopPropagation()}async docClickHandler(e){var _e$target$closest3;if(e.target.dataset.hider===selectors_hider){var _e$target$closest,_e$target$closest2;e.preventDefault();const desiredToHide=e.target.closest(selectors_colVal)?null===(_e$target$closest=e.target.closest(selectors_colVal))||void 0===_e$target$closest?void 0:_e$target$closest.dataset.col:null===(_e$target$closest2=e.target.closest(selectors_itemVal))||void 0===_e$target$closest2?void 0:_e$target$closest2.dataset.itemid;-1===this.getDataset().indexOf(desiredToHide)&&this.getDataset().push(desiredToHide),await this.prefcountpipe(),this.nodesUpdate(desiredToHide)}if((null===(_e$target$closest3=e.target.closest("button"))||void 0===_e$target$closest3?void 0:_e$target$closest3.dataset.hider)===selectors_expand){var _e$target$closest4,_e$target$closest5,_e$target$closest6,_e$target$closest7;e.preventDefault();const desiredToHide=e.target.closest(selectors_colVal)?null===(_e$target$closest4=e.target.closest(selectors_colVal))||void 0===_e$target$closest4?void 0:_e$target$closest4.dataset.col:null===(_e$target$closest5=e.target.closest(selectors_itemVal))||void 0===_e$target$closest5?void 0:_e$target$closest5.dataset.itemid,idx=this.getDataset().indexOf(desiredToHide);this.getDataset().splice(idx,1),await this.prefcountpipe(),this.nodesUpdate(null===(_e$target$closest6=e.target.closest(selectors_colVal))||void 0===_e$target$closest6?void 0:_e$target$closest6.dataset.col),this.nodesUpdate(null===(_e$target$closest7=e.target.closest(selectors_colVal))||void 0===_e$target$closest7?void 0:_e$target$closest7.dataset.itemid)}}async keyHandler(e){if(super.keyHandler(e),"Tab"===e.key)e.target.closest(this.selectors.input)&&(e.preventDefault(),this.clearSearchButton.focus({preventScroll:!0}))}registerInputEvents(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{this.setSearchTerms(this.searchInput.value),""===this.searchInput.value?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none"),await this.filterrenderpipe()}),300))}registerFormEvents(){const form=this.component.querySelector(selectors_formDropdown),events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events);const selectall=form.querySelector('[data-action="selectall"]');events.forEach((event=>{const submitBtn=form.querySelector('[data-action="'.concat(selectors_formItems.save,'"'));form.addEventListener(event,(e=>{e.stopPropagation();const input=e.target.closest("input");if(input){selectall.checked&&!input.checked&&(selectall.checked=!1);const checkedCount=Array.from(form.querySelectorAll(selectors_formItems.checked)).length;submitBtn.disabled=checkedCount<=0}}),!1),this.searchInput.addEventListener(event,(e=>e.stopPropagation())),this.clearSearchButton.addEventListener(event,(async e=>{e.stopPropagation(),this.searchInput.value="",this.setSearchTerms(this.searchInput.value),await this.filterrenderpipe()})),selectall.addEventListener(event,(e=>{if(e.stopPropagation(),selectall.checked){Array.from(form.querySelectorAll(selectors_formItems.currentlyUnchecked)).forEach((item=>{item.checked=!0})),submitBtn.disabled=!1}else{Array.from(form.querySelectorAll(selectors_formItems.checked)).forEach((item=>{item.checked=!1})),submitBtn.disabled=!0}}))})),form.addEventListener("submit",(async e=>{if(e.preventDefault(),e.submitter.dataset.action===selectors_formItems.cancel)return void(0,_jquery.default)(this.component).dropdown("toggle");[...form.elements].filter((item=>item.checked)).forEach((item=>{const idx=this.getDataset().indexOf(item.dataset.collapse);this.getDataset().splice(idx,1),this.nodesUpdate(item.dataset.collapse)})),selectall.checked=!1,e.submitter.disabled=!0,await this.prefcountpipe()}))}nodesUpdate(item){const colNodesToHide=[...document.querySelectorAll('[data-col="'.concat(item,'"]'))],itemIDNodesToHide=[...document.querySelectorAll('[data-itemid="'.concat(item,'"]'))];this.nodes=[...colNodesToHide,...itemIDNodesToHide],this.updateDisplay()}async prefcountpipe(){this.setPreferences(),this.countUpdate(),await this.filterrenderpipe()}async filterDataset(filterableData){const stringUserMap=await this.fetchRequiredUserStrings(),stringGradeMap=await this.fetchRequiredGradeStrings(),customFieldMap=this.fetchCustomFieldValues();this.stringMap=new Map([...stringGradeMap,...stringUserMap,...customFieldMap]);const searching=filterableData.map((s=>{var _mapObj$itemname,_mapObj$category;const mapObj=this.stringMap.get(s);return void 0===mapObj?{key:s,string:s}:{key:s,string:null!==(_mapObj$itemname=mapObj.itemname)&&void 0!==_mapObj$itemname?_mapObj$itemname:this.stringMap.get(s),category:null!==(_mapObj$category=mapObj.category)&&void 0!==_mapObj$category?_mapObj$category:""}}));return""===this.getPreppedSearchTerm()?searching:searching.filter((col=>col.string.toString().toLowerCase().includes(this.getPreppedSearchTerm())))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((column=>{var _column$string,_column$category;return{name:column.key,displayName:null!==(_column$string=column.string)&&void 0!==_column$string?_column$string:column.key,category:null!==(_column$category=column.category)&&void 0!==_column$category?_column$category:""}})))}updateDisplay(){this.nodes.forEach((element=>{const content=element.querySelector(selectors_content),sort=element.querySelector(selectors_sort),expandButton=element.querySelector(selectors_expandbutton),rangeRowCell=element.querySelector(selectors_rangerowcell),avgRowCell=element.querySelector(selectors_avgrowcell),nodeSet=[element.querySelector(selectors_menu),element.querySelector(selectors_icons),content];if(element.classList.contains("cell"))if(null!==sort&&(window.location=this.defaultSort),null===content){const rowCell=null!=avgRowCell?avgRowCell:rangeRowCell;null==rowCell||rowCell.classList.toggle("d-none"),null==rowCell||rowCell.setAttribute("aria-hidden",null!=rowCell&&rowCell.classList.contains("d-none")?"true":"false")}else content.classList.contains("d-none")?(element.classList.remove("collapsed"),content.childNodes.length>1&&content.classList.add("d-flex"),nodeSet.forEach((node=>{null==node||node.classList.remove("d-none"),null==node||node.setAttribute("aria-hidden","false")})),null==expandButton||expandButton.classList.add("d-none"),null==expandButton||expandButton.setAttribute("aria-hidden","true")):(element.classList.add("collapsed"),content.classList.remove("d-flex"),nodeSet.forEach((node=>{null==node||node.classList.add("d-none"),null==node||node.setAttribute("aria-hidden","true")})),null==expandButton||expandButton.classList.remove("d-none"),null==expandButton||expandButton.setAttribute("aria-hidden","false"))}))}countUpdate(){countIndicator.textContent=this.getDatasetSize(),this.getDatasetSize()>0?(this.component.parentElement.classList.add("d-flex"),this.component.parentElement.classList.remove("d-none")):(this.component.parentElement.classList.remove("d-flex"),this.component.parentElement.classList.add("d-none"))}async renderDefault(){this.setMatchedResults(await this.filterDataset(this.getDataset())),this.filterMatchDataset(),this.countUpdate();const{html:html,js:js}=await(0,_templates.renderForPromise)("gradereport_grader/collapse/collapsebody",{results:this.getMatchedResults(),userid:this.userID});(0,_templates.replaceNode)(selectors_placeholder,html,js),this.updateNodes(),this.registerFormEvents(),this.registerInputEvents(),this.$component.on("shown.bs.dropdown",(()=>{this.searchInput.focus({preventScroll:!0})}))}async renderDropdown(){const selectall=this.component.querySelector(selectors_formDropdown).querySelector('[data-action="selectall"]'),{html:html,js:js}=await(0,_templates.renderForPromise)("gradereport_grader/collapse/collapseresults",{results:this.getMatchedResults(),searchTerm:this.getSearchTerm()});selectall.disabled=0===this.getMatchedResults().length,(0,_templates.replaceNodeContents)(this.getHTMLElements().searchDropdown,html,js)}fetchCustomFieldValues(){return[...document.querySelectorAll("[data-collapse-name]")].map((field=>[field.parentElement.dataset.col,field.dataset.collapseName]))}fetchRequiredUserStrings(){if(!this.userStrings){const requiredStrings=["username","firstname","lastname","email","city","country","department","institution","idnumber","phone1","phone2"];this.userStrings=(0,_str.getStrings)(requiredStrings.map((key=>({key:key})))).then((stringArray=>new Map(requiredStrings.map(((key,index)=>[key,stringArray[index]])))))}return this.userStrings}fetchRequiredGradeStrings(){return this.gradeStrings||(this.gradeStrings=Repository.gradeItems(this.courseID).then((result=>new Map(result.gradeItems.map((key=>[key.id,key])))))),this.gradeStrings}}return _exports.default=ColumnSearch,_exports.default})); //# sourceMappingURL=collapse.min.js.map \ No newline at end of file diff --git a/grade/report/grader/amd/build/collapse.min.js.map b/grade/report/grader/amd/build/collapse.min.js.map index 7613d890bee..2635d3e4b90 100644 --- a/grade/report/grader/amd/build/collapse.min.js.map +++ b/grade/report/grader/amd/build/collapse.min.js.map @@ -1 +1 @@ -{"version":3,"file":"collapse.min.js","sources":["../src/collapse.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 * Allow the user to show and hide columns of the report at will.\n *\n * @module gradereport_grader/collapse\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport * as Repository from 'gradereport_grader/collapse/repository';\nimport search_combobox from 'core/comboboxsearch/search_combobox';\nimport {renderForPromise, replaceNodeContents, replaceNode} from 'core/templates';\nimport {debounce} from 'core/utils';\nimport $ from 'jquery';\nimport {get_strings as getStrings} from 'core/str';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport storage from 'core/localstorage';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\n\n// Contain our selectors within this file until they could be of use elsewhere.\nconst selectors = {\n component: '.collapse-columns',\n formDropdown: '.columnsdropdownform',\n formItems: {\n cancel: 'cancel',\n save: 'save',\n checked: 'input[type=\"checkbox\"]:checked',\n currentlyUnchecked: 'input[type=\"checkbox\"]:not([data-action=\"selectall\"])',\n },\n hider: 'hide',\n expand: 'expand',\n colVal: '[data-col]',\n itemVal: '[data-itemid]',\n content: '[data-collapse=\"content\"]',\n sort: '[data-collapse=\"sort\"]',\n expandbutton: '[data-collapse=\"expandbutton\"]',\n rangerowcell: '[data-collapse=\"rangerowcell\"]',\n avgrowcell: '[data-collapse=\"avgrowcell\"]',\n menu: '[data-collapse=\"menu\"]',\n icons: '.data-collapse_gradeicons',\n count: '[data-collapse=\"count\"]',\n placeholder: '.collapsecolumndropdown [data-region=\"placeholder\"]',\n fullDropdown: '.collapsecolumndropdown',\n};\n\nconst countIndicator = document.querySelector(selectors.count);\n\nexport default class ColumnSearch extends search_combobox {\n\n userID = -1;\n courseID = null;\n defaultSort = '';\n\n nodes = [];\n\n gradeStrings = null;\n userStrings = null;\n stringMap = [];\n\n static init(userID, courseID, defaultSort) {\n return new ColumnSearch(userID, courseID, defaultSort);\n }\n\n constructor(userID, courseID, defaultSort) {\n super();\n this.userID = userID;\n this.courseID = courseID;\n this.defaultSort = defaultSort;\n this.component = document.querySelector(selectors.component);\n\n const pendingPromise = new Pending();\n // Display a loader whilst collapsing appropriate columns (based on the locally stored state for the current user).\n addIconToContainer(document.querySelector('.gradeparent')).then((loader) => {\n setTimeout(() => {\n // Get the users' checked columns to change.\n this.getDataset().forEach((item) => {\n this.nodesUpdate(item);\n });\n this.renderDefault();\n\n // Once the grade categories have been re-collapsed, remove the loader and display the Gradebook setup content.\n loader.remove();\n document.querySelector('.gradereport-grader-table').classList.remove('d-none');\n }, 10);\n }).then(() => pendingPromise.resolve()).catch(Notification.exception);\n }\n\n /**\n * The overall div that contains the searching widget.\n *\n * @returns {string}\n */\n componentSelector() {\n return '.collapse-columns';\n }\n\n /**\n * The dropdown div that contains the searching widget result space.\n *\n * @returns {string}\n */\n dropdownSelector() {\n return '.searchresultitemscontainer';\n }\n\n /**\n * The triggering div that contains the searching widget.\n *\n * @returns {string}\n */\n triggerSelector() {\n return '.collapsecolumn';\n }\n\n /**\n * Return the dataset that we will be searching upon.\n *\n * @returns {Array}\n */\n getDataset() {\n if (!this.dataset) {\n const cols = this.fetchDataset();\n this.dataset = JSON.parse(cols) ? JSON.parse(cols).split(',') : [];\n }\n this.datasetSize = this.dataset.length;\n return this.dataset;\n }\n\n /**\n * Get the data we will be searching against in this component.\n *\n * @returns {string}\n */\n fetchDataset() {\n return storage.get(`gradereport_grader_collapseditems_${this.courseID}_${this.userID}`);\n }\n\n /**\n * Given a user performs an action, update the users' preferences.\n */\n setPreferences() {\n storage.set(`gradereport_grader_collapseditems_${this.courseID}_${this.userID}`,\n JSON.stringify(this.getDataset().join(','))\n );\n }\n\n /**\n * Register clickable event listeners.\n */\n registerClickHandlers() {\n // Register click events within the component.\n this.component.addEventListener('click', this.clickHandler.bind(this));\n\n document.addEventListener('click', this.docClickHandler.bind(this));\n }\n\n /**\n * The handler for when a user interacts with the component.\n *\n * @param {MouseEvent} e The triggering event that we are working with.\n */\n clickHandler(e) {\n super.clickHandler(e);\n // Prevent BS from closing the dropdown if they click elsewhere within the dropdown besides the form.\n if (e.target.closest(selectors.fullDropdown)) {\n e.stopPropagation();\n }\n }\n\n /**\n * Externally defined click function to improve memory handling.\n *\n * @param {MouseEvent} e\n * @returns {Promise}\n */\n async docClickHandler(e) {\n if (e.target.dataset.hider === selectors.hider) {\n e.preventDefault();\n const desiredToHide = e.target.closest(selectors.colVal) ?\n e.target.closest(selectors.colVal)?.dataset.col :\n e.target.closest(selectors.itemVal)?.dataset.itemid;\n const idx = this.getDataset().indexOf(desiredToHide);\n if (idx === -1) {\n this.getDataset().push(desiredToHide);\n }\n await this.prefcountpipe();\n\n this.nodesUpdate(desiredToHide);\n }\n\n if (e.target.closest('button')?.dataset.hider === selectors.expand) {\n e.preventDefault();\n const desiredToHide = e.target.closest(selectors.colVal) ?\n e.target.closest(selectors.colVal)?.dataset.col :\n e.target.closest(selectors.itemVal)?.dataset.itemid;\n const idx = this.getDataset().indexOf(desiredToHide);\n this.getDataset().splice(idx, 1);\n\n await this.prefcountpipe();\n\n this.nodesUpdate(e.target.closest(selectors.colVal)?.dataset.col);\n this.nodesUpdate(e.target.closest(selectors.colVal)?.dataset.itemid);\n }\n }\n\n /**\n * The handler for when a user presses a key within the component.\n *\n * @param {KeyboardEvent} e The triggering event that we are working with.\n */\n async keyHandler(e) {\n super.keyHandler(e);\n\n // Switch the key presses to handle keyboard nav.\n switch (e.key) {\n case 'Tab':\n if (e.target.closest(this.selectors.input)) {\n e.preventDefault();\n this.clearSearchButton.focus({preventScroll: true});\n }\n break;\n }\n }\n\n /**\n * Handle any keyboard inputs.\n */\n registerInputEvents() {\n // Register & handle the text input.\n this.searchInput.addEventListener('input', debounce(async() => {\n this.setSearchTerms(this.searchInput.value);\n // We can also require a set amount of input before search.\n if (this.searchInput.value === '') {\n // Hide the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.add('d-none');\n } else {\n // Display the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.remove('d-none');\n }\n // User has given something for us to filter against.\n await this.filterrenderpipe();\n }, 300));\n }\n\n /**\n * Handle the form submission within the dropdown.\n */\n registerFormEvents() {\n const form = this.component.querySelector(selectors.formDropdown);\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n const selectall = form.querySelector('[data-action=\"selectall\"]');\n\n // Register clicks & keyboard form handling.\n events.forEach((event) => {\n const submitBtn = form.querySelector(`[data-action=\"${selectors.formItems.save}\"`);\n form.addEventListener(event, (e) => {\n // Stop Bootstrap from being clever.\n e.stopPropagation();\n const input = e.target.closest('input');\n if (input) {\n // If the user is unchecking an item, we need to uncheck the select all if it's checked.\n if (selectall.checked && !input.checked) {\n selectall.checked = false;\n }\n const checkedCount = Array.from(form.querySelectorAll(selectors.formItems.checked)).length;\n // Check if any are clicked or not then change disabled.\n submitBtn.disabled = checkedCount <= 0;\n }\n }, false);\n\n // Stop Bootstrap from being clever.\n this.searchInput.addEventListener(event, e => e.stopPropagation());\n this.clearSearchButton.addEventListener(event, async(e) => {\n e.stopPropagation();\n this.searchInput.value = '';\n this.setSearchTerms(this.searchInput.value);\n await this.filterrenderpipe();\n });\n selectall.addEventListener(event, (e) => {\n // Stop Bootstrap from being clever.\n e.stopPropagation();\n if (!selectall.checked) {\n const touncheck = Array.from(form.querySelectorAll(selectors.formItems.checked));\n touncheck.forEach(item => {\n item.checked = false;\n });\n submitBtn.disabled = true;\n } else {\n const currentUnchecked = Array.from(form.querySelectorAll(selectors.formItems.currentlyUnchecked));\n currentUnchecked.forEach(item => {\n item.checked = true;\n });\n submitBtn.disabled = false;\n }\n });\n });\n\n form.addEventListener('submit', async(e) => {\n e.preventDefault();\n if (e.submitter.dataset.action === selectors.formItems.cancel) {\n $(this.component).dropdown('toggle');\n return;\n }\n // Get the users' checked columns to change.\n const checkedItems = [...form.elements].filter(item => item.checked);\n checkedItems.forEach((item) => {\n const idx = this.getDataset().indexOf(item.dataset.collapse);\n this.getDataset().splice(idx, 1);\n this.nodesUpdate(item.dataset.collapse);\n });\n // Reset the check all & submit to false just in case.\n selectall.checked = false;\n e.submitter.disabled = true;\n await this.prefcountpipe();\n });\n }\n\n nodesUpdate(item) {\n const colNodesToHide = [...document.querySelectorAll(`[data-col=\"${item}\"]`)];\n const itemIDNodesToHide = [...document.querySelectorAll(`[data-itemid=\"${item}\"]`)];\n this.nodes = [...colNodesToHide, ...itemIDNodesToHide];\n this.updateDisplay();\n }\n\n /**\n * Update the user preferences, count display then render the results.\n *\n * @returns {Promise}\n */\n async prefcountpipe() {\n this.setPreferences();\n this.countUpdate();\n await this.filterrenderpipe();\n }\n\n /**\n * Dictate to the search component how and what we want to match upon.\n *\n * @param {Array} filterableData\n * @returns {Array} An array of objects containing the system reference and the user readable value.\n */\n async filterDataset(filterableData) {\n const stringUserMap = await this.fetchRequiredUserStrings();\n const stringGradeMap = await this.fetchRequiredGradeStrings();\n // Custom user profile fields are not in our string map and need a bit of extra love.\n const customFieldMap = this.fetchCustomFieldValues();\n this.stringMap = new Map([...stringGradeMap, ...stringUserMap, ...customFieldMap]);\n\n const searching = filterableData.map(s => {\n const mapObj = this.stringMap.get(s);\n if (mapObj === undefined) {\n return {key: s, string: s};\n }\n return {\n key: s,\n string: mapObj.itemname ?? this.stringMap.get(s),\n category: mapObj.category ?? '',\n };\n });\n // Sometimes we just want to show everything.\n if (this.getPreppedSearchTerm() === '') {\n return searching;\n }\n // Other times we want to actually filter the content.\n return searching.filter((col) => {\n return col.string.toString().toLowerCase().includes(this.getPreppedSearchTerm());\n });\n }\n\n /**\n * Given we have a subset of the dataset, set the field that we matched upon to inform the end user.\n */\n filterMatchDataset() {\n this.setMatchedResults(\n this.getMatchedResults().map((column) => {\n return {\n name: column.key,\n displayName: column.string ?? column.key,\n category: column.category ?? '',\n };\n })\n );\n }\n\n /**\n * With an array of nodes, switch their classes and values.\n */\n updateDisplay() {\n this.nodes.forEach((element) => {\n const content = element.querySelector(selectors.content);\n const sort = element.querySelector(selectors.sort);\n const expandButton = element.querySelector(selectors.expandbutton);\n const rangeRowCell = element.querySelector(selectors.rangerowcell);\n const avgRowCell = element.querySelector(selectors.avgrowcell);\n const nodeSet = [\n element.querySelector(selectors.menu),\n element.querySelector(selectors.icons),\n content\n ];\n\n // This can be further improved to reduce redundant similar calls.\n if (element.classList.contains('cell')) {\n // The column is actively being sorted, lets reset that and reload the page.\n if (sort !== null) {\n window.location = this.defaultSort;\n }\n if (content === null) {\n // If it's not a content cell, it must be an overall average or a range cell.\n const rowCell = avgRowCell ?? rangeRowCell;\n\n rowCell?.classList.toggle('d-none');\n rowCell?.setAttribute('aria-hidden',\n rowCell?.classList.contains('d-none') ? 'true' : 'false');\n } else if (content.classList.contains('d-none')) {\n // We should always have content but some cells do not contain menus or other actions.\n element.classList.remove('collapsed');\n // If there are many nodes, apply the following.\n if (content.childNodes.length > 1) {\n content.classList.add('d-flex');\n }\n nodeSet.forEach(node => {\n node?.classList.remove('d-none');\n node?.setAttribute('aria-hidden', 'false');\n });\n expandButton?.classList.add('d-none');\n expandButton?.setAttribute('aria-hidden', 'true');\n } else {\n element.classList.add('collapsed');\n content.classList.remove('d-flex');\n nodeSet.forEach(node => {\n node?.classList.add('d-none');\n node?.setAttribute('aria-hidden', 'true');\n });\n expandButton?.classList.remove('d-none');\n expandButton?.setAttribute('aria-hidden', 'false');\n }\n }\n });\n }\n\n /**\n * Update the visual count of collapsed columns or hide the count all together.\n */\n countUpdate() {\n countIndicator.textContent = this.getDatasetSize();\n if (this.getDatasetSize() > 0) {\n this.component.parentElement.classList.add('d-flex');\n this.component.parentElement.classList.remove('d-none');\n } else {\n this.component.parentElement.classList.remove('d-flex');\n this.component.parentElement.classList.add('d-none');\n }\n }\n\n /**\n * Build the content then replace the node by default we want our form to exist.\n */\n async renderDefault() {\n this.setMatchedResults(await this.filterDataset(this.getDataset()));\n this.filterMatchDataset();\n\n // Update the collapsed button pill.\n this.countUpdate();\n const {html, js} = await renderForPromise('gradereport_grader/collapse/collapsebody', {\n 'results': this.getMatchedResults(),\n 'userid': this.userID,\n });\n replaceNode(selectors.placeholder, html, js);\n this.updateNodes();\n\n // Given we now have the body, we can set up more triggers.\n this.registerFormEvents();\n this.registerInputEvents();\n\n // Add a small BS listener so that we can set the focus correctly on open.\n this.$component.on('shown.bs.dropdown', () => {\n this.searchInput.focus({preventScroll: true});\n });\n }\n\n /**\n * Build the content then replace the node.\n */\n async renderDropdown() {\n const form = this.component.querySelector(selectors.formDropdown);\n const selectall = form.querySelector('[data-action=\"selectall\"]');\n const {html, js} = await renderForPromise('gradereport_grader/collapse/collapseresults', {\n 'results': this.getMatchedResults(),\n 'searchTerm': this.getSearchTerm(),\n });\n selectall.disabled = this.getMatchedResults().length === 0;\n replaceNodeContents(this.getHTMLElements().searchDropdown, html, js);\n }\n\n /**\n * If we have any custom user profile fields, grab their system & readable names to add to our string map.\n *\n * @returns {array} An array of associated string arrays ready for our map.\n */\n fetchCustomFieldValues() {\n const customFields = document.querySelectorAll('[data-collapse-name]');\n // Cast from NodeList to array to grab all the values.\n return [...customFields].map(field => [field.parentElement.dataset.col, field.dataset.collapseName]);\n }\n\n /**\n * Given the set of profile fields we can possibly search, fetch their strings,\n * so we can report to screen readers the field that matched.\n *\n * @returns {Promise}\n */\n fetchRequiredUserStrings() {\n if (!this.userStrings) {\n const requiredStrings = [\n 'username',\n 'firstname',\n 'lastname',\n 'email',\n 'city',\n 'country',\n 'department',\n 'institution',\n 'idnumber',\n 'phone1',\n 'phone2',\n ];\n this.userStrings = getStrings(requiredStrings.map((key) => ({key})))\n .then((stringArray) => new Map(\n requiredStrings.map((key, index) => ([key, stringArray[index]]))\n ));\n }\n return this.userStrings;\n }\n\n /**\n * Given the set of gradable items we can possibly search, fetch their strings,\n * so we can report to screen readers the field that matched.\n *\n * @returns {Promise}\n */\n fetchRequiredGradeStrings() {\n if (!this.gradeStrings) {\n this.gradeStrings = Repository.gradeItems(this.courseID)\n .then((result) => new Map(\n result.gradeItems.map(key => ([key.id, key]))\n ));\n }\n return this.gradeStrings;\n }\n}\n"],"names":["selectors","cancel","save","checked","currentlyUnchecked","countIndicator","document","querySelector","ColumnSearch","search_combobox","userID","courseID","defaultSort","constructor","component","pendingPromise","Pending","then","loader","setTimeout","getDataset","forEach","item","nodesUpdate","renderDefault","remove","classList","resolve","catch","Notification","exception","componentSelector","dropdownSelector","triggerSelector","this","dataset","cols","fetchDataset","JSON","parse","split","datasetSize","length","storage","get","setPreferences","set","stringify","join","registerClickHandlers","addEventListener","clickHandler","bind","docClickHandler","e","target","closest","stopPropagation","hider","preventDefault","desiredToHide","_e$target$closest","col","_e$target$closest2","itemid","indexOf","push","prefcountpipe","_e$target$closest4","_e$target$closest5","idx","splice","_e$target$closest6","_e$target$closest7","keyHandler","key","input","clearSearchButton","focus","preventScroll","registerInputEvents","searchInput","async","setSearchTerms","value","add","filterrenderpipe","registerFormEvents","form","events","CustomEvents","activate","keyboardActivate","define","selectall","event","submitBtn","checkedCount","Array","from","querySelectorAll","disabled","submitter","action","dropdown","elements","filter","collapse","colNodesToHide","itemIDNodesToHide","nodes","updateDisplay","countUpdate","filterableData","stringUserMap","fetchRequiredUserStrings","stringGradeMap","fetchRequiredGradeStrings","customFieldMap","fetchCustomFieldValues","stringMap","Map","searching","map","s","mapObj","undefined","string","itemname","category","getPreppedSearchTerm","toString","toLowerCase","includes","filterMatchDataset","setMatchedResults","getMatchedResults","column","name","displayName","element","content","sort","expandButton","rangeRowCell","avgRowCell","nodeSet","contains","window","location","rowCell","toggle","setAttribute","childNodes","node","textContent","getDatasetSize","parentElement","filterDataset","html","js","updateNodes","$component","on","getSearchTerm","getHTMLElements","searchDropdown","field","collapseName","userStrings","requiredStrings","stringArray","index","gradeStrings","Repository","gradeItems","result","id"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"collapse.min.js","sources":["../src/collapse.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 * Allow the user to show and hide columns of the report at will.\n *\n * @module gradereport_grader/collapse\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport * as Repository from 'gradereport_grader/collapse/repository';\nimport search_combobox from 'core/comboboxsearch/search_combobox';\nimport {renderForPromise, replaceNodeContents, replaceNode} from 'core/templates';\nimport {debounce} from 'core/utils';\nimport $ from 'jquery';\nimport {getStrings} from 'core/str';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport storage from 'core/localstorage';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\n\n// Contain our selectors within this file until they could be of use elsewhere.\nconst selectors = {\n component: '.collapse-columns',\n formDropdown: '.columnsdropdownform',\n formItems: {\n cancel: 'cancel',\n save: 'save',\n checked: 'input[type=\"checkbox\"]:checked',\n currentlyUnchecked: 'input[type=\"checkbox\"]:not([data-action=\"selectall\"])',\n },\n hider: 'hide',\n expand: 'expand',\n colVal: '[data-col]',\n itemVal: '[data-itemid]',\n content: '[data-collapse=\"content\"]',\n sort: '[data-collapse=\"sort\"]',\n expandbutton: '[data-collapse=\"expandbutton\"]',\n rangerowcell: '[data-collapse=\"rangerowcell\"]',\n avgrowcell: '[data-collapse=\"avgrowcell\"]',\n menu: '[data-collapse=\"menu\"]',\n icons: '.data-collapse_gradeicons',\n count: '[data-collapse=\"count\"]',\n placeholder: '.collapsecolumndropdown [data-region=\"placeholder\"]',\n fullDropdown: '.collapsecolumndropdown',\n};\n\nconst countIndicator = document.querySelector(selectors.count);\n\nexport default class ColumnSearch extends search_combobox {\n\n userID = -1;\n courseID = null;\n defaultSort = '';\n\n nodes = [];\n\n gradeStrings = null;\n userStrings = null;\n stringMap = [];\n\n static init(userID, courseID, defaultSort) {\n return new ColumnSearch(userID, courseID, defaultSort);\n }\n\n constructor(userID, courseID, defaultSort) {\n super();\n this.userID = userID;\n this.courseID = courseID;\n this.defaultSort = defaultSort;\n this.component = document.querySelector(selectors.component);\n\n const pendingPromise = new Pending();\n // Display a loader whilst collapsing appropriate columns (based on the locally stored state for the current user).\n addIconToContainer(document.querySelector('.gradeparent')).then((loader) => {\n setTimeout(() => {\n // Get the users' checked columns to change.\n this.getDataset().forEach((item) => {\n this.nodesUpdate(item);\n });\n this.renderDefault();\n\n // Once the grade categories have been re-collapsed, remove the loader and display the Gradebook setup content.\n loader.remove();\n document.querySelector('.gradereport-grader-table').classList.remove('d-none');\n }, 10);\n }).then(() => pendingPromise.resolve()).catch(Notification.exception);\n }\n\n /**\n * The overall div that contains the searching widget.\n *\n * @returns {string}\n */\n componentSelector() {\n return '.collapse-columns';\n }\n\n /**\n * The dropdown div that contains the searching widget result space.\n *\n * @returns {string}\n */\n dropdownSelector() {\n return '.searchresultitemscontainer';\n }\n\n /**\n * The triggering div that contains the searching widget.\n *\n * @returns {string}\n */\n triggerSelector() {\n return '.collapsecolumn';\n }\n\n /**\n * Return the dataset that we will be searching upon.\n *\n * @returns {Array}\n */\n getDataset() {\n if (!this.dataset) {\n const cols = this.fetchDataset();\n this.dataset = JSON.parse(cols) ? JSON.parse(cols).split(',') : [];\n }\n this.datasetSize = this.dataset.length;\n return this.dataset;\n }\n\n /**\n * Get the data we will be searching against in this component.\n *\n * @returns {string}\n */\n fetchDataset() {\n return storage.get(`gradereport_grader_collapseditems_${this.courseID}_${this.userID}`);\n }\n\n /**\n * Given a user performs an action, update the users' preferences.\n */\n setPreferences() {\n storage.set(`gradereport_grader_collapseditems_${this.courseID}_${this.userID}`,\n JSON.stringify(this.getDataset().join(','))\n );\n }\n\n /**\n * Register clickable event listeners.\n */\n registerClickHandlers() {\n // Register click events within the component.\n this.component.addEventListener('click', this.clickHandler.bind(this));\n\n document.addEventListener('click', this.docClickHandler.bind(this));\n }\n\n /**\n * The handler for when a user interacts with the component.\n *\n * @param {MouseEvent} e The triggering event that we are working with.\n */\n clickHandler(e) {\n super.clickHandler(e);\n // Prevent BS from closing the dropdown if they click elsewhere within the dropdown besides the form.\n if (e.target.closest(selectors.fullDropdown)) {\n e.stopPropagation();\n }\n }\n\n /**\n * Externally defined click function to improve memory handling.\n *\n * @param {MouseEvent} e\n * @returns {Promise}\n */\n async docClickHandler(e) {\n if (e.target.dataset.hider === selectors.hider) {\n e.preventDefault();\n const desiredToHide = e.target.closest(selectors.colVal) ?\n e.target.closest(selectors.colVal)?.dataset.col :\n e.target.closest(selectors.itemVal)?.dataset.itemid;\n const idx = this.getDataset().indexOf(desiredToHide);\n if (idx === -1) {\n this.getDataset().push(desiredToHide);\n }\n await this.prefcountpipe();\n\n this.nodesUpdate(desiredToHide);\n }\n\n if (e.target.closest('button')?.dataset.hider === selectors.expand) {\n e.preventDefault();\n const desiredToHide = e.target.closest(selectors.colVal) ?\n e.target.closest(selectors.colVal)?.dataset.col :\n e.target.closest(selectors.itemVal)?.dataset.itemid;\n const idx = this.getDataset().indexOf(desiredToHide);\n this.getDataset().splice(idx, 1);\n\n await this.prefcountpipe();\n\n this.nodesUpdate(e.target.closest(selectors.colVal)?.dataset.col);\n this.nodesUpdate(e.target.closest(selectors.colVal)?.dataset.itemid);\n }\n }\n\n /**\n * The handler for when a user presses a key within the component.\n *\n * @param {KeyboardEvent} e The triggering event that we are working with.\n */\n async keyHandler(e) {\n super.keyHandler(e);\n\n // Switch the key presses to handle keyboard nav.\n switch (e.key) {\n case 'Tab':\n if (e.target.closest(this.selectors.input)) {\n e.preventDefault();\n this.clearSearchButton.focus({preventScroll: true});\n }\n break;\n }\n }\n\n /**\n * Handle any keyboard inputs.\n */\n registerInputEvents() {\n // Register & handle the text input.\n this.searchInput.addEventListener('input', debounce(async() => {\n this.setSearchTerms(this.searchInput.value);\n // We can also require a set amount of input before search.\n if (this.searchInput.value === '') {\n // Hide the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.add('d-none');\n } else {\n // Display the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.remove('d-none');\n }\n // User has given something for us to filter against.\n await this.filterrenderpipe();\n }, 300));\n }\n\n /**\n * Handle the form submission within the dropdown.\n */\n registerFormEvents() {\n const form = this.component.querySelector(selectors.formDropdown);\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n const selectall = form.querySelector('[data-action=\"selectall\"]');\n\n // Register clicks & keyboard form handling.\n events.forEach((event) => {\n const submitBtn = form.querySelector(`[data-action=\"${selectors.formItems.save}\"`);\n form.addEventListener(event, (e) => {\n // Stop Bootstrap from being clever.\n e.stopPropagation();\n const input = e.target.closest('input');\n if (input) {\n // If the user is unchecking an item, we need to uncheck the select all if it's checked.\n if (selectall.checked && !input.checked) {\n selectall.checked = false;\n }\n const checkedCount = Array.from(form.querySelectorAll(selectors.formItems.checked)).length;\n // Check if any are clicked or not then change disabled.\n submitBtn.disabled = checkedCount <= 0;\n }\n }, false);\n\n // Stop Bootstrap from being clever.\n this.searchInput.addEventListener(event, e => e.stopPropagation());\n this.clearSearchButton.addEventListener(event, async(e) => {\n e.stopPropagation();\n this.searchInput.value = '';\n this.setSearchTerms(this.searchInput.value);\n await this.filterrenderpipe();\n });\n selectall.addEventListener(event, (e) => {\n // Stop Bootstrap from being clever.\n e.stopPropagation();\n if (!selectall.checked) {\n const touncheck = Array.from(form.querySelectorAll(selectors.formItems.checked));\n touncheck.forEach(item => {\n item.checked = false;\n });\n submitBtn.disabled = true;\n } else {\n const currentUnchecked = Array.from(form.querySelectorAll(selectors.formItems.currentlyUnchecked));\n currentUnchecked.forEach(item => {\n item.checked = true;\n });\n submitBtn.disabled = false;\n }\n });\n });\n\n form.addEventListener('submit', async(e) => {\n e.preventDefault();\n if (e.submitter.dataset.action === selectors.formItems.cancel) {\n $(this.component).dropdown('toggle');\n return;\n }\n // Get the users' checked columns to change.\n const checkedItems = [...form.elements].filter(item => item.checked);\n checkedItems.forEach((item) => {\n const idx = this.getDataset().indexOf(item.dataset.collapse);\n this.getDataset().splice(idx, 1);\n this.nodesUpdate(item.dataset.collapse);\n });\n // Reset the check all & submit to false just in case.\n selectall.checked = false;\n e.submitter.disabled = true;\n await this.prefcountpipe();\n });\n }\n\n nodesUpdate(item) {\n const colNodesToHide = [...document.querySelectorAll(`[data-col=\"${item}\"]`)];\n const itemIDNodesToHide = [...document.querySelectorAll(`[data-itemid=\"${item}\"]`)];\n this.nodes = [...colNodesToHide, ...itemIDNodesToHide];\n this.updateDisplay();\n }\n\n /**\n * Update the user preferences, count display then render the results.\n *\n * @returns {Promise}\n */\n async prefcountpipe() {\n this.setPreferences();\n this.countUpdate();\n await this.filterrenderpipe();\n }\n\n /**\n * Dictate to the search component how and what we want to match upon.\n *\n * @param {Array} filterableData\n * @returns {Array} An array of objects containing the system reference and the user readable value.\n */\n async filterDataset(filterableData) {\n const stringUserMap = await this.fetchRequiredUserStrings();\n const stringGradeMap = await this.fetchRequiredGradeStrings();\n // Custom user profile fields are not in our string map and need a bit of extra love.\n const customFieldMap = this.fetchCustomFieldValues();\n this.stringMap = new Map([...stringGradeMap, ...stringUserMap, ...customFieldMap]);\n\n const searching = filterableData.map(s => {\n const mapObj = this.stringMap.get(s);\n if (mapObj === undefined) {\n return {key: s, string: s};\n }\n return {\n key: s,\n string: mapObj.itemname ?? this.stringMap.get(s),\n category: mapObj.category ?? '',\n };\n });\n // Sometimes we just want to show everything.\n if (this.getPreppedSearchTerm() === '') {\n return searching;\n }\n // Other times we want to actually filter the content.\n return searching.filter((col) => {\n return col.string.toString().toLowerCase().includes(this.getPreppedSearchTerm());\n });\n }\n\n /**\n * Given we have a subset of the dataset, set the field that we matched upon to inform the end user.\n */\n filterMatchDataset() {\n this.setMatchedResults(\n this.getMatchedResults().map((column) => {\n return {\n name: column.key,\n displayName: column.string ?? column.key,\n category: column.category ?? '',\n };\n })\n );\n }\n\n /**\n * With an array of nodes, switch their classes and values.\n */\n updateDisplay() {\n this.nodes.forEach((element) => {\n const content = element.querySelector(selectors.content);\n const sort = element.querySelector(selectors.sort);\n const expandButton = element.querySelector(selectors.expandbutton);\n const rangeRowCell = element.querySelector(selectors.rangerowcell);\n const avgRowCell = element.querySelector(selectors.avgrowcell);\n const nodeSet = [\n element.querySelector(selectors.menu),\n element.querySelector(selectors.icons),\n content\n ];\n\n // This can be further improved to reduce redundant similar calls.\n if (element.classList.contains('cell')) {\n // The column is actively being sorted, lets reset that and reload the page.\n if (sort !== null) {\n window.location = this.defaultSort;\n }\n if (content === null) {\n // If it's not a content cell, it must be an overall average or a range cell.\n const rowCell = avgRowCell ?? rangeRowCell;\n\n rowCell?.classList.toggle('d-none');\n rowCell?.setAttribute('aria-hidden',\n rowCell?.classList.contains('d-none') ? 'true' : 'false');\n } else if (content.classList.contains('d-none')) {\n // We should always have content but some cells do not contain menus or other actions.\n element.classList.remove('collapsed');\n // If there are many nodes, apply the following.\n if (content.childNodes.length > 1) {\n content.classList.add('d-flex');\n }\n nodeSet.forEach(node => {\n node?.classList.remove('d-none');\n node?.setAttribute('aria-hidden', 'false');\n });\n expandButton?.classList.add('d-none');\n expandButton?.setAttribute('aria-hidden', 'true');\n } else {\n element.classList.add('collapsed');\n content.classList.remove('d-flex');\n nodeSet.forEach(node => {\n node?.classList.add('d-none');\n node?.setAttribute('aria-hidden', 'true');\n });\n expandButton?.classList.remove('d-none');\n expandButton?.setAttribute('aria-hidden', 'false');\n }\n }\n });\n }\n\n /**\n * Update the visual count of collapsed columns or hide the count all together.\n */\n countUpdate() {\n countIndicator.textContent = this.getDatasetSize();\n if (this.getDatasetSize() > 0) {\n this.component.parentElement.classList.add('d-flex');\n this.component.parentElement.classList.remove('d-none');\n } else {\n this.component.parentElement.classList.remove('d-flex');\n this.component.parentElement.classList.add('d-none');\n }\n }\n\n /**\n * Build the content then replace the node by default we want our form to exist.\n */\n async renderDefault() {\n this.setMatchedResults(await this.filterDataset(this.getDataset()));\n this.filterMatchDataset();\n\n // Update the collapsed button pill.\n this.countUpdate();\n const {html, js} = await renderForPromise('gradereport_grader/collapse/collapsebody', {\n 'results': this.getMatchedResults(),\n 'userid': this.userID,\n });\n replaceNode(selectors.placeholder, html, js);\n this.updateNodes();\n\n // Given we now have the body, we can set up more triggers.\n this.registerFormEvents();\n this.registerInputEvents();\n\n // Add a small BS listener so that we can set the focus correctly on open.\n this.$component.on('shown.bs.dropdown', () => {\n this.searchInput.focus({preventScroll: true});\n });\n }\n\n /**\n * Build the content then replace the node.\n */\n async renderDropdown() {\n const form = this.component.querySelector(selectors.formDropdown);\n const selectall = form.querySelector('[data-action=\"selectall\"]');\n const {html, js} = await renderForPromise('gradereport_grader/collapse/collapseresults', {\n 'results': this.getMatchedResults(),\n 'searchTerm': this.getSearchTerm(),\n });\n selectall.disabled = this.getMatchedResults().length === 0;\n replaceNodeContents(this.getHTMLElements().searchDropdown, html, js);\n }\n\n /**\n * If we have any custom user profile fields, grab their system & readable names to add to our string map.\n *\n * @returns {array} An array of associated string arrays ready for our map.\n */\n fetchCustomFieldValues() {\n const customFields = document.querySelectorAll('[data-collapse-name]');\n // Cast from NodeList to array to grab all the values.\n return [...customFields].map(field => [field.parentElement.dataset.col, field.dataset.collapseName]);\n }\n\n /**\n * Given the set of profile fields we can possibly search, fetch their strings,\n * so we can report to screen readers the field that matched.\n *\n * @returns {Promise}\n */\n fetchRequiredUserStrings() {\n if (!this.userStrings) {\n const requiredStrings = [\n 'username',\n 'firstname',\n 'lastname',\n 'email',\n 'city',\n 'country',\n 'department',\n 'institution',\n 'idnumber',\n 'phone1',\n 'phone2',\n ];\n this.userStrings = getStrings(requiredStrings.map((key) => ({key})))\n .then((stringArray) => new Map(\n requiredStrings.map((key, index) => ([key, stringArray[index]]))\n ));\n }\n return this.userStrings;\n }\n\n /**\n * Given the set of gradable items we can possibly search, fetch their strings,\n * so we can report to screen readers the field that matched.\n *\n * @returns {Promise}\n */\n fetchRequiredGradeStrings() {\n if (!this.gradeStrings) {\n this.gradeStrings = Repository.gradeItems(this.courseID)\n .then((result) => new Map(\n result.gradeItems.map(key => ([key.id, key]))\n ));\n }\n return this.gradeStrings;\n }\n}\n"],"names":["selectors","cancel","save","checked","currentlyUnchecked","countIndicator","document","querySelector","ColumnSearch","search_combobox","userID","courseID","defaultSort","constructor","component","pendingPromise","Pending","then","loader","setTimeout","getDataset","forEach","item","nodesUpdate","renderDefault","remove","classList","resolve","catch","Notification","exception","componentSelector","dropdownSelector","triggerSelector","this","dataset","cols","fetchDataset","JSON","parse","split","datasetSize","length","storage","get","setPreferences","set","stringify","join","registerClickHandlers","addEventListener","clickHandler","bind","docClickHandler","e","target","closest","stopPropagation","hider","preventDefault","desiredToHide","_e$target$closest","col","_e$target$closest2","itemid","indexOf","push","prefcountpipe","_e$target$closest4","_e$target$closest5","idx","splice","_e$target$closest6","_e$target$closest7","keyHandler","key","input","clearSearchButton","focus","preventScroll","registerInputEvents","searchInput","async","setSearchTerms","value","add","filterrenderpipe","registerFormEvents","form","events","CustomEvents","activate","keyboardActivate","define","selectall","event","submitBtn","checkedCount","Array","from","querySelectorAll","disabled","submitter","action","dropdown","elements","filter","collapse","colNodesToHide","itemIDNodesToHide","nodes","updateDisplay","countUpdate","filterableData","stringUserMap","fetchRequiredUserStrings","stringGradeMap","fetchRequiredGradeStrings","customFieldMap","fetchCustomFieldValues","stringMap","Map","searching","map","s","mapObj","undefined","string","itemname","category","getPreppedSearchTerm","toString","toLowerCase","includes","filterMatchDataset","setMatchedResults","getMatchedResults","column","name","displayName","element","content","sort","expandButton","rangeRowCell","avgRowCell","nodeSet","contains","window","location","rowCell","toggle","setAttribute","childNodes","node","textContent","getDatasetSize","parentElement","filterDataset","html","js","updateNodes","$component","on","getSearchTerm","getHTMLElements","searchDropdown","field","collapseName","userStrings","requiredStrings","stringArray","index","gradeStrings","Repository","gradeItems","result","id"],"mappings":"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"} \ No newline at end of file diff --git a/grade/report/grader/amd/src/collapse.js b/grade/report/grader/amd/src/collapse.js index 0a018db391e..453734b22cc 100644 --- a/grade/report/grader/amd/src/collapse.js +++ b/grade/report/grader/amd/src/collapse.js @@ -25,7 +25,7 @@ import search_combobox from 'core/comboboxsearch/search_combobox'; import {renderForPromise, replaceNodeContents, replaceNode} from 'core/templates'; import {debounce} from 'core/utils'; import $ from 'jquery'; -import {get_strings as getStrings} from 'core/str'; +import {getStrings} from 'core/str'; import CustomEvents from "core/custom_interaction_events"; import storage from 'core/localstorage'; import {addIconToContainer} from 'core/loadingicon'; diff --git a/lib/amd/build/addblockmodal.min.js b/lib/amd/build/addblockmodal.min.js index af43594d223..954f67ec3d1 100644 --- a/lib/amd/build/addblockmodal.min.js +++ b/lib/amd/build/addblockmodal.min.js @@ -6,6 +6,6 @@ define("core/addblockmodal",["exports","core/modal_factory","core/templates","co * @deprecated since Moodle 4.2 - please use core_block/add_modal instead. * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modal_factory=_interopRequireDefault(_modal_factory),_templates=_interopRequireDefault(_templates),_ajax=_interopRequireDefault(_ajax);const SELECTORS_ADD_BLOCK='[data-key="addblock"]';let listenerEventsRegistered=!1;const registerListenerEvents=(pageType,pageLayout,addBlockUrl,subPage)=>{document.addEventListener("click",(e=>{const addBlock=e.target.closest(SELECTORS_ADD_BLOCK);if(addBlock){e.preventDefault();let addBlockModal=null,addBlockModalUrl=null!=addBlockUrl?addBlockUrl:addBlock.dataset.url;buildAddBlockModal().then((modal=>{addBlockModal=modal;const modalBody=renderBlocks(addBlockModalUrl,pageType,pageLayout,subPage);return modal.setBody(modalBody),modal.show(),modalBody})).catch((()=>{addBlockModal.destroy()}))}}))},buildAddBlockModal=()=>_modal_factory.default.create({type:_modal_factory.default.types.CANCEL,title:(0,_str.get_string)("addblock")}),renderBlocks=async(addBlockUrl,pageType,pageLayout,subPage)=>{const blocks=await getAddableBlocks(pageType,pageLayout,subPage);return _templates.default.render("core/add_block_body",{blocks:blocks,url:addBlockUrl})},getAddableBlocks=async(pageType,pageLayout,subPage)=>{const request={methodname:"core_block_fetch_addable_blocks",args:{pagecontextid:M.cfg.contextid,pagetype:pageType,pagelayout:pageLayout,subpage:subPage}};return _ajax.default.call([request])[0]};_exports.init=function(pageType,pageLayout){let addBlockUrl=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,subPage=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"";listenerEventsRegistered||(registerListenerEvents(pageType,pageLayout,addBlockUrl,subPage),listenerEventsRegistered=!0)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modal_factory=_interopRequireDefault(_modal_factory),_templates=_interopRequireDefault(_templates),_ajax=_interopRequireDefault(_ajax);const SELECTORS_ADD_BLOCK='[data-key="addblock"]';let listenerEventsRegistered=!1;const registerListenerEvents=(pageType,pageLayout,addBlockUrl,subPage)=>{document.addEventListener("click",(e=>{const addBlock=e.target.closest(SELECTORS_ADD_BLOCK);if(addBlock){e.preventDefault();let addBlockModal=null,addBlockModalUrl=null!=addBlockUrl?addBlockUrl:addBlock.dataset.url;buildAddBlockModal().then((modal=>{addBlockModal=modal;const modalBody=renderBlocks(addBlockModalUrl,pageType,pageLayout,subPage);return modal.setBody(modalBody),modal.show(),modalBody})).catch((()=>{addBlockModal.destroy()}))}}))},buildAddBlockModal=()=>_modal_factory.default.create({type:_modal_factory.default.types.CANCEL,title:(0,_str.getString)("addblock")}),renderBlocks=async(addBlockUrl,pageType,pageLayout,subPage)=>{const blocks=await getAddableBlocks(pageType,pageLayout,subPage);return _templates.default.render("core/add_block_body",{blocks:blocks,url:addBlockUrl})},getAddableBlocks=async(pageType,pageLayout,subPage)=>{const request={methodname:"core_block_fetch_addable_blocks",args:{pagecontextid:M.cfg.contextid,pagetype:pageType,pagelayout:pageLayout,subpage:subPage}};return _ajax.default.call([request])[0]};_exports.init=function(pageType,pageLayout){let addBlockUrl=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,subPage=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"";listenerEventsRegistered||(registerListenerEvents(pageType,pageLayout,addBlockUrl,subPage),listenerEventsRegistered=!0)}})); //# sourceMappingURL=addblockmodal.min.js.map \ No newline at end of file diff --git a/lib/amd/build/addblockmodal.min.js.map b/lib/amd/build/addblockmodal.min.js.map index b3316c79e77..3516e7f28af 100644 --- a/lib/amd/build/addblockmodal.min.js.map +++ b/lib/amd/build/addblockmodal.min.js.map @@ -1 +1 @@ -{"version":3,"file":"addblockmodal.min.js","sources":["../src/addblockmodal.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 * Show an add block modal instead of doing it on a separate page.\n *\n * @module core/addblockmodal\n * @deprecated since Moodle 4.2 - please use core_block/add_modal instead.\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalFactory from 'core/modal_factory';\nimport Templates from 'core/templates';\nimport {get_string as getString} from 'core/str';\nimport Ajax from 'core/ajax';\n\nconst SELECTORS = {\n ADD_BLOCK: '[data-key=\"addblock\"]'\n};\n\n// Ensure we only add our listeners once.\nlet listenerEventsRegistered = false;\n\n/**\n * Register related event listeners.\n *\n * @method registerListenerEvents\n * @param {String} pageType The type of the page\n * @param {String} pageLayout The layout of the page\n * @param {String|null} addBlockUrl The add block URL\n * @param {String} subPage The subpage identifier\n */\nconst registerListenerEvents = (pageType, pageLayout, addBlockUrl, subPage) => {\n document.addEventListener('click', e => {\n\n const addBlock = e.target.closest(SELECTORS.ADD_BLOCK);\n if (addBlock) {\n e.preventDefault();\n\n let addBlockModal = null;\n let addBlockModalUrl = addBlockUrl ?? addBlock.dataset.url;\n\n buildAddBlockModal()\n .then(modal => {\n addBlockModal = modal;\n const modalBody = renderBlocks(addBlockModalUrl, pageType, pageLayout, subPage);\n modal.setBody(modalBody);\n modal.show();\n\n return modalBody;\n })\n .catch(() => {\n addBlockModal.destroy();\n });\n }\n });\n};\n\n/**\n * Method that creates the 'add block' modal.\n *\n * @method buildAddBlockModal\n * @returns {Promise} The modal promise (modal's body will be rendered later).\n */\nconst buildAddBlockModal = () => {\n return ModalFactory.create({\n type: ModalFactory.types.CANCEL,\n title: getString('addblock')\n });\n};\n\n/**\n * Method that renders the list of available blocks.\n *\n * @method renderBlocks\n * @param {String} addBlockUrl The add block URL\n * @param {String} pageType The type of the page\n * @param {String} pageLayout The layout of the page\n * @param {String} subPage The subpage identifier\n * @return {Promise}\n */\nconst renderBlocks = async(addBlockUrl, pageType, pageLayout, subPage) => {\n // Fetch all addable blocks in the given page.\n const blocks = await getAddableBlocks(pageType, pageLayout, subPage);\n\n return Templates.render('core/add_block_body', {\n blocks: blocks,\n url: addBlockUrl\n });\n};\n\n/**\n * Method that fetches all addable blocks in a given page.\n *\n * @method getAddableBlocks\n * @param {String} pageType The type of the page\n * @param {String} pageLayout The layout of the page\n * @param {String} subPage The subpage identifier\n * @return {Promise}\n */\nconst getAddableBlocks = async(pageType, pageLayout, subPage) => {\n const request = {\n methodname: 'core_block_fetch_addable_blocks',\n args: {\n pagecontextid: M.cfg.contextid,\n pagetype: pageType,\n pagelayout: pageLayout,\n subpage: subPage,\n },\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Set up the actions.\n *\n * @method init\n * @param {String} pageType The type of the page\n * @param {String} pageLayout The layout of the page\n * @param {String|null} addBlockUrl The add block URL\n * @param {String} subPage The subpage identifier\n */\nexport const init = (pageType, pageLayout, addBlockUrl = null, subPage = '') => {\n if (!listenerEventsRegistered) {\n registerListenerEvents(pageType, pageLayout, addBlockUrl, subPage);\n listenerEventsRegistered = true;\n }\n};\n"],"names":["SELECTORS","listenerEventsRegistered","registerListenerEvents","pageType","pageLayout","addBlockUrl","subPage","document","addEventListener","e","addBlock","target","closest","preventDefault","addBlockModal","addBlockModalUrl","dataset","url","buildAddBlockModal","then","modal","modalBody","renderBlocks","setBody","show","catch","destroy","ModalFactory","create","type","types","CANCEL","title","async","blocks","getAddableBlocks","Templates","render","request","methodname","args","pagecontextid","M","cfg","contextid","pagetype","pagelayout","subpage","Ajax","call"],"mappings":";;;;;;;;gOA6BMA,oBACS,4BAIXC,0BAA2B,QAWzBC,uBAAyB,CAACC,SAAUC,WAAYC,YAAaC,WAC/DC,SAASC,iBAAiB,SAASC,UAEzBC,SAAWD,EAAEE,OAAOC,QAAQZ,wBAC9BU,SAAU,CACVD,EAAEI,qBAEEC,cAAgB,KAChBC,iBAAmBV,MAAAA,YAAAA,YAAeK,SAASM,QAAQC,IAEvDC,qBACKC,MAAKC,QACFN,cAAgBM,YACVC,UAAYC,aAAaP,iBAAkBZ,SAAUC,WAAYE,gBACvEc,MAAMG,QAAQF,WACdD,MAAMI,OAECH,aAEVI,OAAM,KACHX,cAAcY,kBAY5BR,mBAAqB,IAChBS,uBAAaC,OAAO,CACvBC,KAAMF,uBAAaG,MAAMC,OACzBC,OAAO,mBAAU,cAcnBV,aAAeW,MAAM5B,YAAaF,SAAUC,WAAYE,iBAEpD4B,aAAeC,iBAAiBhC,SAAUC,WAAYE,gBAErD8B,mBAAUC,OAAO,sBAAuB,CAC3CH,OAAQA,OACRjB,IAAKZ,eAaP8B,iBAAmBF,MAAM9B,SAAUC,WAAYE,iBAC3CgC,QAAU,CACZC,WAAY,kCACZC,KAAM,CACFC,cAAeC,EAAEC,IAAIC,UACrBC,SAAU1C,SACV2C,WAAY1C,WACZ2C,QAASzC,iBAIV0C,cAAKC,KAAK,CAACX,UAAU,kBAYZ,SAACnC,SAAUC,gBAAYC,mEAAc,KAAMC,+DAAU,GAChEL,2BACDC,uBAAuBC,SAAUC,WAAYC,YAAaC,SAC1DL,0BAA2B"} \ No newline at end of file +{"version":3,"file":"addblockmodal.min.js","sources":["../src/addblockmodal.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 * Show an add block modal instead of doing it on a separate page.\n *\n * @module core/addblockmodal\n * @deprecated since Moodle 4.2 - please use core_block/add_modal instead.\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalFactory from 'core/modal_factory';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport Ajax from 'core/ajax';\n\nconst SELECTORS = {\n ADD_BLOCK: '[data-key=\"addblock\"]'\n};\n\n// Ensure we only add our listeners once.\nlet listenerEventsRegistered = false;\n\n/**\n * Register related event listeners.\n *\n * @method registerListenerEvents\n * @param {String} pageType The type of the page\n * @param {String} pageLayout The layout of the page\n * @param {String|null} addBlockUrl The add block URL\n * @param {String} subPage The subpage identifier\n */\nconst registerListenerEvents = (pageType, pageLayout, addBlockUrl, subPage) => {\n document.addEventListener('click', e => {\n\n const addBlock = e.target.closest(SELECTORS.ADD_BLOCK);\n if (addBlock) {\n e.preventDefault();\n\n let addBlockModal = null;\n let addBlockModalUrl = addBlockUrl ?? addBlock.dataset.url;\n\n buildAddBlockModal()\n .then(modal => {\n addBlockModal = modal;\n const modalBody = renderBlocks(addBlockModalUrl, pageType, pageLayout, subPage);\n modal.setBody(modalBody);\n modal.show();\n\n return modalBody;\n })\n .catch(() => {\n addBlockModal.destroy();\n });\n }\n });\n};\n\n/**\n * Method that creates the 'add block' modal.\n *\n * @method buildAddBlockModal\n * @returns {Promise} The modal promise (modal's body will be rendered later).\n */\nconst buildAddBlockModal = () => {\n return ModalFactory.create({\n type: ModalFactory.types.CANCEL,\n title: getString('addblock')\n });\n};\n\n/**\n * Method that renders the list of available blocks.\n *\n * @method renderBlocks\n * @param {String} addBlockUrl The add block URL\n * @param {String} pageType The type of the page\n * @param {String} pageLayout The layout of the page\n * @param {String} subPage The subpage identifier\n * @return {Promise}\n */\nconst renderBlocks = async(addBlockUrl, pageType, pageLayout, subPage) => {\n // Fetch all addable blocks in the given page.\n const blocks = await getAddableBlocks(pageType, pageLayout, subPage);\n\n return Templates.render('core/add_block_body', {\n blocks: blocks,\n url: addBlockUrl\n });\n};\n\n/**\n * Method that fetches all addable blocks in a given page.\n *\n * @method getAddableBlocks\n * @param {String} pageType The type of the page\n * @param {String} pageLayout The layout of the page\n * @param {String} subPage The subpage identifier\n * @return {Promise}\n */\nconst getAddableBlocks = async(pageType, pageLayout, subPage) => {\n const request = {\n methodname: 'core_block_fetch_addable_blocks',\n args: {\n pagecontextid: M.cfg.contextid,\n pagetype: pageType,\n pagelayout: pageLayout,\n subpage: subPage,\n },\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Set up the actions.\n *\n * @method init\n * @param {String} pageType The type of the page\n * @param {String} pageLayout The layout of the page\n * @param {String|null} addBlockUrl The add block URL\n * @param {String} subPage The subpage identifier\n */\nexport const init = (pageType, pageLayout, addBlockUrl = null, subPage = '') => {\n if (!listenerEventsRegistered) {\n registerListenerEvents(pageType, pageLayout, addBlockUrl, subPage);\n listenerEventsRegistered = true;\n }\n};\n"],"names":["SELECTORS","listenerEventsRegistered","registerListenerEvents","pageType","pageLayout","addBlockUrl","subPage","document","addEventListener","e","addBlock","target","closest","preventDefault","addBlockModal","addBlockModalUrl","dataset","url","buildAddBlockModal","then","modal","modalBody","renderBlocks","setBody","show","catch","destroy","ModalFactory","create","type","types","CANCEL","title","async","blocks","getAddableBlocks","Templates","render","request","methodname","args","pagecontextid","M","cfg","contextid","pagetype","pagelayout","subpage","Ajax","call"],"mappings":";;;;;;;;gOA6BMA,oBACS,4BAIXC,0BAA2B,QAWzBC,uBAAyB,CAACC,SAAUC,WAAYC,YAAaC,WAC/DC,SAASC,iBAAiB,SAASC,UAEzBC,SAAWD,EAAEE,OAAOC,QAAQZ,wBAC9BU,SAAU,CACVD,EAAEI,qBAEEC,cAAgB,KAChBC,iBAAmBV,MAAAA,YAAAA,YAAeK,SAASM,QAAQC,IAEvDC,qBACKC,MAAKC,QACFN,cAAgBM,YACVC,UAAYC,aAAaP,iBAAkBZ,SAAUC,WAAYE,gBACvEc,MAAMG,QAAQF,WACdD,MAAMI,OAECH,aAEVI,OAAM,KACHX,cAAcY,kBAY5BR,mBAAqB,IAChBS,uBAAaC,OAAO,CACvBC,KAAMF,uBAAaG,MAAMC,OACzBC,OAAO,kBAAU,cAcnBV,aAAeW,MAAM5B,YAAaF,SAAUC,WAAYE,iBAEpD4B,aAAeC,iBAAiBhC,SAAUC,WAAYE,gBAErD8B,mBAAUC,OAAO,sBAAuB,CAC3CH,OAAQA,OACRjB,IAAKZ,eAaP8B,iBAAmBF,MAAM9B,SAAUC,WAAYE,iBAC3CgC,QAAU,CACZC,WAAY,kCACZC,KAAM,CACFC,cAAeC,EAAEC,IAAIC,UACrBC,SAAU1C,SACV2C,WAAY1C,WACZ2C,QAASzC,iBAIV0C,cAAKC,KAAK,CAACX,UAAU,kBAYZ,SAACnC,SAAUC,gBAAYC,mEAAc,KAAMC,+DAAU,GAChEL,2BACDC,uBAAuBC,SAAUC,WAAYC,YAAaC,SAC1DL,0BAA2B"} \ No newline at end of file diff --git a/lib/amd/build/copy_to_clipboard.min.js b/lib/amd/build/copy_to_clipboard.min.js index af599f064bd..551ed7a676b 100644 --- a/lib/amd/build/copy_to_clipboard.min.js +++ b/lib/amd/build/copy_to_clipboard.min.js @@ -26,6 +26,6 @@ define("core/copy_to_clipboard",["core/str","core/toast","core/prefetch"],(funct * Copy to clipboard * */ -const copyNodeContentToClipboard=(copyButton,copyTarget)=>(copyTarget.select(),document.execCommand("copy")?(displaySuccessToast(copyButton),!0):(displayFailureToast(),!1)),displaySuccessToast=copyButton=>getSuccessText(copyButton).then((successMessage=>(0,_toast.add)(successMessage,{}))),displayFailureToast=()=>getFailureText().then((message=>(0,_toast.add)(message,{type:"warning"}))),getFailureText=()=>(0,_str.get_string)("unabletocopytoclipboard","core"),getSuccessText=copyButton=>copyButton.dataset.clipboardSuccessMessage?Promise.resolve(copyButton.dataset.clipboardSuccessMessage):(0,_str.get_string)("textcopiedtoclipboard","core"),getTextFromContainer=container=>container.value?container.value:container.innerText?container.innerText:null;let loaded=!1;loaded||((0,_prefetch.prefetchStrings)("core",["textcopiedtoclipboard","unabletocopytoclipboard"]),document.addEventListener("click",(e=>{const copyButton=e.target.closest('[data-action="copytoclipboard"]');if(!copyButton)return;if(!copyButton.dataset.clipboardTarget)return;const copyTarget=document.querySelector(copyButton.dataset.clipboardTarget);if(!copyTarget)return;e.preventDefault();const textToCopy=getTextFromContainer(copyTarget);if(textToCopy)if(navigator.clipboard)navigator.clipboard.writeText(textToCopy).then((()=>displaySuccessToast(copyButton))).catch();else if(copyTarget instanceof HTMLInputElement||copyTarget instanceof HTMLTextAreaElement)copyTarget.focus(),copyNodeContentToClipboard(copyButton,copyTarget)&©Button.focus();else{const copyRegion=document.createElement("textarea");copyRegion.value=textToCopy,copyRegion.classList.add("sr-only"),document.body.appendChild(copyRegion),copyNodeContentToClipboard(copyButton,copyRegion),copyRegion.remove(),copyButton.focus()}else displayFailureToast()})),loaded=!0)})); +const copyNodeContentToClipboard=(copyButton,copyTarget)=>(copyTarget.select(),document.execCommand("copy")?(displaySuccessToast(copyButton),!0):(displayFailureToast(),!1)),displaySuccessToast=copyButton=>getSuccessText(copyButton).then((successMessage=>(0,_toast.add)(successMessage,{}))),displayFailureToast=()=>getFailureText().then((message=>(0,_toast.add)(message,{type:"warning"}))),getFailureText=()=>(0,_str.getString)("unabletocopytoclipboard","core"),getSuccessText=copyButton=>copyButton.dataset.clipboardSuccessMessage?Promise.resolve(copyButton.dataset.clipboardSuccessMessage):(0,_str.getString)("textcopiedtoclipboard","core"),getTextFromContainer=container=>container.value?container.value:container.innerText?container.innerText:null;let loaded=!1;loaded||((0,_prefetch.prefetchStrings)("core",["textcopiedtoclipboard","unabletocopytoclipboard"]),document.addEventListener("click",(e=>{const copyButton=e.target.closest('[data-action="copytoclipboard"]');if(!copyButton)return;if(!copyButton.dataset.clipboardTarget)return;const copyTarget=document.querySelector(copyButton.dataset.clipboardTarget);if(!copyTarget)return;e.preventDefault();const textToCopy=getTextFromContainer(copyTarget);if(textToCopy)if(navigator.clipboard)navigator.clipboard.writeText(textToCopy).then((()=>displaySuccessToast(copyButton))).catch();else if(copyTarget instanceof HTMLInputElement||copyTarget instanceof HTMLTextAreaElement)copyTarget.focus(),copyNodeContentToClipboard(copyButton,copyTarget)&©Button.focus();else{const copyRegion=document.createElement("textarea");copyRegion.value=textToCopy,copyRegion.classList.add("sr-only"),document.body.appendChild(copyRegion),copyNodeContentToClipboard(copyButton,copyRegion),copyRegion.remove(),copyButton.focus()}else displayFailureToast()})),loaded=!0)})); //# sourceMappingURL=copy_to_clipboard.min.js.map \ No newline at end of file diff --git a/lib/amd/build/copy_to_clipboard.min.js.map b/lib/amd/build/copy_to_clipboard.min.js.map index 3472dc4c5e2..cec8741ef29 100644 --- a/lib/amd/build/copy_to_clipboard.min.js.map +++ b/lib/amd/build/copy_to_clipboard.min.js.map @@ -1 +1 @@ -{"version":3,"file":"copy_to_clipboard.min.js","sources":["../src/copy_to_clipboard.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 * A JavaScript module that enhances a button and text container to support copy-to-clipboard functionality.\n *\n * This module needs to be loaded by pages/templates/modules that require this functionality.\n *\n * To enable copy-to-clipboard functionality, we need a trigger element (usually a button) and a copy target element\n * (e.g. a div, span, text input, or text area).\n *\n * In the trigger element, we need to declare the data-action=\"copytoclipboard\" attribute and set the\n * data-clipboard-target attribute which is the CSS selector that points to the target element that contains the text\n * to be copied.\n *\n * When the text is successfully copied to the clipboard, a toast message that indicates that the copy operation was a success\n * will be shown. This success message can be customised by setting the data-clipboard-success-message attribute in the\n * trigger element.\n *\n * @module core/copy_to_clipboard\n * @copyright 2021 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n *\n * @example Markup for the trigger and target elements\n * \n * \n */\nimport {get_string as getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\nimport {prefetchStrings} from 'core/prefetch';\n\n/**\n * Add event listeners to trigger elements through event delegation.\n *\n * @private\n */\nconst addEventListeners = () => {\n document.addEventListener('click', e => {\n const copyButton = e.target.closest('[data-action=\"copytoclipboard\"]');\n if (!copyButton) {\n return;\n }\n\n if (!copyButton.dataset.clipboardTarget) {\n return;\n }\n\n const copyTarget = document.querySelector(copyButton.dataset.clipboardTarget);\n if (!copyTarget) {\n return;\n }\n\n // This is a copy target and there is content.\n // Prevent the default action.\n e.preventDefault();\n\n // We have a copy target - great. Let's copy its content.\n const textToCopy = getTextFromContainer(copyTarget);\n if (!textToCopy) {\n displayFailureToast();\n return;\n }\n\n if (navigator.clipboard) {\n navigator.clipboard.writeText(textToCopy)\n .then(() => displaySuccessToast(copyButton)).catch();\n\n return;\n }\n\n // The clipboard API is not available.\n // This may happen when the page is not served over SSL.\n // Try to fall back to document.execCommand() approach of copying the text.\n // WARNING: This is deprecated functionality that may get dropped at anytime by browsers.\n\n if (copyTarget instanceof HTMLInputElement || copyTarget instanceof HTMLTextAreaElement) {\n // Focus and select the text in the target element.\n // If the execCommand fails, at least the user can readily copy the text.\n copyTarget.focus();\n\n if (copyNodeContentToClipboard(copyButton, copyTarget)) {\n // If the copy was successful then focus back on the copy button.\n copyButton.focus();\n }\n } else {\n // This copyTarget is not an input, or text area so cannot be used with the execCommand('copy') command.\n // To work around this we create a new textarea and copy that.\n // This textarea must be part of the DOM and must be visible.\n // We (ab)use the sr-only tag to ensure that it is considered visible to the browser, whilst being\n // hidden from view by the user.\n const copyRegion = document.createElement('textarea');\n copyRegion.value = textToCopy;\n copyRegion.classList.add('sr-only');\n document.body.appendChild(copyRegion);\n\n copyNodeContentToClipboard(copyButton, copyRegion);\n\n // After copying, remove the temporary element and move focus back to the triggering button.\n copyRegion.remove();\n copyButton.focus();\n }\n });\n};\n\n/**\n * Copy the content of the selected element to the clipboard, and display a notifiction if successful.\n *\n * @param {HTMLElement} copyButton\n * @param {HTMLElement} copyTarget\n * @returns {boolean}\n * @private\n */\nconst copyNodeContentToClipboard = (copyButton, copyTarget) => {\n copyTarget.select();\n\n // Try to copy the text from the target element.\n if (document.execCommand('copy')) {\n displaySuccessToast(copyButton);\n return true;\n }\n\n displayFailureToast();\n return false;\n};\n\n/**\n * Displays a toast containing the success message.\n *\n * @param {HTMLElement} copyButton The element that copies the text from the container.\n * @returns {Promise}\n * @private\n */\nconst displaySuccessToast = copyButton => getSuccessText(copyButton)\n .then(successMessage => addToast(successMessage, {}));\n\n/**\n * Displays a toast containing the failure message.\n *\n * @returns {Promise}\n * @private\n */\nconst displayFailureToast = () => getFailureText()\n .then(message => addToast(message, {type: 'warning'}));\n\n/**\n * Fetches the failure message to show to the user.\n *\n * @returns {Promise}\n * @private\n */\nconst getFailureText = () => getString('unabletocopytoclipboard', 'core');\n\n/**\n * Fetches the success message to show to the user.\n *\n * @param {HTMLElement} copyButton The element that copies the text from the container. This may contain the custom success message\n * via its data-clipboard-success-message attribute.\n * @returns {Promise|*}\n * @private\n */\nconst getSuccessText = copyButton => {\n if (copyButton.dataset.clipboardSuccessMessage) {\n return Promise.resolve(copyButton.dataset.clipboardSuccessMessage);\n }\n\n return getString('textcopiedtoclipboard', 'core');\n};\n\n/**\n * Fetches the text to be copied from the container.\n *\n * @param {HTMLElement} container The element containing the text to be copied.\n * @returns {null|string}\n * @private\n */\nconst getTextFromContainer = container => {\n if (container.value) {\n // For containers which are form elements (e.g. text area, text input), get the element's value.\n return container.value;\n } else if (container.innerText) {\n // For other elements, try to use the innerText attribute.\n return container.innerText;\n }\n\n return null;\n};\n\nlet loaded = false;\nif (!loaded) {\n prefetchStrings('core', [\n 'textcopiedtoclipboard',\n 'unabletocopytoclipboard',\n ]);\n\n // Add event listeners.\n addEventListeners();\n loaded = true;\n}\n"],"names":["copyNodeContentToClipboard","copyButton","copyTarget","select","document","execCommand","displaySuccessToast","displayFailureToast","getSuccessText","then","successMessage","getFailureText","message","type","dataset","clipboardSuccessMessage","Promise","resolve","getTextFromContainer","container","value","innerText","loaded","addEventListener","e","target","closest","clipboardTarget","querySelector","preventDefault","textToCopy","navigator","clipboard","writeText","catch","HTMLInputElement","HTMLTextAreaElement","focus","copyRegion","createElement","classList","add","body","appendChild","remove"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;MA+HMA,2BAA6B,CAACC,WAAYC,cAC5CA,WAAWC,SAGPC,SAASC,YAAY,SACrBC,oBAAoBL,aACb,IAGXM,uBACO,IAULD,oBAAsBL,YAAcO,eAAeP,YACpDQ,MAAKC,iBAAkB,cAASA,eAAgB,MAQ/CH,oBAAsB,IAAMI,iBAC7BF,MAAKG,UAAW,cAASA,QAAS,CAACC,KAAM,cAQxCF,eAAiB,KAAM,mBAAU,0BAA2B,QAU5DH,eAAiBP,YACfA,WAAWa,QAAQC,wBACZC,QAAQC,QAAQhB,WAAWa,QAAQC,0BAGvC,mBAAU,wBAAyB,QAUxCG,qBAAuBC,WACrBA,UAAUC,MAEHD,UAAUC,MACVD,UAAUE,UAEVF,UAAUE,UAGd,SAGPC,QAAS,EACRA,uCACe,OAAQ,CACpB,wBACA,4BA1JJlB,SAASmB,iBAAiB,SAASC,UACzBvB,WAAauB,EAAEC,OAAOC,QAAQ,uCAC/BzB,sBAIAA,WAAWa,QAAQa,6BAIlBzB,WAAaE,SAASwB,cAAc3B,WAAWa,QAAQa,qBACxDzB,kBAMLsB,EAAEK,uBAGIC,WAAaZ,qBAAqBhB,eACnC4B,cAKDC,UAAUC,UACVD,UAAUC,UAAUC,UAAUH,YACzBrB,MAAK,IAAMH,oBAAoBL,cAAaiC,gBAUjDhC,sBAAsBiC,kBAAoBjC,sBAAsBkC,oBAGhElC,WAAWmC,QAEPrC,2BAA2BC,WAAYC,aAEvCD,WAAWoC,YAEZ,OAMGC,WAAalC,SAASmC,cAAc,YAC1CD,WAAWlB,MAAQU,WACnBQ,WAAWE,UAAUC,IAAI,WACzBrC,SAASsC,KAAKC,YAAYL,YAE1BtC,2BAA2BC,WAAYqC,YAGvCA,WAAWM,SACX3C,WAAWoC,aAxCX9B,yBAyIRe,QAAS"} \ No newline at end of file +{"version":3,"file":"copy_to_clipboard.min.js","sources":["../src/copy_to_clipboard.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 * A JavaScript module that enhances a button and text container to support copy-to-clipboard functionality.\n *\n * This module needs to be loaded by pages/templates/modules that require this functionality.\n *\n * To enable copy-to-clipboard functionality, we need a trigger element (usually a button) and a copy target element\n * (e.g. a div, span, text input, or text area).\n *\n * In the trigger element, we need to declare the data-action=\"copytoclipboard\" attribute and set the\n * data-clipboard-target attribute which is the CSS selector that points to the target element that contains the text\n * to be copied.\n *\n * When the text is successfully copied to the clipboard, a toast message that indicates that the copy operation was a success\n * will be shown. This success message can be customised by setting the data-clipboard-success-message attribute in the\n * trigger element.\n *\n * @module core/copy_to_clipboard\n * @copyright 2021 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n *\n * @example Markup for the trigger and target elements\n * \n * \n */\nimport {getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\nimport {prefetchStrings} from 'core/prefetch';\n\n/**\n * Add event listeners to trigger elements through event delegation.\n *\n * @private\n */\nconst addEventListeners = () => {\n document.addEventListener('click', e => {\n const copyButton = e.target.closest('[data-action=\"copytoclipboard\"]');\n if (!copyButton) {\n return;\n }\n\n if (!copyButton.dataset.clipboardTarget) {\n return;\n }\n\n const copyTarget = document.querySelector(copyButton.dataset.clipboardTarget);\n if (!copyTarget) {\n return;\n }\n\n // This is a copy target and there is content.\n // Prevent the default action.\n e.preventDefault();\n\n // We have a copy target - great. Let's copy its content.\n const textToCopy = getTextFromContainer(copyTarget);\n if (!textToCopy) {\n displayFailureToast();\n return;\n }\n\n if (navigator.clipboard) {\n navigator.clipboard.writeText(textToCopy)\n .then(() => displaySuccessToast(copyButton)).catch();\n\n return;\n }\n\n // The clipboard API is not available.\n // This may happen when the page is not served over SSL.\n // Try to fall back to document.execCommand() approach of copying the text.\n // WARNING: This is deprecated functionality that may get dropped at anytime by browsers.\n\n if (copyTarget instanceof HTMLInputElement || copyTarget instanceof HTMLTextAreaElement) {\n // Focus and select the text in the target element.\n // If the execCommand fails, at least the user can readily copy the text.\n copyTarget.focus();\n\n if (copyNodeContentToClipboard(copyButton, copyTarget)) {\n // If the copy was successful then focus back on the copy button.\n copyButton.focus();\n }\n } else {\n // This copyTarget is not an input, or text area so cannot be used with the execCommand('copy') command.\n // To work around this we create a new textarea and copy that.\n // This textarea must be part of the DOM and must be visible.\n // We (ab)use the sr-only tag to ensure that it is considered visible to the browser, whilst being\n // hidden from view by the user.\n const copyRegion = document.createElement('textarea');\n copyRegion.value = textToCopy;\n copyRegion.classList.add('sr-only');\n document.body.appendChild(copyRegion);\n\n copyNodeContentToClipboard(copyButton, copyRegion);\n\n // After copying, remove the temporary element and move focus back to the triggering button.\n copyRegion.remove();\n copyButton.focus();\n }\n });\n};\n\n/**\n * Copy the content of the selected element to the clipboard, and display a notifiction if successful.\n *\n * @param {HTMLElement} copyButton\n * @param {HTMLElement} copyTarget\n * @returns {boolean}\n * @private\n */\nconst copyNodeContentToClipboard = (copyButton, copyTarget) => {\n copyTarget.select();\n\n // Try to copy the text from the target element.\n if (document.execCommand('copy')) {\n displaySuccessToast(copyButton);\n return true;\n }\n\n displayFailureToast();\n return false;\n};\n\n/**\n * Displays a toast containing the success message.\n *\n * @param {HTMLElement} copyButton The element that copies the text from the container.\n * @returns {Promise}\n * @private\n */\nconst displaySuccessToast = copyButton => getSuccessText(copyButton)\n .then(successMessage => addToast(successMessage, {}));\n\n/**\n * Displays a toast containing the failure message.\n *\n * @returns {Promise}\n * @private\n */\nconst displayFailureToast = () => getFailureText()\n .then(message => addToast(message, {type: 'warning'}));\n\n/**\n * Fetches the failure message to show to the user.\n *\n * @returns {Promise}\n * @private\n */\nconst getFailureText = () => getString('unabletocopytoclipboard', 'core');\n\n/**\n * Fetches the success message to show to the user.\n *\n * @param {HTMLElement} copyButton The element that copies the text from the container. This may contain the custom success message\n * via its data-clipboard-success-message attribute.\n * @returns {Promise|*}\n * @private\n */\nconst getSuccessText = copyButton => {\n if (copyButton.dataset.clipboardSuccessMessage) {\n return Promise.resolve(copyButton.dataset.clipboardSuccessMessage);\n }\n\n return getString('textcopiedtoclipboard', 'core');\n};\n\n/**\n * Fetches the text to be copied from the container.\n *\n * @param {HTMLElement} container The element containing the text to be copied.\n * @returns {null|string}\n * @private\n */\nconst getTextFromContainer = container => {\n if (container.value) {\n // For containers which are form elements (e.g. text area, text input), get the element's value.\n return container.value;\n } else if (container.innerText) {\n // For other elements, try to use the innerText attribute.\n return container.innerText;\n }\n\n return null;\n};\n\nlet loaded = false;\nif (!loaded) {\n prefetchStrings('core', [\n 'textcopiedtoclipboard',\n 'unabletocopytoclipboard',\n ]);\n\n // Add event listeners.\n addEventListeners();\n loaded = true;\n}\n"],"names":["copyNodeContentToClipboard","copyButton","copyTarget","select","document","execCommand","displaySuccessToast","displayFailureToast","getSuccessText","then","successMessage","getFailureText","message","type","dataset","clipboardSuccessMessage","Promise","resolve","getTextFromContainer","container","value","innerText","loaded","addEventListener","e","target","closest","clipboardTarget","querySelector","preventDefault","textToCopy","navigator","clipboard","writeText","catch","HTMLInputElement","HTMLTextAreaElement","focus","copyRegion","createElement","classList","add","body","appendChild","remove"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;MA+HMA,2BAA6B,CAACC,WAAYC,cAC5CA,WAAWC,SAGPC,SAASC,YAAY,SACrBC,oBAAoBL,aACb,IAGXM,uBACO,IAULD,oBAAsBL,YAAcO,eAAeP,YACpDQ,MAAKC,iBAAkB,cAASA,eAAgB,MAQ/CH,oBAAsB,IAAMI,iBAC7BF,MAAKG,UAAW,cAASA,QAAS,CAACC,KAAM,cAQxCF,eAAiB,KAAM,kBAAU,0BAA2B,QAU5DH,eAAiBP,YACfA,WAAWa,QAAQC,wBACZC,QAAQC,QAAQhB,WAAWa,QAAQC,0BAGvC,kBAAU,wBAAyB,QAUxCG,qBAAuBC,WACrBA,UAAUC,MAEHD,UAAUC,MACVD,UAAUE,UAEVF,UAAUE,UAGd,SAGPC,QAAS,EACRA,uCACe,OAAQ,CACpB,wBACA,4BA1JJlB,SAASmB,iBAAiB,SAASC,UACzBvB,WAAauB,EAAEC,OAAOC,QAAQ,uCAC/BzB,sBAIAA,WAAWa,QAAQa,6BAIlBzB,WAAaE,SAASwB,cAAc3B,WAAWa,QAAQa,qBACxDzB,kBAMLsB,EAAEK,uBAGIC,WAAaZ,qBAAqBhB,eACnC4B,cAKDC,UAAUC,UACVD,UAAUC,UAAUC,UAAUH,YACzBrB,MAAK,IAAMH,oBAAoBL,cAAaiC,gBAUjDhC,sBAAsBiC,kBAAoBjC,sBAAsBkC,oBAGhElC,WAAWmC,QAEPrC,2BAA2BC,WAAYC,aAEvCD,WAAWoC,YAEZ,OAMGC,WAAalC,SAASmC,cAAc,YAC1CD,WAAWlB,MAAQU,WACnBQ,WAAWE,UAAUC,IAAI,WACzBrC,SAASsC,KAAKC,YAAYL,YAE1BtC,2BAA2BC,WAAYqC,YAGvCA,WAAWM,SACX3C,WAAWoC,aAxCX9B,yBAyIRe,QAAS"} \ No newline at end of file diff --git a/lib/amd/build/datafilter.min.js b/lib/amd/build/datafilter.min.js index ee84c73b060..2378450a148 100644 --- a/lib/amd/build/datafilter.min.js +++ b/lib/amd/build/datafilter.min.js @@ -1,3 +1,3 @@ -define("core/datafilter",["exports","core/datafilter/filtertypes/courseid","core/datafilter/filtertype","core/str","core/notification","core/pending","core/datafilter/selectors","core/templates","core/custom_interaction_events","jquery"],(function(_exports,_courseid,_filtertype,_str,_notification,_pending,_selectors,_templates,_custom_interaction_events,_jquery){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_courseid=_interopRequireDefault(_courseid),_filtertype=_interopRequireDefault(_filtertype),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_selectors=_interopRequireDefault(_selectors),_templates=_interopRequireDefault(_templates),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_jquery=_interopRequireDefault(_jquery);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}return _exports.default=class{constructor(filterSet,applyCallback){this.filterSet=filterSet,this.applyCallback=applyCallback,this.activeFilters={courseid:new _courseid.default("courseid",filterSet)}}init(){this.filterSet.querySelector(_selectors.default.filterset.region).addEventListener("click",(e=>{e.target.closest(_selectors.default.filterset.actions.addRow)&&(e.preventDefault(),this.addFilterRow()),e.target.closest(_selectors.default.filterset.actions.applyFilters)&&(e.preventDefault(),this.updateTableFromFilter()),e.target.closest(_selectors.default.filterset.actions.resetFilters)&&(e.preventDefault(),this.removeAllFilters())})),this.filterSet.querySelector(_selectors.default.filterset.regions.filterlist).addEventListener("click",(e=>{e.target.closest(_selectors.default.filter.actions.remove)&&(e.preventDefault(),this.removeOrReplaceFilterRow(e.target.closest(_selectors.default.filter.region),!0))}));let filterRegion=(0,_jquery.default)(this.getFilterRegion());_custom_interaction_events.default.define(filterRegion,[_custom_interaction_events.default.events.accessibleChange]),filterRegion.on(_custom_interaction_events.default.events.accessibleChange,(e=>{const typeField=e.target.closest(_selectors.default.filter.fields.type);if(typeField&&typeField.value){const filter=e.target.closest(_selectors.default.filter.region);this.addFilter(filter,typeField.value)}})),this.filterSet.querySelector(_selectors.default.filterset.fields.join).addEventListener("change",(e=>{this.filterSet.dataset.filterverb=e.target.value}))}getFilterRegion(){return this.filterSet.querySelector(_selectors.default.filterset.regions.filterlist)}addFilterRow(){const pendingPromise=new _pending.default("core/datafilter:addFilterRow"),rownum=1+this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).length;return _templates.default.renderForPromise("core/datafilter/filter_row",{rownumber:rownum}).then((_ref=>{let{html:html,js:js}=_ref;return _templates.default.appendNodeContents(this.getFilterRegion(),html,js)})).then((filterRow=>{const typeList=this.filterSet.querySelector(_selectors.default.data.typeList);return filterRow.forEach((contentNode=>{const contentTypeList=contentNode.querySelector(_selectors.default.filter.fields.type);contentTypeList&&(contentTypeList.innerHTML=typeList.innerHTML)})),filterRow})).then((filterRow=>(this.updateFiltersOptions(),filterRow))).then((result=>(pendingPromise.resolve(),result))).catch(_notification.default.exception)}getFilterDataSource(filterType){return this.filterSet.querySelector(_selectors.default.filterset.regions.datasource).querySelector(_selectors.default.data.fields.byName(filterType))}async addFilter(filterRow,filterType,initialFilterValues){filterRow.dataset.filterType=filterType;const filterDataNode=this.getFilterDataSource(filterType);let Filter=_filtertype.default;filterDataNode.dataset.filterTypeClass&&(Filter=await("function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require([filterDataNode.dataset.filterTypeClass],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require(filterDataNode.dataset.filterTypeClass)):Promise.resolve(_systemImportTransformerGlobalIdentifier[filterDataNode.dataset.filterTypeClass]))),this.activeFilters[filterType]=new Filter(filterType,this.filterSet,initialFilterValues);const typeField=filterRow.querySelector(_selectors.default.filter.fields.type);return typeField.value=filterType,typeField.disabled="disabled",this.updateFiltersOptions(),this.activeFilters[filterType]}getFilterObject(name){return this.activeFilters[name]}removeOrReplaceFilterRow(filterRow,refreshContent){1===this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).length?this.replaceFilterRow(filterRow,refreshContent):this.removeFilterRow(filterRow,refreshContent)}async removeFilterRow(filterRow){let refreshContent=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];const hasFilterValue=!!filterRow.querySelector(_selectors.default.filter.fields.type).value;this.removeFilterObject(filterRow.dataset.filterType),filterRow.remove(),this.updateFiltersOptions(),hasFilterValue&&refreshContent&&this.updateTableFromFilter();const filterLegends=await this.getAvailableFilterLegends();this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).forEach(((filterRow,index)=>{filterRow.querySelector("legend").innerText=filterLegends[index]}))}replaceFilterRow(filterRow){let refreshContent=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],rowNum=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;return this.removeFilterObject(filterRow.dataset.filterType),_templates.default.renderForPromise("core/datafilter/filter_row",{rownumber:rowNum}).then((_ref2=>{let{html:html,js:js}=_ref2;return _templates.default.replaceNode(filterRow,html,js)})).then((filterRow=>{const typeList=this.filterSet.querySelector(_selectors.default.data.typeList);return filterRow.forEach((contentNode=>{const contentTypeList=contentNode.querySelector(_selectors.default.filter.fields.type);contentTypeList&&(contentTypeList.innerHTML=typeList.innerHTML)})),filterRow})).then((filterRow=>(this.updateFiltersOptions(),filterRow))).then((filterRow=>refreshContent?this.updateTableFromFilter():filterRow)).catch(_notification.default.exception)}removeFilterObject(filterName){if(filterName){const filter=this.getFilterObject(filterName);filter&&(filter.tearDown(),delete this.activeFilters[filterName])}}removeAllFilters(){return this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).forEach((filterRow=>this.removeOrReplaceFilterRow(filterRow,!1))),this.updateTableFromFilter()}removeEmptyFilters(){this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).forEach((filterRow=>{filterRow.querySelector(_selectors.default.filter.fields.type).value||this.removeOrReplaceFilterRow(filterRow,!1)}))}updateFiltersOptions(){const filters=this.getFilterRegion().querySelectorAll(_selectors.default.filter.region);filters.forEach((filterRow=>{filterRow.querySelectorAll(_selectors.default.filter.fields.type+" option").forEach((option=>{option.value===filterRow.dataset.filterType?(option.classList.remove("hidden"),option.disabled=!1):this.activeFilters[option.value]?(option.classList.add("hidden"),option.disabled=!0):(option.classList.remove("hidden"),option.disabled=!1)}))}));const addRowButton=this.filterSet.querySelector(_selectors.default.filterset.actions.addRow);this.filterSet.querySelectorAll(_selectors.default.data.fields.all).length<=filters.length?addRowButton.setAttribute("disabled","disabled"):addRowButton.removeAttribute("disabled"),1===filters.length?(this.filterSet.querySelector(_selectors.default.filterset.regions.filtermatch).classList.add("hidden"),this.filterSet.querySelector(_selectors.default.filterset.fields.join).value=2,this.filterSet.dataset.filterverb=2):this.filterSet.querySelector(_selectors.default.filterset.regions.filtermatch).classList.remove("hidden")}updateTableFromFilter(){const pendingPromise=new _pending.default("core/datafilter:updateTableFromFilter"),filters={};Object.values(this.activeFilters).forEach((filter=>{filters[filter.filterValue.name]=filter.filterValue})),this.applyCallback&&this.applyCallback(filters,pendingPromise)}async getAvailableFilterLegends(){const maxFilters=document.querySelector(_selectors.default.data.typeListSelect).length-1;let requests=[];[...Array(maxFilters)].forEach(((_,rowIndex)=>{requests.push({key:"filterrowlegend",component:"core",param:rowIndex+1})}));return await(0,_str.get_strings)(requests).then((fetchedStrings=>fetchedStrings)).catch(_notification.default.exception)}},_exports.default})); +define("core/datafilter",["exports","core/datafilter/filtertypes/courseid","core/datafilter/filtertype","core/str","core/notification","core/pending","core/datafilter/selectors","core/templates","core/custom_interaction_events","jquery"],(function(_exports,_courseid,_filtertype,_str,_notification,_pending,_selectors,_templates,_custom_interaction_events,_jquery){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_courseid=_interopRequireDefault(_courseid),_filtertype=_interopRequireDefault(_filtertype),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_selectors=_interopRequireDefault(_selectors),_templates=_interopRequireDefault(_templates),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_jquery=_interopRequireDefault(_jquery);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}return _exports.default=class{constructor(filterSet,applyCallback){this.filterSet=filterSet,this.applyCallback=applyCallback,this.activeFilters={courseid:new _courseid.default("courseid",filterSet)}}init(){this.filterSet.querySelector(_selectors.default.filterset.region).addEventListener("click",(e=>{e.target.closest(_selectors.default.filterset.actions.addRow)&&(e.preventDefault(),this.addFilterRow()),e.target.closest(_selectors.default.filterset.actions.applyFilters)&&(e.preventDefault(),this.updateTableFromFilter()),e.target.closest(_selectors.default.filterset.actions.resetFilters)&&(e.preventDefault(),this.removeAllFilters())})),this.filterSet.querySelector(_selectors.default.filterset.regions.filterlist).addEventListener("click",(e=>{e.target.closest(_selectors.default.filter.actions.remove)&&(e.preventDefault(),this.removeOrReplaceFilterRow(e.target.closest(_selectors.default.filter.region),!0))}));let filterRegion=(0,_jquery.default)(this.getFilterRegion());_custom_interaction_events.default.define(filterRegion,[_custom_interaction_events.default.events.accessibleChange]),filterRegion.on(_custom_interaction_events.default.events.accessibleChange,(e=>{const typeField=e.target.closest(_selectors.default.filter.fields.type);if(typeField&&typeField.value){const filter=e.target.closest(_selectors.default.filter.region);this.addFilter(filter,typeField.value)}})),this.filterSet.querySelector(_selectors.default.filterset.fields.join).addEventListener("change",(e=>{this.filterSet.dataset.filterverb=e.target.value}))}getFilterRegion(){return this.filterSet.querySelector(_selectors.default.filterset.regions.filterlist)}addFilterRow(){const pendingPromise=new _pending.default("core/datafilter:addFilterRow"),rownum=1+this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).length;return _templates.default.renderForPromise("core/datafilter/filter_row",{rownumber:rownum}).then((_ref=>{let{html:html,js:js}=_ref;return _templates.default.appendNodeContents(this.getFilterRegion(),html,js)})).then((filterRow=>{const typeList=this.filterSet.querySelector(_selectors.default.data.typeList);return filterRow.forEach((contentNode=>{const contentTypeList=contentNode.querySelector(_selectors.default.filter.fields.type);contentTypeList&&(contentTypeList.innerHTML=typeList.innerHTML)})),filterRow})).then((filterRow=>(this.updateFiltersOptions(),filterRow))).then((result=>(pendingPromise.resolve(),result))).catch(_notification.default.exception)}getFilterDataSource(filterType){return this.filterSet.querySelector(_selectors.default.filterset.regions.datasource).querySelector(_selectors.default.data.fields.byName(filterType))}async addFilter(filterRow,filterType,initialFilterValues){filterRow.dataset.filterType=filterType;const filterDataNode=this.getFilterDataSource(filterType);let Filter=_filtertype.default;filterDataNode.dataset.filterTypeClass&&(Filter=await("function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require([filterDataNode.dataset.filterTypeClass],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require(filterDataNode.dataset.filterTypeClass)):Promise.resolve(_systemImportTransformerGlobalIdentifier[filterDataNode.dataset.filterTypeClass]))),this.activeFilters[filterType]=new Filter(filterType,this.filterSet,initialFilterValues);const typeField=filterRow.querySelector(_selectors.default.filter.fields.type);return typeField.value=filterType,typeField.disabled="disabled",this.updateFiltersOptions(),this.activeFilters[filterType]}getFilterObject(name){return this.activeFilters[name]}removeOrReplaceFilterRow(filterRow,refreshContent){1===this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).length?this.replaceFilterRow(filterRow,refreshContent):this.removeFilterRow(filterRow,refreshContent)}async removeFilterRow(filterRow){let refreshContent=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];const hasFilterValue=!!filterRow.querySelector(_selectors.default.filter.fields.type).value;this.removeFilterObject(filterRow.dataset.filterType),filterRow.remove(),this.updateFiltersOptions(),hasFilterValue&&refreshContent&&this.updateTableFromFilter();const filterLegends=await this.getAvailableFilterLegends();this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).forEach(((filterRow,index)=>{filterRow.querySelector("legend").innerText=filterLegends[index]}))}replaceFilterRow(filterRow){let refreshContent=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],rowNum=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;return this.removeFilterObject(filterRow.dataset.filterType),_templates.default.renderForPromise("core/datafilter/filter_row",{rownumber:rowNum}).then((_ref2=>{let{html:html,js:js}=_ref2;return _templates.default.replaceNode(filterRow,html,js)})).then((filterRow=>{const typeList=this.filterSet.querySelector(_selectors.default.data.typeList);return filterRow.forEach((contentNode=>{const contentTypeList=contentNode.querySelector(_selectors.default.filter.fields.type);contentTypeList&&(contentTypeList.innerHTML=typeList.innerHTML)})),filterRow})).then((filterRow=>(this.updateFiltersOptions(),filterRow))).then((filterRow=>refreshContent?this.updateTableFromFilter():filterRow)).catch(_notification.default.exception)}removeFilterObject(filterName){if(filterName){const filter=this.getFilterObject(filterName);filter&&(filter.tearDown(),delete this.activeFilters[filterName])}}removeAllFilters(){return this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).forEach((filterRow=>this.removeOrReplaceFilterRow(filterRow,!1))),this.updateTableFromFilter()}removeEmptyFilters(){this.getFilterRegion().querySelectorAll(_selectors.default.filter.region).forEach((filterRow=>{filterRow.querySelector(_selectors.default.filter.fields.type).value||this.removeOrReplaceFilterRow(filterRow,!1)}))}updateFiltersOptions(){const filters=this.getFilterRegion().querySelectorAll(_selectors.default.filter.region);filters.forEach((filterRow=>{filterRow.querySelectorAll(_selectors.default.filter.fields.type+" option").forEach((option=>{option.value===filterRow.dataset.filterType?(option.classList.remove("hidden"),option.disabled=!1):this.activeFilters[option.value]?(option.classList.add("hidden"),option.disabled=!0):(option.classList.remove("hidden"),option.disabled=!1)}))}));const addRowButton=this.filterSet.querySelector(_selectors.default.filterset.actions.addRow);this.filterSet.querySelectorAll(_selectors.default.data.fields.all).length<=filters.length?addRowButton.setAttribute("disabled","disabled"):addRowButton.removeAttribute("disabled"),1===filters.length?(this.filterSet.querySelector(_selectors.default.filterset.regions.filtermatch).classList.add("hidden"),this.filterSet.querySelector(_selectors.default.filterset.fields.join).value=2,this.filterSet.dataset.filterverb=2):this.filterSet.querySelector(_selectors.default.filterset.regions.filtermatch).classList.remove("hidden")}updateTableFromFilter(){const pendingPromise=new _pending.default("core/datafilter:updateTableFromFilter"),filters={};Object.values(this.activeFilters).forEach((filter=>{filters[filter.filterValue.name]=filter.filterValue})),this.applyCallback&&this.applyCallback(filters,pendingPromise)}async getAvailableFilterLegends(){const maxFilters=document.querySelector(_selectors.default.data.typeListSelect).length-1;let requests=[];[...Array(maxFilters)].forEach(((_,rowIndex)=>{requests.push({key:"filterrowlegend",component:"core",param:rowIndex+1})}));return await(0,_str.getStrings)(requests).then((fetchedStrings=>fetchedStrings)).catch(_notification.default.exception)}},_exports.default})); //# sourceMappingURL=datafilter.min.js.map \ No newline at end of file diff --git a/lib/amd/build/datafilter.min.js.map b/lib/amd/build/datafilter.min.js.map index bbcd1d7069a..bdd8c2ec619 100644 --- a/lib/amd/build/datafilter.min.js.map +++ b/lib/amd/build/datafilter.min.js.map @@ -1 +1 @@ -{"version":3,"file":"datafilter.min.js","sources":["../src/datafilter.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 * Data filter management.\n *\n * @module core/datafilter\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport CourseFilter from 'core/datafilter/filtertypes/courseid';\nimport GenericFilter from 'core/datafilter/filtertype';\nimport {get_strings as getStrings} from 'core/str';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Selectors from 'core/datafilter/selectors';\nimport Templates from 'core/templates';\nimport CustomEvents from 'core/custom_interaction_events';\nimport jQuery from 'jquery';\n\nexport default class {\n\n /**\n * Initialise the filter on the element with the given filterSet and callback.\n *\n * @param {HTMLElement} filterSet The filter element.\n * @param {Function} applyCallback Callback function when updateTableFromFilter\n */\n constructor(filterSet, applyCallback) {\n\n this.filterSet = filterSet;\n this.applyCallback = applyCallback;\n // Keep a reference to all of the active filters.\n this.activeFilters = {\n courseid: new CourseFilter('courseid', filterSet),\n };\n }\n\n /**\n * Initialise event listeners to the filter.\n */\n init() {\n // Add listeners for the main actions.\n this.filterSet.querySelector(Selectors.filterset.region).addEventListener('click', e => {\n if (e.target.closest(Selectors.filterset.actions.addRow)) {\n e.preventDefault();\n\n this.addFilterRow();\n }\n\n if (e.target.closest(Selectors.filterset.actions.applyFilters)) {\n e.preventDefault();\n\n this.updateTableFromFilter();\n }\n\n if (e.target.closest(Selectors.filterset.actions.resetFilters)) {\n e.preventDefault();\n\n this.removeAllFilters();\n }\n });\n\n // Add the listener to remove a single filter.\n this.filterSet.querySelector(Selectors.filterset.regions.filterlist).addEventListener('click', e => {\n if (e.target.closest(Selectors.filter.actions.remove)) {\n e.preventDefault();\n\n this.removeOrReplaceFilterRow(e.target.closest(Selectors.filter.region), true);\n }\n });\n\n // Add listeners for the filter type selection.\n let filterRegion = jQuery(this.getFilterRegion());\n CustomEvents.define(filterRegion, [CustomEvents.events.accessibleChange]);\n filterRegion.on(CustomEvents.events.accessibleChange, e => {\n const typeField = e.target.closest(Selectors.filter.fields.type);\n if (typeField && typeField.value) {\n const filter = e.target.closest(Selectors.filter.region);\n\n this.addFilter(filter, typeField.value);\n }\n });\n\n this.filterSet.querySelector(Selectors.filterset.fields.join).addEventListener('change', e => {\n this.filterSet.dataset.filterverb = e.target.value;\n });\n }\n\n /**\n * Get the filter list region.\n *\n * @return {HTMLElement}\n */\n getFilterRegion() {\n return this.filterSet.querySelector(Selectors.filterset.regions.filterlist);\n }\n\n /**\n * Add an unselected filter row.\n *\n * @return {Promise}\n */\n addFilterRow() {\n const pendingPromise = new Pending('core/datafilter:addFilterRow');\n const rownum = 1 + this.getFilterRegion().querySelectorAll(Selectors.filter.region).length;\n return Templates.renderForPromise('core/datafilter/filter_row', {\"rownumber\": rownum})\n .then(({html, js}) => {\n const newContentNodes = Templates.appendNodeContents(this.getFilterRegion(), html, js);\n\n return newContentNodes;\n })\n .then(filterRow => {\n // Note: This is a nasty hack.\n // We should try to find a better way of doing this.\n // We do not have the list of types in a readily consumable format, so we take the pre-rendered one and copy\n // it in place.\n const typeList = this.filterSet.querySelector(Selectors.data.typeList);\n\n filterRow.forEach(contentNode => {\n const contentTypeList = contentNode.querySelector(Selectors.filter.fields.type);\n\n if (contentTypeList) {\n contentTypeList.innerHTML = typeList.innerHTML;\n }\n });\n\n return filterRow;\n })\n .then(filterRow => {\n this.updateFiltersOptions();\n\n return filterRow;\n })\n .then(result => {\n pendingPromise.resolve();\n\n return result;\n })\n .catch(Notification.exception);\n }\n\n /**\n * Get the filter data source node fro the specified filter type.\n *\n * @param {String} filterType\n * @return {HTMLElement}\n */\n getFilterDataSource(filterType) {\n const filterDataNode = this.filterSet.querySelector(Selectors.filterset.regions.datasource);\n\n return filterDataNode.querySelector(Selectors.data.fields.byName(filterType));\n }\n\n /**\n * Add a filter to the list of active filters, performing any necessary setup.\n *\n * @param {HTMLElement} filterRow\n * @param {String} filterType\n * @param {Array} initialFilterValues The initially selected values for the filter\n * @returns {Filter}\n */\n async addFilter(filterRow, filterType, initialFilterValues) {\n // Name the filter on the filter row.\n filterRow.dataset.filterType = filterType;\n\n const filterDataNode = this.getFilterDataSource(filterType);\n\n // Instantiate the Filter class.\n let Filter = GenericFilter;\n if (filterDataNode.dataset.filterTypeClass) {\n Filter = await import(filterDataNode.dataset.filterTypeClass);\n }\n this.activeFilters[filterType] = new Filter(filterType, this.filterSet, initialFilterValues);\n\n // Disable the select.\n const typeField = filterRow.querySelector(Selectors.filter.fields.type);\n typeField.value = filterType;\n typeField.disabled = 'disabled';\n\n // Update the list of available filter types.\n this.updateFiltersOptions();\n\n return this.activeFilters[filterType];\n }\n\n /**\n * Get the registered filter class for the named filter.\n *\n * @param {String} name\n * @return {Object} See the Filter class.\n */\n getFilterObject(name) {\n return this.activeFilters[name];\n }\n\n /**\n * Remove or replace the specified filter row and associated class, ensuring that if there is only one filter row,\n * that it is replaced instead of being removed.\n *\n * @param {HTMLElement} filterRow\n * @param {Bool} refreshContent Whether to refresh the table content when removing\n */\n removeOrReplaceFilterRow(filterRow, refreshContent) {\n const filterCount = this.getFilterRegion().querySelectorAll(Selectors.filter.region).length;\n if (filterCount === 1) {\n this.replaceFilterRow(filterRow, refreshContent);\n } else {\n this.removeFilterRow(filterRow, refreshContent);\n }\n }\n\n /**\n * Remove the specified filter row and associated class.\n *\n * @param {HTMLElement} filterRow\n * @param {Bool} refreshContent Whether to refresh the table content when removing\n */\n async removeFilterRow(filterRow, refreshContent = true) {\n const filterType = filterRow.querySelector(Selectors.filter.fields.type);\n const hasFilterValue = !!filterType.value;\n\n // Remove the filter object.\n this.removeFilterObject(filterRow.dataset.filterType);\n\n // Remove the actual filter HTML.\n filterRow.remove();\n\n // Update the list of available filter types.\n this.updateFiltersOptions();\n\n if (hasFilterValue && refreshContent) {\n // Refresh the table if there was any content in this row.\n this.updateTableFromFilter();\n }\n\n // Update filter fieldset legends.\n const filterLegends = await this.getAvailableFilterLegends();\n\n this.getFilterRegion().querySelectorAll(Selectors.filter.region).forEach((filterRow, index) => {\n filterRow.querySelector('legend').innerText = filterLegends[index];\n });\n\n }\n\n /**\n * Replace the specified filter row with a new one.\n *\n * @param {HTMLElement} filterRow\n * @param {Bool} refreshContent Whether to refresh the table content when removing\n * @param {Number} rowNum The number used to label the filter fieldset legend (eg Row 1). Defaults to 1 (the first filter).\n * @return {Promise}\n */\n replaceFilterRow(filterRow, refreshContent = true, rowNum = 1) {\n // Remove the filter object.\n this.removeFilterObject(filterRow.dataset.filterType);\n\n return Templates.renderForPromise('core/datafilter/filter_row', {\"rownumber\": rowNum})\n .then(({html, js}) => {\n const newContentNodes = Templates.replaceNode(filterRow, html, js);\n\n return newContentNodes;\n })\n .then(filterRow => {\n // Note: This is a nasty hack.\n // We should try to find a better way of doing this.\n // We do not have the list of types in a readily consumable format, so we take the pre-rendered one and copy\n // it in place.\n const typeList = this.filterSet.querySelector(Selectors.data.typeList);\n\n filterRow.forEach(contentNode => {\n const contentTypeList = contentNode.querySelector(Selectors.filter.fields.type);\n\n if (contentTypeList) {\n contentTypeList.innerHTML = typeList.innerHTML;\n }\n });\n\n return filterRow;\n })\n .then(filterRow => {\n this.updateFiltersOptions();\n\n return filterRow;\n })\n .then(filterRow => {\n // Refresh the table.\n if (refreshContent) {\n return this.updateTableFromFilter();\n } else {\n return filterRow;\n }\n })\n .catch(Notification.exception);\n }\n\n /**\n * Remove the Filter Object from the register.\n *\n * @param {string} filterName The name of the filter to be removed\n */\n removeFilterObject(filterName) {\n if (filterName) {\n const filter = this.getFilterObject(filterName);\n if (filter) {\n filter.tearDown();\n\n // Remove from the list of active filters.\n delete this.activeFilters[filterName];\n }\n }\n }\n\n /**\n * Remove all filters.\n *\n * @returns {Promise}\n */\n removeAllFilters() {\n const filters = this.getFilterRegion().querySelectorAll(Selectors.filter.region);\n filters.forEach(filterRow => this.removeOrReplaceFilterRow(filterRow, false));\n\n // Refresh the table.\n return this.updateTableFromFilter();\n }\n\n /**\n * Remove any empty filters.\n */\n removeEmptyFilters() {\n const filters = this.getFilterRegion().querySelectorAll(Selectors.filter.region);\n filters.forEach(filterRow => {\n const filterType = filterRow.querySelector(Selectors.filter.fields.type);\n if (!filterType.value) {\n this.removeOrReplaceFilterRow(filterRow, false);\n }\n });\n }\n\n /**\n * Update the list of filter types to filter out those already selected.\n */\n updateFiltersOptions() {\n const filters = this.getFilterRegion().querySelectorAll(Selectors.filter.region);\n filters.forEach(filterRow => {\n const options = filterRow.querySelectorAll(Selectors.filter.fields.type + ' option');\n options.forEach(option => {\n if (option.value === filterRow.dataset.filterType) {\n option.classList.remove('hidden');\n option.disabled = false;\n } else if (this.activeFilters[option.value]) {\n option.classList.add('hidden');\n option.disabled = true;\n } else {\n option.classList.remove('hidden');\n option.disabled = false;\n }\n });\n });\n\n // Configure the state of the \"Add row\" button.\n // This button is disabled when there is a filter row available for each condition.\n const addRowButton = this.filterSet.querySelector(Selectors.filterset.actions.addRow);\n const filterDataNode = this.filterSet.querySelectorAll(Selectors.data.fields.all);\n if (filterDataNode.length <= filters.length) {\n addRowButton.setAttribute('disabled', 'disabled');\n } else {\n addRowButton.removeAttribute('disabled');\n }\n\n if (filters.length === 1) {\n this.filterSet.querySelector(Selectors.filterset.regions.filtermatch).classList.add('hidden');\n this.filterSet.querySelector(Selectors.filterset.fields.join).value = 2;\n this.filterSet.dataset.filterverb = 2;\n } else {\n this.filterSet.querySelector(Selectors.filterset.regions.filtermatch).classList.remove('hidden');\n }\n }\n\n /**\n * Update the Dynamic table based upon the current filter.\n */\n updateTableFromFilter() {\n const pendingPromise = new Pending('core/datafilter:updateTableFromFilter');\n\n const filters = {};\n Object.values(this.activeFilters).forEach(filter => {\n filters[filter.filterValue.name] = filter.filterValue;\n });\n\n if (this.applyCallback) {\n this.applyCallback(filters, pendingPromise);\n }\n }\n\n /**\n * Fetch the strings used to populate the fieldset legends for the maximum number of filters possible.\n *\n * @return {array}\n */\n async getAvailableFilterLegends() {\n const maxFilters = document.querySelector(Selectors.data.typeListSelect).length - 1;\n let requests = [];\n\n [...Array(maxFilters)].forEach((_, rowIndex) => {\n requests.push({\n \"key\": \"filterrowlegend\",\n \"component\": \"core\",\n // Add 1 since rows begin at 1 (index begins at zero).\n \"param\": rowIndex + 1\n });\n });\n\n const legendStrings = await getStrings(requests)\n .then(fetchedStrings => {\n return fetchedStrings;\n })\n .catch(Notification.exception);\n\n return legendStrings;\n }\n\n}\n"],"names":["constructor","filterSet","applyCallback","activeFilters","courseid","CourseFilter","init","querySelector","Selectors","filterset","region","addEventListener","e","target","closest","actions","addRow","preventDefault","addFilterRow","applyFilters","updateTableFromFilter","resetFilters","removeAllFilters","regions","filterlist","filter","remove","removeOrReplaceFilterRow","filterRegion","this","getFilterRegion","define","CustomEvents","events","accessibleChange","on","typeField","fields","type","value","addFilter","join","dataset","filterverb","pendingPromise","Pending","rownum","querySelectorAll","length","Templates","renderForPromise","then","_ref","html","js","appendNodeContents","filterRow","typeList","data","forEach","contentNode","contentTypeList","innerHTML","updateFiltersOptions","result","resolve","catch","Notification","exception","getFilterDataSource","filterType","datasource","byName","initialFilterValues","filterDataNode","Filter","GenericFilter","filterTypeClass","disabled","getFilterObject","name","refreshContent","replaceFilterRow","removeFilterRow","hasFilterValue","removeFilterObject","filterLegends","getAvailableFilterLegends","index","innerText","rowNum","_ref2","replaceNode","filterName","tearDown","removeEmptyFilters","filters","option","classList","add","addRowButton","all","setAttribute","removeAttribute","filtermatch","Object","values","filterValue","maxFilters","document","typeListSelect","requests","Array","_","rowIndex","push","fetchedStrings"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"datafilter.min.js","sources":["../src/datafilter.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 * Data filter management.\n *\n * @module core/datafilter\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport CourseFilter from 'core/datafilter/filtertypes/courseid';\nimport GenericFilter from 'core/datafilter/filtertype';\nimport {getStrings} from 'core/str';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Selectors from 'core/datafilter/selectors';\nimport Templates from 'core/templates';\nimport CustomEvents from 'core/custom_interaction_events';\nimport jQuery from 'jquery';\n\nexport default class {\n\n /**\n * Initialise the filter on the element with the given filterSet and callback.\n *\n * @param {HTMLElement} filterSet The filter element.\n * @param {Function} applyCallback Callback function when updateTableFromFilter\n */\n constructor(filterSet, applyCallback) {\n\n this.filterSet = filterSet;\n this.applyCallback = applyCallback;\n // Keep a reference to all of the active filters.\n this.activeFilters = {\n courseid: new CourseFilter('courseid', filterSet),\n };\n }\n\n /**\n * Initialise event listeners to the filter.\n */\n init() {\n // Add listeners for the main actions.\n this.filterSet.querySelector(Selectors.filterset.region).addEventListener('click', e => {\n if (e.target.closest(Selectors.filterset.actions.addRow)) {\n e.preventDefault();\n\n this.addFilterRow();\n }\n\n if (e.target.closest(Selectors.filterset.actions.applyFilters)) {\n e.preventDefault();\n\n this.updateTableFromFilter();\n }\n\n if (e.target.closest(Selectors.filterset.actions.resetFilters)) {\n e.preventDefault();\n\n this.removeAllFilters();\n }\n });\n\n // Add the listener to remove a single filter.\n this.filterSet.querySelector(Selectors.filterset.regions.filterlist).addEventListener('click', e => {\n if (e.target.closest(Selectors.filter.actions.remove)) {\n e.preventDefault();\n\n this.removeOrReplaceFilterRow(e.target.closest(Selectors.filter.region), true);\n }\n });\n\n // Add listeners for the filter type selection.\n let filterRegion = jQuery(this.getFilterRegion());\n CustomEvents.define(filterRegion, [CustomEvents.events.accessibleChange]);\n filterRegion.on(CustomEvents.events.accessibleChange, e => {\n const typeField = e.target.closest(Selectors.filter.fields.type);\n if (typeField && typeField.value) {\n const filter = e.target.closest(Selectors.filter.region);\n\n this.addFilter(filter, typeField.value);\n }\n });\n\n this.filterSet.querySelector(Selectors.filterset.fields.join).addEventListener('change', e => {\n this.filterSet.dataset.filterverb = e.target.value;\n });\n }\n\n /**\n * Get the filter list region.\n *\n * @return {HTMLElement}\n */\n getFilterRegion() {\n return this.filterSet.querySelector(Selectors.filterset.regions.filterlist);\n }\n\n /**\n * Add an unselected filter row.\n *\n * @return {Promise}\n */\n addFilterRow() {\n const pendingPromise = new Pending('core/datafilter:addFilterRow');\n const rownum = 1 + this.getFilterRegion().querySelectorAll(Selectors.filter.region).length;\n return Templates.renderForPromise('core/datafilter/filter_row', {\"rownumber\": rownum})\n .then(({html, js}) => {\n const newContentNodes = Templates.appendNodeContents(this.getFilterRegion(), html, js);\n\n return newContentNodes;\n })\n .then(filterRow => {\n // Note: This is a nasty hack.\n // We should try to find a better way of doing this.\n // We do not have the list of types in a readily consumable format, so we take the pre-rendered one and copy\n // it in place.\n const typeList = this.filterSet.querySelector(Selectors.data.typeList);\n\n filterRow.forEach(contentNode => {\n const contentTypeList = contentNode.querySelector(Selectors.filter.fields.type);\n\n if (contentTypeList) {\n contentTypeList.innerHTML = typeList.innerHTML;\n }\n });\n\n return filterRow;\n })\n .then(filterRow => {\n this.updateFiltersOptions();\n\n return filterRow;\n })\n .then(result => {\n pendingPromise.resolve();\n\n return result;\n })\n .catch(Notification.exception);\n }\n\n /**\n * Get the filter data source node fro the specified filter type.\n *\n * @param {String} filterType\n * @return {HTMLElement}\n */\n getFilterDataSource(filterType) {\n const filterDataNode = this.filterSet.querySelector(Selectors.filterset.regions.datasource);\n\n return filterDataNode.querySelector(Selectors.data.fields.byName(filterType));\n }\n\n /**\n * Add a filter to the list of active filters, performing any necessary setup.\n *\n * @param {HTMLElement} filterRow\n * @param {String} filterType\n * @param {Array} initialFilterValues The initially selected values for the filter\n * @returns {Filter}\n */\n async addFilter(filterRow, filterType, initialFilterValues) {\n // Name the filter on the filter row.\n filterRow.dataset.filterType = filterType;\n\n const filterDataNode = this.getFilterDataSource(filterType);\n\n // Instantiate the Filter class.\n let Filter = GenericFilter;\n if (filterDataNode.dataset.filterTypeClass) {\n Filter = await import(filterDataNode.dataset.filterTypeClass);\n }\n this.activeFilters[filterType] = new Filter(filterType, this.filterSet, initialFilterValues);\n\n // Disable the select.\n const typeField = filterRow.querySelector(Selectors.filter.fields.type);\n typeField.value = filterType;\n typeField.disabled = 'disabled';\n\n // Update the list of available filter types.\n this.updateFiltersOptions();\n\n return this.activeFilters[filterType];\n }\n\n /**\n * Get the registered filter class for the named filter.\n *\n * @param {String} name\n * @return {Object} See the Filter class.\n */\n getFilterObject(name) {\n return this.activeFilters[name];\n }\n\n /**\n * Remove or replace the specified filter row and associated class, ensuring that if there is only one filter row,\n * that it is replaced instead of being removed.\n *\n * @param {HTMLElement} filterRow\n * @param {Bool} refreshContent Whether to refresh the table content when removing\n */\n removeOrReplaceFilterRow(filterRow, refreshContent) {\n const filterCount = this.getFilterRegion().querySelectorAll(Selectors.filter.region).length;\n if (filterCount === 1) {\n this.replaceFilterRow(filterRow, refreshContent);\n } else {\n this.removeFilterRow(filterRow, refreshContent);\n }\n }\n\n /**\n * Remove the specified filter row and associated class.\n *\n * @param {HTMLElement} filterRow\n * @param {Bool} refreshContent Whether to refresh the table content when removing\n */\n async removeFilterRow(filterRow, refreshContent = true) {\n const filterType = filterRow.querySelector(Selectors.filter.fields.type);\n const hasFilterValue = !!filterType.value;\n\n // Remove the filter object.\n this.removeFilterObject(filterRow.dataset.filterType);\n\n // Remove the actual filter HTML.\n filterRow.remove();\n\n // Update the list of available filter types.\n this.updateFiltersOptions();\n\n if (hasFilterValue && refreshContent) {\n // Refresh the table if there was any content in this row.\n this.updateTableFromFilter();\n }\n\n // Update filter fieldset legends.\n const filterLegends = await this.getAvailableFilterLegends();\n\n this.getFilterRegion().querySelectorAll(Selectors.filter.region).forEach((filterRow, index) => {\n filterRow.querySelector('legend').innerText = filterLegends[index];\n });\n\n }\n\n /**\n * Replace the specified filter row with a new one.\n *\n * @param {HTMLElement} filterRow\n * @param {Bool} refreshContent Whether to refresh the table content when removing\n * @param {Number} rowNum The number used to label the filter fieldset legend (eg Row 1). Defaults to 1 (the first filter).\n * @return {Promise}\n */\n replaceFilterRow(filterRow, refreshContent = true, rowNum = 1) {\n // Remove the filter object.\n this.removeFilterObject(filterRow.dataset.filterType);\n\n return Templates.renderForPromise('core/datafilter/filter_row', {\"rownumber\": rowNum})\n .then(({html, js}) => {\n const newContentNodes = Templates.replaceNode(filterRow, html, js);\n\n return newContentNodes;\n })\n .then(filterRow => {\n // Note: This is a nasty hack.\n // We should try to find a better way of doing this.\n // We do not have the list of types in a readily consumable format, so we take the pre-rendered one and copy\n // it in place.\n const typeList = this.filterSet.querySelector(Selectors.data.typeList);\n\n filterRow.forEach(contentNode => {\n const contentTypeList = contentNode.querySelector(Selectors.filter.fields.type);\n\n if (contentTypeList) {\n contentTypeList.innerHTML = typeList.innerHTML;\n }\n });\n\n return filterRow;\n })\n .then(filterRow => {\n this.updateFiltersOptions();\n\n return filterRow;\n })\n .then(filterRow => {\n // Refresh the table.\n if (refreshContent) {\n return this.updateTableFromFilter();\n } else {\n return filterRow;\n }\n })\n .catch(Notification.exception);\n }\n\n /**\n * Remove the Filter Object from the register.\n *\n * @param {string} filterName The name of the filter to be removed\n */\n removeFilterObject(filterName) {\n if (filterName) {\n const filter = this.getFilterObject(filterName);\n if (filter) {\n filter.tearDown();\n\n // Remove from the list of active filters.\n delete this.activeFilters[filterName];\n }\n }\n }\n\n /**\n * Remove all filters.\n *\n * @returns {Promise}\n */\n removeAllFilters() {\n const filters = this.getFilterRegion().querySelectorAll(Selectors.filter.region);\n filters.forEach(filterRow => this.removeOrReplaceFilterRow(filterRow, false));\n\n // Refresh the table.\n return this.updateTableFromFilter();\n }\n\n /**\n * Remove any empty filters.\n */\n removeEmptyFilters() {\n const filters = this.getFilterRegion().querySelectorAll(Selectors.filter.region);\n filters.forEach(filterRow => {\n const filterType = filterRow.querySelector(Selectors.filter.fields.type);\n if (!filterType.value) {\n this.removeOrReplaceFilterRow(filterRow, false);\n }\n });\n }\n\n /**\n * Update the list of filter types to filter out those already selected.\n */\n updateFiltersOptions() {\n const filters = this.getFilterRegion().querySelectorAll(Selectors.filter.region);\n filters.forEach(filterRow => {\n const options = filterRow.querySelectorAll(Selectors.filter.fields.type + ' option');\n options.forEach(option => {\n if (option.value === filterRow.dataset.filterType) {\n option.classList.remove('hidden');\n option.disabled = false;\n } else if (this.activeFilters[option.value]) {\n option.classList.add('hidden');\n option.disabled = true;\n } else {\n option.classList.remove('hidden');\n option.disabled = false;\n }\n });\n });\n\n // Configure the state of the \"Add row\" button.\n // This button is disabled when there is a filter row available for each condition.\n const addRowButton = this.filterSet.querySelector(Selectors.filterset.actions.addRow);\n const filterDataNode = this.filterSet.querySelectorAll(Selectors.data.fields.all);\n if (filterDataNode.length <= filters.length) {\n addRowButton.setAttribute('disabled', 'disabled');\n } else {\n addRowButton.removeAttribute('disabled');\n }\n\n if (filters.length === 1) {\n this.filterSet.querySelector(Selectors.filterset.regions.filtermatch).classList.add('hidden');\n this.filterSet.querySelector(Selectors.filterset.fields.join).value = 2;\n this.filterSet.dataset.filterverb = 2;\n } else {\n this.filterSet.querySelector(Selectors.filterset.regions.filtermatch).classList.remove('hidden');\n }\n }\n\n /**\n * Update the Dynamic table based upon the current filter.\n */\n updateTableFromFilter() {\n const pendingPromise = new Pending('core/datafilter:updateTableFromFilter');\n\n const filters = {};\n Object.values(this.activeFilters).forEach(filter => {\n filters[filter.filterValue.name] = filter.filterValue;\n });\n\n if (this.applyCallback) {\n this.applyCallback(filters, pendingPromise);\n }\n }\n\n /**\n * Fetch the strings used to populate the fieldset legends for the maximum number of filters possible.\n *\n * @return {array}\n */\n async getAvailableFilterLegends() {\n const maxFilters = document.querySelector(Selectors.data.typeListSelect).length - 1;\n let requests = [];\n\n [...Array(maxFilters)].forEach((_, rowIndex) => {\n requests.push({\n \"key\": \"filterrowlegend\",\n \"component\": \"core\",\n // Add 1 since rows begin at 1 (index begins at zero).\n \"param\": rowIndex + 1\n });\n });\n\n const legendStrings = await getStrings(requests)\n .then(fetchedStrings => {\n return fetchedStrings;\n })\n .catch(Notification.exception);\n\n return legendStrings;\n }\n\n}\n"],"names":["constructor","filterSet","applyCallback","activeFilters","courseid","CourseFilter","init","querySelector","Selectors","filterset","region","addEventListener","e","target","closest","actions","addRow","preventDefault","addFilterRow","applyFilters","updateTableFromFilter","resetFilters","removeAllFilters","regions","filterlist","filter","remove","removeOrReplaceFilterRow","filterRegion","this","getFilterRegion","define","CustomEvents","events","accessibleChange","on","typeField","fields","type","value","addFilter","join","dataset","filterverb","pendingPromise","Pending","rownum","querySelectorAll","length","Templates","renderForPromise","then","_ref","html","js","appendNodeContents","filterRow","typeList","data","forEach","contentNode","contentTypeList","innerHTML","updateFiltersOptions","result","resolve","catch","Notification","exception","getFilterDataSource","filterType","datasource","byName","initialFilterValues","filterDataNode","Filter","GenericFilter","filterTypeClass","disabled","getFilterObject","name","refreshContent","replaceFilterRow","removeFilterRow","hasFilterValue","removeFilterObject","filterLegends","getAvailableFilterLegends","index","innerText","rowNum","_ref2","replaceNode","filterName","tearDown","removeEmptyFilters","filters","option","classList","add","addRowButton","all","setAttribute","removeAttribute","filtermatch","Object","values","filterValue","maxFilters","document","typeListSelect","requests","Array","_","rowIndex","push","fetchedStrings"],"mappings":"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"} \ No newline at end of file diff --git a/lib/amd/build/datafilter/filtertype.min.js b/lib/amd/build/datafilter/filtertype.min.js index 84b94559f41..720174fb58d 100644 --- a/lib/amd/build/datafilter/filtertype.min.js +++ b/lib/amd/build/datafilter/filtertype.min.js @@ -5,6 +5,6 @@ define("core/datafilter/filtertype",["exports","core/form-autocomplete","core/da * @module core/datafilter/filtertype * @copyright 2020 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_formAutocomplete=_interopRequireDefault(_formAutocomplete),_selectors=_interopRequireDefault(_selectors);return _exports.default=class{constructor(filterType,rootNode,initialValues){this.filterType=filterType,this.rootNode=rootNode,this.addValueSelector(initialValues)}tearDown(){}get placeholder(){return(0,_str.get_string)("placeholdertypeorselect","core")}get showSuggestions(){return!0}async addValueSelector(){let initialValues=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];const filterValueNode=this.getFilterValueNode(),sourceDataNode=this.getSourceDataForFilter();if(!sourceDataNode)return;filterValueNode.innerHTML=sourceDataNode.outerHTML;const dataSource=filterValueNode.querySelector("select");dataSource.id="filter-value-"+dataSource.getAttribute("data-field-name");const filterValueLabel=document.createElement("label");filterValueLabel.setAttribute("for",dataSource.id),filterValueLabel.classList.add("sr-only"),filterValueLabel.innerText=dataSource.getAttribute("data-field-title"),filterValueNode.appendChild(filterValueLabel),initialValues.forEach((filterValue=>{let selectedOption=dataSource.querySelector('option[value="'.concat(filterValue,'"]'));selectedOption?selectedOption.selected=!0:this.showSuggestions||(selectedOption=document.createElement("option"),selectedOption.value=filterValue,selectedOption.innerHTML=filterValue,selectedOption.selected=!0,dataSource.append(selectedOption))})),_formAutocomplete.default.enhance(dataSource,"1"==dataSource.dataset.allowCustom,null,await this.placeholder,!1,this.showSuggestions,null,!dataSource.multiple,{items:"core/datafilter/autocomplete_selection_items",layout:"core/datafilter/autocomplete_layout",selection:"core/datafilter/autocomplete_selection"})}get filterRoot(){return this.rootNode.querySelector(_selectors.default.filter.byName(this.filterType))}getSourceDataForFilter(){return this.rootNode.querySelector(_selectors.default.filterset.regions.datasource).querySelector(_selectors.default.data.fields.byName(this.filterType))}getFilterValueNode(){return this.filterRoot.querySelector(_selectors.default.filter.regions.values)}get name(){return this.filterType}get jointype(){return parseInt(this.filterRoot.querySelector(_selectors.default.filter.fields.join).value,10)}get rawValues(){const filterValueSelect=this.getFilterValueNode().querySelector("select");return Object.values((select=filterValueSelect,select.querySelectorAll(":checked"))).map((option=>option.value));var select}get values(){return this.rawValues.map((option=>parseInt(option,10)))}get filterValue(){return{name:this.name,jointype:this.jointype,values:this.values}}},_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_formAutocomplete=_interopRequireDefault(_formAutocomplete),_selectors=_interopRequireDefault(_selectors);return _exports.default=class{constructor(filterType,rootNode,initialValues){this.filterType=filterType,this.rootNode=rootNode,this.addValueSelector(initialValues)}tearDown(){}get placeholder(){return(0,_str.getString)("placeholdertypeorselect","core")}get showSuggestions(){return!0}async addValueSelector(){let initialValues=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];const filterValueNode=this.getFilterValueNode(),sourceDataNode=this.getSourceDataForFilter();if(!sourceDataNode)return;filterValueNode.innerHTML=sourceDataNode.outerHTML;const dataSource=filterValueNode.querySelector("select");dataSource.id="filter-value-"+dataSource.getAttribute("data-field-name");const filterValueLabel=document.createElement("label");filterValueLabel.setAttribute("for",dataSource.id),filterValueLabel.classList.add("sr-only"),filterValueLabel.innerText=dataSource.getAttribute("data-field-title"),filterValueNode.appendChild(filterValueLabel),initialValues.forEach((filterValue=>{let selectedOption=dataSource.querySelector('option[value="'.concat(filterValue,'"]'));selectedOption?selectedOption.selected=!0:this.showSuggestions||(selectedOption=document.createElement("option"),selectedOption.value=filterValue,selectedOption.innerHTML=filterValue,selectedOption.selected=!0,dataSource.append(selectedOption))})),_formAutocomplete.default.enhance(dataSource,"1"==dataSource.dataset.allowCustom,null,await this.placeholder,!1,this.showSuggestions,null,!dataSource.multiple,{items:"core/datafilter/autocomplete_selection_items",layout:"core/datafilter/autocomplete_layout",selection:"core/datafilter/autocomplete_selection"})}get filterRoot(){return this.rootNode.querySelector(_selectors.default.filter.byName(this.filterType))}getSourceDataForFilter(){return this.rootNode.querySelector(_selectors.default.filterset.regions.datasource).querySelector(_selectors.default.data.fields.byName(this.filterType))}getFilterValueNode(){return this.filterRoot.querySelector(_selectors.default.filter.regions.values)}get name(){return this.filterType}get jointype(){return parseInt(this.filterRoot.querySelector(_selectors.default.filter.fields.join).value,10)}get rawValues(){const filterValueSelect=this.getFilterValueNode().querySelector("select");return Object.values((select=filterValueSelect,select.querySelectorAll(":checked"))).map((option=>option.value));var select}get values(){return this.rawValues.map((option=>parseInt(option,10)))}get filterValue(){return{name:this.name,jointype:this.jointype,values:this.values}}},_exports.default})); //# sourceMappingURL=filtertype.min.js.map \ No newline at end of file diff --git a/lib/amd/build/datafilter/filtertype.min.js.map b/lib/amd/build/datafilter/filtertype.min.js.map index a8b5c6eccf7..a5cb0a32f6c 100644 --- a/lib/amd/build/datafilter/filtertype.min.js.map +++ b/lib/amd/build/datafilter/filtertype.min.js.map @@ -1 +1 @@ -{"version":3,"file":"filtertype.min.js","sources":["../../src/datafilter/filtertype.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 * Base Filter class for a filter type in the filter UI.\n *\n * @module core/datafilter/filtertype\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Autocomplete from 'core/form-autocomplete';\nimport Selectors from 'core/datafilter/selectors';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Fetch all checked options in the select.\n *\n * This is a poor-man's polyfill for select.selectedOptions, which is not available in IE11.\n *\n * @param {HTMLSelectElement} select\n * @returns {HTMLOptionElement[]} All selected options\n */\nconst getOptionsForSelect = select => {\n return select.querySelectorAll(':checked');\n};\n\nexport default class {\n\n /**\n * Constructor for a new filter.\n *\n * @param {String} filterType The type of filter that this relates to\n * @param {HTMLElement} rootNode The root node for the participants filterset\n * @param {Array} initialValues The initial values for the selector\n */\n constructor(filterType, rootNode, initialValues) {\n this.filterType = filterType;\n this.rootNode = rootNode;\n\n this.addValueSelector(initialValues);\n }\n\n /**\n * Perform any tear-down for this filter type.\n */\n tearDown() {\n // eslint-disable-line no-empty-function\n }\n\n /**\n * Get the placeholder to use when showing the value selector.\n *\n * @return {Promise} Resolving to a String\n */\n get placeholder() {\n return getString('placeholdertypeorselect', 'core');\n }\n\n /**\n * Whether to show suggestions in the autocomplete.\n *\n * @return {Boolean}\n */\n get showSuggestions() {\n return true;\n }\n\n /**\n * Add the value selector to the filter row.\n *\n * @param {Array} initialValues\n */\n async addValueSelector(initialValues = []) {\n const filterValueNode = this.getFilterValueNode();\n\n // Copy the data in place.\n const sourceDataNode = this.getSourceDataForFilter();\n if (!sourceDataNode) {\n return;\n }\n filterValueNode.innerHTML = sourceDataNode.outerHTML;\n\n const dataSource = filterValueNode.querySelector('select');\n\n // Set an ID for this filter value element.\n dataSource.id = 'filter-value-' + dataSource.getAttribute('data-field-name');\n\n // Create a hidden label for the filter value.\n const filterValueLabel = document.createElement('label');\n filterValueLabel.setAttribute('for', dataSource.id);\n filterValueLabel.classList.add('sr-only');\n filterValueLabel.innerText = dataSource.getAttribute('data-field-title');\n\n // Append this label to the filter value container.\n filterValueNode.appendChild(filterValueLabel);\n\n // If there are any initial values then attempt to apply them.\n initialValues.forEach(filterValue => {\n let selectedOption = dataSource.querySelector(`option[value=\"${filterValue}\"]`);\n if (selectedOption) {\n selectedOption.selected = true;\n } else if (!this.showSuggestions) {\n selectedOption = document.createElement('option');\n selectedOption.value = filterValue;\n selectedOption.innerHTML = filterValue;\n selectedOption.selected = true;\n\n dataSource.append(selectedOption);\n }\n });\n\n Autocomplete.enhance(\n // The source select element.\n dataSource,\n\n // Whether to allow 'tags' (custom entries).\n dataSource.dataset.allowCustom == \"1\",\n\n // We do not require AJAX at all as standard.\n null,\n\n // The string to use as a placeholder.\n await this.placeholder,\n\n // Disable case sensitivity on searches.\n false,\n\n // Show suggestions.\n this.showSuggestions,\n\n // Do not override the 'no suggestions' string.\n null,\n\n // Close the suggestions if this is not a multi-select.\n !dataSource.multiple,\n\n // Template overrides.\n {\n items: 'core/datafilter/autocomplete_selection_items',\n layout: 'core/datafilter/autocomplete_layout',\n selection: 'core/datafilter/autocomplete_selection',\n }\n );\n }\n\n /**\n * Get the root node for this filter.\n *\n * @returns {HTMLElement}\n */\n get filterRoot() {\n return this.rootNode.querySelector(Selectors.filter.byName(this.filterType));\n }\n\n /**\n * Get the possible data for this filter type.\n *\n * @returns {Array}\n */\n getSourceDataForFilter() {\n const filterDataNode = this.rootNode.querySelector(Selectors.filterset.regions.datasource);\n\n return filterDataNode.querySelector(Selectors.data.fields.byName(this.filterType));\n }\n\n /**\n * Get the HTMLElement which contains the value selector.\n *\n * @returns {HTMLElement}\n */\n getFilterValueNode() {\n return this.filterRoot.querySelector(Selectors.filter.regions.values);\n }\n\n /**\n * Get the name of this filter.\n *\n * @returns {String}\n */\n get name() {\n return this.filterType;\n }\n\n /**\n * Get the type of join specified.\n *\n * @returns {Number}\n */\n get jointype() {\n return parseInt(this.filterRoot.querySelector(Selectors.filter.fields.join).value, 10);\n }\n\n /**\n * Get the list of raw values for this filter type.\n *\n * @returns {Array}\n */\n get rawValues() {\n const filterValueNode = this.getFilterValueNode();\n const filterValueSelect = filterValueNode.querySelector('select');\n\n return Object.values(getOptionsForSelect(filterValueSelect)).map(option => option.value);\n }\n\n /**\n * Get the list of values for this filter type.\n *\n * @returns {Array}\n */\n get values() {\n return this.rawValues.map(option => parseInt(option, 10));\n }\n\n /**\n * Get the composed value for this filter.\n *\n * @returns {Object}\n */\n get filterValue() {\n return {\n name: this.name,\n jointype: this.jointype,\n values: this.values,\n };\n }\n}\n"],"names":["constructor","filterType","rootNode","initialValues","addValueSelector","tearDown","placeholder","showSuggestions","filterValueNode","this","getFilterValueNode","sourceDataNode","getSourceDataForFilter","innerHTML","outerHTML","dataSource","querySelector","id","getAttribute","filterValueLabel","document","createElement","setAttribute","classList","add","innerText","appendChild","forEach","filterValue","selectedOption","selected","value","append","enhance","dataset","allowCustom","multiple","items","layout","selection","filterRoot","Selectors","filter","byName","filterset","regions","datasource","data","fields","values","name","jointype","parseInt","join","rawValues","filterValueSelect","Object","select","querySelectorAll","map","option"],"mappings":";;;;;;;6NA+CIA,YAAYC,WAAYC,SAAUC,oBACzBF,WAAaA,gBACbC,SAAWA,cAEXE,iBAAiBD,eAM1BE,YASIC,yBACO,mBAAU,0BAA2B,QAQ5CC,6BACO,+BAQYJ,qEAAgB,SAC7BK,gBAAkBC,KAAKC,qBAGvBC,eAAiBF,KAAKG,6BACvBD,sBAGLH,gBAAgBK,UAAYF,eAAeG,gBAErCC,WAAaP,gBAAgBQ,cAAc,UAGjDD,WAAWE,GAAK,gBAAkBF,WAAWG,aAAa,yBAGpDC,iBAAmBC,SAASC,cAAc,SAChDF,iBAAiBG,aAAa,MAAOP,WAAWE,IAChDE,iBAAiBI,UAAUC,IAAI,WAC/BL,iBAAiBM,UAAYV,WAAWG,aAAa,oBAGrDV,gBAAgBkB,YAAYP,kBAG5BhB,cAAcwB,SAAQC,kBACdC,eAAiBd,WAAWC,sCAA+BY,mBAC3DC,eACAA,eAAeC,UAAW,EAClBrB,KAAKF,kBACbsB,eAAiBT,SAASC,cAAc,UACxCQ,eAAeE,MAAQH,YACvBC,eAAehB,UAAYe,YAC3BC,eAAeC,UAAW,EAE1Bf,WAAWiB,OAAOH,8CAIbI,QAETlB,WAGkC,KAAlCA,WAAWmB,QAAQC,YAGnB,WAGM1B,KAAKH,aAGX,EAGAG,KAAKF,gBAGL,MAGCQ,WAAWqB,SAGZ,CACIC,MAAO,+CACPC,OAAQ,sCACRC,UAAW,2CAUnBC,wBACO/B,KAAKP,SAASc,cAAcyB,mBAAUC,OAAOC,OAAOlC,KAAKR,aAQpEW,gCAC2BH,KAAKP,SAASc,cAAcyB,mBAAUG,UAAUC,QAAQC,YAEzD9B,cAAcyB,mBAAUM,KAAKC,OAAOL,OAAOlC,KAAKR,aAQ1ES,4BACWD,KAAK+B,WAAWxB,cAAcyB,mBAAUC,OAAOG,QAAQI,QAQ9DC,kBACOzC,KAAKR,WAQZkD,sBACOC,SAAS3C,KAAK+B,WAAWxB,cAAcyB,mBAAUC,OAAOM,OAAOK,MAAMtB,MAAO,IAQnFuB,sBAEMC,kBADkB9C,KAAKC,qBACaM,cAAc,iBAEjDwC,OAAOP,QAnLMQ,OAmLqBF,kBAlLtCE,OAAOC,iBAAiB,cAkLkCC,KAAIC,QAAUA,OAAO7B,QAnL9D0B,IAAAA,OA2LpBR,oBACOxC,KAAK6C,UAAUK,KAAIC,QAAUR,SAASQ,OAAQ,MAQrDhC,wBACO,CACHsB,KAAMzC,KAAKyC,KACXC,SAAU1C,KAAK0C,SACfF,OAAQxC,KAAKwC"} \ No newline at end of file +{"version":3,"file":"filtertype.min.js","sources":["../../src/datafilter/filtertype.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 * Base Filter class for a filter type in the filter UI.\n *\n * @module core/datafilter/filtertype\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Autocomplete from 'core/form-autocomplete';\nimport Selectors from 'core/datafilter/selectors';\nimport {getString} from 'core/str';\n\n/**\n * Fetch all checked options in the select.\n *\n * This is a poor-man's polyfill for select.selectedOptions, which is not available in IE11.\n *\n * @param {HTMLSelectElement} select\n * @returns {HTMLOptionElement[]} All selected options\n */\nconst getOptionsForSelect = select => {\n return select.querySelectorAll(':checked');\n};\n\nexport default class {\n\n /**\n * Constructor for a new filter.\n *\n * @param {String} filterType The type of filter that this relates to\n * @param {HTMLElement} rootNode The root node for the participants filterset\n * @param {Array} initialValues The initial values for the selector\n */\n constructor(filterType, rootNode, initialValues) {\n this.filterType = filterType;\n this.rootNode = rootNode;\n\n this.addValueSelector(initialValues);\n }\n\n /**\n * Perform any tear-down for this filter type.\n */\n tearDown() {\n // eslint-disable-line no-empty-function\n }\n\n /**\n * Get the placeholder to use when showing the value selector.\n *\n * @return {Promise} Resolving to a String\n */\n get placeholder() {\n return getString('placeholdertypeorselect', 'core');\n }\n\n /**\n * Whether to show suggestions in the autocomplete.\n *\n * @return {Boolean}\n */\n get showSuggestions() {\n return true;\n }\n\n /**\n * Add the value selector to the filter row.\n *\n * @param {Array} initialValues\n */\n async addValueSelector(initialValues = []) {\n const filterValueNode = this.getFilterValueNode();\n\n // Copy the data in place.\n const sourceDataNode = this.getSourceDataForFilter();\n if (!sourceDataNode) {\n return;\n }\n filterValueNode.innerHTML = sourceDataNode.outerHTML;\n\n const dataSource = filterValueNode.querySelector('select');\n\n // Set an ID for this filter value element.\n dataSource.id = 'filter-value-' + dataSource.getAttribute('data-field-name');\n\n // Create a hidden label for the filter value.\n const filterValueLabel = document.createElement('label');\n filterValueLabel.setAttribute('for', dataSource.id);\n filterValueLabel.classList.add('sr-only');\n filterValueLabel.innerText = dataSource.getAttribute('data-field-title');\n\n // Append this label to the filter value container.\n filterValueNode.appendChild(filterValueLabel);\n\n // If there are any initial values then attempt to apply them.\n initialValues.forEach(filterValue => {\n let selectedOption = dataSource.querySelector(`option[value=\"${filterValue}\"]`);\n if (selectedOption) {\n selectedOption.selected = true;\n } else if (!this.showSuggestions) {\n selectedOption = document.createElement('option');\n selectedOption.value = filterValue;\n selectedOption.innerHTML = filterValue;\n selectedOption.selected = true;\n\n dataSource.append(selectedOption);\n }\n });\n\n Autocomplete.enhance(\n // The source select element.\n dataSource,\n\n // Whether to allow 'tags' (custom entries).\n dataSource.dataset.allowCustom == \"1\",\n\n // We do not require AJAX at all as standard.\n null,\n\n // The string to use as a placeholder.\n await this.placeholder,\n\n // Disable case sensitivity on searches.\n false,\n\n // Show suggestions.\n this.showSuggestions,\n\n // Do not override the 'no suggestions' string.\n null,\n\n // Close the suggestions if this is not a multi-select.\n !dataSource.multiple,\n\n // Template overrides.\n {\n items: 'core/datafilter/autocomplete_selection_items',\n layout: 'core/datafilter/autocomplete_layout',\n selection: 'core/datafilter/autocomplete_selection',\n }\n );\n }\n\n /**\n * Get the root node for this filter.\n *\n * @returns {HTMLElement}\n */\n get filterRoot() {\n return this.rootNode.querySelector(Selectors.filter.byName(this.filterType));\n }\n\n /**\n * Get the possible data for this filter type.\n *\n * @returns {Array}\n */\n getSourceDataForFilter() {\n const filterDataNode = this.rootNode.querySelector(Selectors.filterset.regions.datasource);\n\n return filterDataNode.querySelector(Selectors.data.fields.byName(this.filterType));\n }\n\n /**\n * Get the HTMLElement which contains the value selector.\n *\n * @returns {HTMLElement}\n */\n getFilterValueNode() {\n return this.filterRoot.querySelector(Selectors.filter.regions.values);\n }\n\n /**\n * Get the name of this filter.\n *\n * @returns {String}\n */\n get name() {\n return this.filterType;\n }\n\n /**\n * Get the type of join specified.\n *\n * @returns {Number}\n */\n get jointype() {\n return parseInt(this.filterRoot.querySelector(Selectors.filter.fields.join).value, 10);\n }\n\n /**\n * Get the list of raw values for this filter type.\n *\n * @returns {Array}\n */\n get rawValues() {\n const filterValueNode = this.getFilterValueNode();\n const filterValueSelect = filterValueNode.querySelector('select');\n\n return Object.values(getOptionsForSelect(filterValueSelect)).map(option => option.value);\n }\n\n /**\n * Get the list of values for this filter type.\n *\n * @returns {Array}\n */\n get values() {\n return this.rawValues.map(option => parseInt(option, 10));\n }\n\n /**\n * Get the composed value for this filter.\n *\n * @returns {Object}\n */\n get filterValue() {\n return {\n name: this.name,\n jointype: this.jointype,\n values: this.values,\n };\n }\n}\n"],"names":["constructor","filterType","rootNode","initialValues","addValueSelector","tearDown","placeholder","showSuggestions","filterValueNode","this","getFilterValueNode","sourceDataNode","getSourceDataForFilter","innerHTML","outerHTML","dataSource","querySelector","id","getAttribute","filterValueLabel","document","createElement","setAttribute","classList","add","innerText","appendChild","forEach","filterValue","selectedOption","selected","value","append","enhance","dataset","allowCustom","multiple","items","layout","selection","filterRoot","Selectors","filter","byName","filterset","regions","datasource","data","fields","values","name","jointype","parseInt","join","rawValues","filterValueSelect","Object","select","querySelectorAll","map","option"],"mappings":";;;;;;;6NA+CIA,YAAYC,WAAYC,SAAUC,oBACzBF,WAAaA,gBACbC,SAAWA,cAEXE,iBAAiBD,eAM1BE,YASIC,yBACO,kBAAU,0BAA2B,QAQ5CC,6BACO,+BAQYJ,qEAAgB,SAC7BK,gBAAkBC,KAAKC,qBAGvBC,eAAiBF,KAAKG,6BACvBD,sBAGLH,gBAAgBK,UAAYF,eAAeG,gBAErCC,WAAaP,gBAAgBQ,cAAc,UAGjDD,WAAWE,GAAK,gBAAkBF,WAAWG,aAAa,yBAGpDC,iBAAmBC,SAASC,cAAc,SAChDF,iBAAiBG,aAAa,MAAOP,WAAWE,IAChDE,iBAAiBI,UAAUC,IAAI,WAC/BL,iBAAiBM,UAAYV,WAAWG,aAAa,oBAGrDV,gBAAgBkB,YAAYP,kBAG5BhB,cAAcwB,SAAQC,kBACdC,eAAiBd,WAAWC,sCAA+BY,mBAC3DC,eACAA,eAAeC,UAAW,EAClBrB,KAAKF,kBACbsB,eAAiBT,SAASC,cAAc,UACxCQ,eAAeE,MAAQH,YACvBC,eAAehB,UAAYe,YAC3BC,eAAeC,UAAW,EAE1Bf,WAAWiB,OAAOH,8CAIbI,QAETlB,WAGkC,KAAlCA,WAAWmB,QAAQC,YAGnB,WAGM1B,KAAKH,aAGX,EAGAG,KAAKF,gBAGL,MAGCQ,WAAWqB,SAGZ,CACIC,MAAO,+CACPC,OAAQ,sCACRC,UAAW,2CAUnBC,wBACO/B,KAAKP,SAASc,cAAcyB,mBAAUC,OAAOC,OAAOlC,KAAKR,aAQpEW,gCAC2BH,KAAKP,SAASc,cAAcyB,mBAAUG,UAAUC,QAAQC,YAEzD9B,cAAcyB,mBAAUM,KAAKC,OAAOL,OAAOlC,KAAKR,aAQ1ES,4BACWD,KAAK+B,WAAWxB,cAAcyB,mBAAUC,OAAOG,QAAQI,QAQ9DC,kBACOzC,KAAKR,WAQZkD,sBACOC,SAAS3C,KAAK+B,WAAWxB,cAAcyB,mBAAUC,OAAOM,OAAOK,MAAMtB,MAAO,IAQnFuB,sBAEMC,kBADkB9C,KAAKC,qBACaM,cAAc,iBAEjDwC,OAAOP,QAnLMQ,OAmLqBF,kBAlLtCE,OAAOC,iBAAiB,cAkLkCC,KAAIC,QAAUA,OAAO7B,QAnL9D0B,IAAAA,OA2LpBR,oBACOxC,KAAK6C,UAAUK,KAAIC,QAAUR,SAASQ,OAAQ,MAQrDhC,wBACO,CACHsB,KAAMzC,KAAKyC,KACXC,SAAU1C,KAAK0C,SACfF,OAAQxC,KAAKwC"} \ No newline at end of file diff --git a/lib/amd/build/datafilter/filtertypes/keyword.min.js b/lib/amd/build/datafilter/filtertypes/keyword.min.js index bfc2fb23040..2fa612423c4 100644 --- a/lib/amd/build/datafilter/filtertypes/keyword.min.js +++ b/lib/amd/build/datafilter/filtertypes/keyword.min.js @@ -5,6 +5,6 @@ define("core/datafilter/filtertypes/keyword",["exports","core/datafilter/filtert * @module core/datafilter/filtertypes/keyword * @copyright 2020 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_filtertype=(obj=_filtertype)&&obj.__esModule?obj:{default:obj};class _default extends _filtertype.default{get values(){return this.rawValues}get placeholder(){return(0,_str.get_string)("placeholdertype","core_user")}get showSuggestions(){return!1}}return _exports.default=_default,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_filtertype=(obj=_filtertype)&&obj.__esModule?obj:{default:obj};class _default extends _filtertype.default{get values(){return this.rawValues}get placeholder(){return(0,_str.getString)("placeholdertype","core_user")}get showSuggestions(){return!1}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=keyword.min.js.map \ No newline at end of file diff --git a/lib/amd/build/datafilter/filtertypes/keyword.min.js.map b/lib/amd/build/datafilter/filtertypes/keyword.min.js.map index 14f1d92d58b..010a8400717 100644 --- a/lib/amd/build/datafilter/filtertypes/keyword.min.js.map +++ b/lib/amd/build/datafilter/filtertypes/keyword.min.js.map @@ -1 +1 @@ -{"version":3,"file":"keyword.min.js","sources":["../../../src/datafilter/filtertypes/keyword.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 * Keyword filter.\n *\n * @module core/datafilter/filtertypes/keyword\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Filter from 'core/datafilter/filtertype';\nimport {get_string as getString} from 'core/str';\n\nexport default class extends Filter {\n /**\n * For keywords the final value is an Array of strings.\n *\n * @returns {Object}\n */\n get values() {\n return this.rawValues;\n }\n\n /**\n * Get the placeholder to use when showing the value selector.\n *\n * @return {Promise} Resolving to a String\n */\n get placeholder() {\n return getString('placeholdertype', 'core_user');\n }\n\n /**\n * Whether to show suggestions in the autocomplete.\n *\n * @return {Boolean}\n */\n get showSuggestions() {\n return false;\n }\n}\n"],"names":["Filter","values","this","rawValues","placeholder","showSuggestions"],"mappings":";;;;;;;4KAyB6BA,oBAMrBC,oBACOC,KAAKC,UAQZC,yBACO,mBAAU,kBAAmB,aAQpCC,6BACO"} \ No newline at end of file +{"version":3,"file":"keyword.min.js","sources":["../../../src/datafilter/filtertypes/keyword.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 * Keyword filter.\n *\n * @module core/datafilter/filtertypes/keyword\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Filter from 'core/datafilter/filtertype';\nimport {getString} from 'core/str';\n\nexport default class extends Filter {\n /**\n * For keywords the final value is an Array of strings.\n *\n * @returns {Object}\n */\n get values() {\n return this.rawValues;\n }\n\n /**\n * Get the placeholder to use when showing the value selector.\n *\n * @return {Promise} Resolving to a String\n */\n get placeholder() {\n return getString('placeholdertype', 'core_user');\n }\n\n /**\n * Whether to show suggestions in the autocomplete.\n *\n * @return {Boolean}\n */\n get showSuggestions() {\n return false;\n }\n}\n"],"names":["Filter","values","this","rawValues","placeholder","showSuggestions"],"mappings":";;;;;;;4KAyB6BA,oBAMrBC,oBACOC,KAAKC,UAQZC,yBACO,kBAAU,kBAAmB,aAQpCC,6BACO"} \ No newline at end of file diff --git a/lib/amd/build/dynamic_tabs.min.js b/lib/amd/build/dynamic_tabs.min.js index 89193e99da7..4441d99fbfa 100644 --- a/lib/amd/build/dynamic_tabs.min.js +++ b/lib/amd/build/dynamic_tabs.min.js @@ -5,6 +5,6 @@ define("core/dynamic_tabs",["exports","jquery","core/templates","core/loadingico * @module core/dynamic_tabs * @copyright 2021 David Matamoros based on code from Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const SELECTORS={dynamicTabs:".dynamictabs",activeTab:".dynamictabs .nav-link.active",allActiveTabs:'.dynamictabs .nav-link[data-toggle="tab"]:not(.disabled)',tabContent:".dynamictabs .tab-pane [data-tab-content]",tabToggle:'a[data-toggle="tab"]',tabPane:".dynamictabs .tab-pane",forTabName:tabName=>'.dynamictabs [data-tab-content="'.concat(tabName,'"]'),forTabId:tabName=>'.dynamictabs [data-toggle="tab"][href="#'.concat(tabName,'"]')};_exports.init=()=>{const tabToggle=(0,_jquery.default)(SELECTORS.tabToggle);if(tabToggle.on("click",(event=>{(0,_changechecker.isAnyWatchedFormDirty)()&&(event.preventDefault(),event.stopPropagation(),(0,_str.get_strings)([{key:"changesmade",component:"moodle"},{key:"changesmadereallygoaway",component:"moodle"},{key:"confirm",component:"moodle"}]).then((_ref=>{let[strChangesMade,strChangesMadeReally,strConfirm]=_ref;return _notification.default.confirm(strChangesMade,strChangesMadeReally,strConfirm,null,(()=>{(0,_changechecker.resetAllFormDirtyStates)(),(0,_jquery.default)(event.target).trigger(event.type)}))})).catch(_notification.default.exception))})),tabToggle.on("show.bs.tab",(function(){const previousTabName=getActiveTabName();if(previousTabName){document.querySelector(SELECTORS.forTabName(previousTabName)).textContent=""}})).on("shown.bs.tab",(function(){const tab=(0,_jquery.default)((0,_jquery.default)(this).attr("href"));1===tab.length&&loadTab(tab.attr("id"))})),!openTabFromHash()){const tabs=document.querySelector(SELECTORS.allActiveTabs);if(tabs)openTab(tabs.getAttribute("aria-controls"));else{const tabPane=document.querySelector(SELECTORS.tabPane);tabPane&&(tabPane.classList.add("active","show"),loadTab(tabPane.getAttribute("id")))}}};const getActiveTabName=()=>{const element=document.querySelector(SELECTORS.activeTab);return(null==element?void 0:element.getAttribute("aria-controls"))||null},loadTab=tabName=>{var _ref2,_tabName;tabName=null!==(_ref2=null!==(_tabName=tabName)&&void 0!==_tabName?_tabName:getActiveTabName())&&void 0!==_ref2?_ref2:(()=>{const element=document.querySelector(SELECTORS.tabContent);return(null==element?void 0:element.dataset.tabContent)||null})();const tab=document.querySelector(SELECTORS.forTabName(tabName));if(!tab)return;const pendingPromise=new _pending.default("core/dynamic_tabs:loadTab:"+tabName);(0,_loadingicon.addIconToContainer)(tab).then((()=>{let tabArgs={...tab.dataset};return delete tabArgs.tabClass,delete tabArgs.tabContent,(0,_dynamic_tabs.getContent)(tab.dataset.tabClass,JSON.stringify(tabArgs))})).then((response=>Promise.all([_jquery.default.parseHTML(response.javascript,null,!0).map((node=>node.innerHTML)).join("\n"),_templates.default.renderForPromise(response.template,JSON.parse(response.content))]))).then((_ref3=>{let[responseJs,{html:html,js:js}]=_ref3;return _templates.default.replaceNodeContents(tab,html,js+responseJs)})).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)},openTab=tabName=>{const tab=(tabName=>document.querySelector(SELECTORS.forTabId(tabName)))(tabName);return!!tab&&(loadTab(tabName),tab.classList.add("active"),(tabName=>document.getElementById(tabName))(tabName).classList.add("active","show"),!0)},openTabFromHash=()=>{const hash=document.location.hash;return!!hash.match(/^#\w+$/g)&&openTab(hash.replace(/^#/g,""))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const SELECTORS={dynamicTabs:".dynamictabs",activeTab:".dynamictabs .nav-link.active",allActiveTabs:'.dynamictabs .nav-link[data-toggle="tab"]:not(.disabled)',tabContent:".dynamictabs .tab-pane [data-tab-content]",tabToggle:'a[data-toggle="tab"]',tabPane:".dynamictabs .tab-pane",forTabName:tabName=>'.dynamictabs [data-tab-content="'.concat(tabName,'"]'),forTabId:tabName=>'.dynamictabs [data-toggle="tab"][href="#'.concat(tabName,'"]')};_exports.init=()=>{const tabToggle=(0,_jquery.default)(SELECTORS.tabToggle);if(tabToggle.on("click",(event=>{(0,_changechecker.isAnyWatchedFormDirty)()&&(event.preventDefault(),event.stopPropagation(),(0,_str.getStrings)([{key:"changesmade",component:"moodle"},{key:"changesmadereallygoaway",component:"moodle"},{key:"confirm",component:"moodle"}]).then((_ref=>{let[strChangesMade,strChangesMadeReally,strConfirm]=_ref;return _notification.default.confirm(strChangesMade,strChangesMadeReally,strConfirm,null,(()=>{(0,_changechecker.resetAllFormDirtyStates)(),(0,_jquery.default)(event.target).trigger(event.type)}))})).catch(_notification.default.exception))})),tabToggle.on("show.bs.tab",(function(){const previousTabName=getActiveTabName();if(previousTabName){document.querySelector(SELECTORS.forTabName(previousTabName)).textContent=""}})).on("shown.bs.tab",(function(){const tab=(0,_jquery.default)((0,_jquery.default)(this).attr("href"));1===tab.length&&loadTab(tab.attr("id"))})),!openTabFromHash()){const tabs=document.querySelector(SELECTORS.allActiveTabs);if(tabs)openTab(tabs.getAttribute("aria-controls"));else{const tabPane=document.querySelector(SELECTORS.tabPane);tabPane&&(tabPane.classList.add("active","show"),loadTab(tabPane.getAttribute("id")))}}};const getActiveTabName=()=>{const element=document.querySelector(SELECTORS.activeTab);return(null==element?void 0:element.getAttribute("aria-controls"))||null},loadTab=tabName=>{var _ref2,_tabName;tabName=null!==(_ref2=null!==(_tabName=tabName)&&void 0!==_tabName?_tabName:getActiveTabName())&&void 0!==_ref2?_ref2:(()=>{const element=document.querySelector(SELECTORS.tabContent);return(null==element?void 0:element.dataset.tabContent)||null})();const tab=document.querySelector(SELECTORS.forTabName(tabName));if(!tab)return;const pendingPromise=new _pending.default("core/dynamic_tabs:loadTab:"+tabName);(0,_loadingicon.addIconToContainer)(tab).then((()=>{let tabArgs={...tab.dataset};return delete tabArgs.tabClass,delete tabArgs.tabContent,(0,_dynamic_tabs.getContent)(tab.dataset.tabClass,JSON.stringify(tabArgs))})).then((response=>Promise.all([_jquery.default.parseHTML(response.javascript,null,!0).map((node=>node.innerHTML)).join("\n"),_templates.default.renderForPromise(response.template,JSON.parse(response.content))]))).then((_ref3=>{let[responseJs,{html:html,js:js}]=_ref3;return _templates.default.replaceNodeContents(tab,html,js+responseJs)})).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)},openTab=tabName=>{const tab=(tabName=>document.querySelector(SELECTORS.forTabId(tabName)))(tabName);return!!tab&&(loadTab(tabName),tab.classList.add("active"),(tabName=>document.getElementById(tabName))(tabName).classList.add("active","show"),!0)},openTabFromHash=()=>{const hash=document.location.hash;return!!hash.match(/^#\w+$/g)&&openTab(hash.replace(/^#/g,""))}})); //# sourceMappingURL=dynamic_tabs.min.js.map \ No newline at end of file diff --git a/lib/amd/build/dynamic_tabs.min.js.map b/lib/amd/build/dynamic_tabs.min.js.map index dbbc5952417..26be38f4404 100644 --- a/lib/amd/build/dynamic_tabs.min.js.map +++ b/lib/amd/build/dynamic_tabs.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dynamic_tabs.min.js","sources":["../src/dynamic_tabs.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 * Dynamic Tabs UI element with AJAX loading of tabs content\n *\n * @module core/dynamic_tabs\n * @copyright 2021 David Matamoros based on code from Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Templates from 'core/templates';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {get_strings as getStrings} from 'core/str';\nimport {getContent} from 'core/local/repository/dynamic_tabs';\nimport {isAnyWatchedFormDirty, resetAllFormDirtyStates} from 'core_form/changechecker';\n\nconst SELECTORS = {\n dynamicTabs: '.dynamictabs',\n activeTab: '.dynamictabs .nav-link.active',\n allActiveTabs: '.dynamictabs .nav-link[data-toggle=\"tab\"]:not(.disabled)',\n tabContent: '.dynamictabs .tab-pane [data-tab-content]',\n tabToggle: 'a[data-toggle=\"tab\"]',\n tabPane: '.dynamictabs .tab-pane',\n};\n\nSELECTORS.forTabName = tabName => `.dynamictabs [data-tab-content=\"${tabName}\"]`;\nSELECTORS.forTabId = tabName => `.dynamictabs [data-toggle=\"tab\"][href=\"#${tabName}\"]`;\n\n/**\n * Initialises the tabs view on the page (only one tabs view per page is supported)\n */\nexport const init = () => {\n const tabToggle = $(SELECTORS.tabToggle);\n\n // Listen to click, warn user if they are navigating away with unsaved form changes.\n tabToggle.on('click', (event) => {\n if (!isAnyWatchedFormDirty()) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n\n getStrings([\n {key: 'changesmade', component: 'moodle'},\n {key: 'changesmadereallygoaway', component: 'moodle'},\n {key: 'confirm', component: 'moodle'},\n ]).then(([strChangesMade, strChangesMadeReally, strConfirm]) =>\n // Reset form dirty state on confirmation, re-trigger the event.\n Notification.confirm(strChangesMade, strChangesMadeReally, strConfirm, null, () => {\n resetAllFormDirtyStates();\n $(event.target).trigger(event.type);\n })\n ).catch(Notification.exception);\n });\n\n // This code listens to Bootstrap events 'show.bs.tab' and 'shown.bs.tab' which is triggered using JQuery and\n // can not be converted yet to native events.\n tabToggle\n .on('show.bs.tab', function() {\n // Clean content from previous tab.\n const previousTabName = getActiveTabName();\n if (previousTabName) {\n const previousTab = document.querySelector(SELECTORS.forTabName(previousTabName));\n previousTab.textContent = '';\n }\n })\n .on('shown.bs.tab', function() {\n const tab = $($(this).attr('href'));\n if (tab.length !== 1) {\n return;\n }\n loadTab(tab.attr('id'));\n });\n\n if (!openTabFromHash()) {\n const tabs = document.querySelector(SELECTORS.allActiveTabs);\n if (tabs) {\n openTab(tabs.getAttribute('aria-controls'));\n } else {\n // We may hide tabs if there is only one available, just load the contents of the first tab.\n const tabPane = document.querySelector(SELECTORS.tabPane);\n if (tabPane) {\n tabPane.classList.add('active', 'show');\n loadTab(tabPane.getAttribute('id'));\n }\n }\n }\n};\n\n/**\n * Returns id/name of the currently active tab\n *\n * @return {String|null}\n */\nconst getActiveTabName = () => {\n const element = document.querySelector(SELECTORS.activeTab);\n return element?.getAttribute('aria-controls') || null;\n};\n\n/**\n * Returns the id/name of the first tab\n *\n * @return {String|null}\n */\nconst getFirstTabName = () => {\n const element = document.querySelector(SELECTORS.tabContent);\n return element?.dataset.tabContent || null;\n};\n\n/**\n * Loads contents of a tab using an AJAX request\n *\n * @param {String} tabName\n */\nconst loadTab = (tabName) => {\n // If tabName is not specified find the active tab, or if is not defined, the first available tab.\n tabName = tabName ?? getActiveTabName() ?? getFirstTabName();\n const tab = document.querySelector(SELECTORS.forTabName(tabName));\n if (!tab) {\n return;\n }\n\n const pendingPromise = new Pending('core/dynamic_tabs:loadTab:' + tabName);\n\n addIconToContainer(tab)\n .then(() => {\n let tabArgs = {...tab.dataset};\n delete tabArgs.tabClass;\n delete tabArgs.tabContent;\n return getContent(tab.dataset.tabClass, JSON.stringify(tabArgs));\n })\n .then(response => Promise.all([\n $.parseHTML(response.javascript, null, true).map(node => node.innerHTML).join(\"\\n\"),\n Templates.renderForPromise(response.template, JSON.parse(response.content)),\n ]))\n .then(([responseJs, {html, js}]) => Templates.replaceNodeContents(tab, html, js + responseJs))\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n};\n\n/**\n * Return the tab given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTab = (tabName) => {\n return document.querySelector(SELECTORS.forTabId(tabName));\n};\n\n/**\n * Return the tab pane given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTabPane = (tabName) => {\n return document.getElementById(tabName);\n};\n\n/**\n * Open the tab on page load. If this script loads before theme_boost/tab we need to open tab ourselves\n *\n * @param {String} tabName\n * @return {Boolean}\n */\nconst openTab = (tabName) => {\n const tab = getTab(tabName);\n if (!tab) {\n return false;\n }\n\n loadTab(tabName);\n tab.classList.add('active');\n getTabPane(tabName).classList.add('active', 'show');\n return true;\n};\n\n/**\n * If there is a location hash that is the same as the tab name - open this tab.\n *\n * @return {Boolean}\n */\nconst openTabFromHash = () => {\n const hash = document.location.hash;\n if (hash.match(/^#\\w+$/g)) {\n return openTab(hash.replace(/^#/g, ''));\n }\n\n return false;\n};\n"],"names":["SELECTORS","dynamicTabs","activeTab","allActiveTabs","tabContent","tabToggle","tabPane","tabName","on","event","preventDefault","stopPropagation","key","component","then","_ref","strChangesMade","strChangesMadeReally","strConfirm","Notification","confirm","target","trigger","type","catch","exception","previousTabName","getActiveTabName","document","querySelector","forTabName","textContent","tab","this","attr","length","loadTab","openTabFromHash","tabs","openTab","getAttribute","classList","add","element","dataset","getFirstTabName","pendingPromise","Pending","tabArgs","tabClass","JSON","stringify","response","Promise","all","$","parseHTML","javascript","map","node","innerHTML","join","Templates","renderForPromise","template","parse","content","_ref3","responseJs","html","js","replaceNodeContents","resolve","forTabId","getTab","getElementById","getTabPane","hash","location","match","replace"],"mappings":";;;;;;;4QAgCMA,UAAY,CACdC,YAAa,eACbC,UAAW,gCACXC,cAAe,2DACfC,WAAY,4CACZC,UAAW,uBACXC,QAAS,yBAGbN,WAAuBO,mDAA8CA,cACrEP,SAAqBO,2DAAsDA,6BAKvD,WACVF,WAAY,mBAAEL,UAAUK,cAG9BA,UAAUG,GAAG,SAAUC,SACd,4CAILA,MAAMC,iBACND,MAAME,uCAEK,CACP,CAACC,IAAK,cAAeC,UAAW,UAChC,CAACD,IAAK,0BAA2BC,UAAW,UAC5C,CAACD,IAAK,UAAWC,UAAW,YAC7BC,MAAKC,WAAEC,eAAgBC,qBAAsBC,wBAE5CC,sBAAaC,QAAQJ,eAAgBC,qBAAsBC,WAAY,MAAM,sEAEvET,MAAMY,QAAQC,QAAQb,MAAMc,YAEpCC,MAAML,sBAAaM,eAKzBpB,UACKG,GAAG,eAAe,iBAETkB,gBAAkBC,sBACpBD,gBAAiB,CACGE,SAASC,cAAc7B,UAAU8B,WAAWJ,kBACpDK,YAAc,OAGjCvB,GAAG,gBAAgB,iBACVwB,KAAM,oBAAE,mBAAEC,MAAMC,KAAK,SACR,IAAfF,IAAIG,QAGRC,QAAQJ,IAAIE,KAAK,WAGpBG,kBAAmB,OACdC,KAAOV,SAASC,cAAc7B,UAAUG,kBAC1CmC,KACAC,QAAQD,KAAKE,aAAa,sBACvB,OAEGlC,QAAUsB,SAASC,cAAc7B,UAAUM,SAC7CA,UACAA,QAAQmC,UAAUC,IAAI,SAAU,QAChCN,QAAQ9B,QAAQkC,aAAa,iBAWvCb,iBAAmB,WACfgB,QAAUf,SAASC,cAAc7B,UAAUE,kBAC1CyC,MAAAA,eAAAA,QAASH,aAAa,mBAAoB,MAkB/CJ,QAAW7B,6BAEbA,uCAAUA,qCAAWoB,0CAZD,YACdgB,QAAUf,SAASC,cAAc7B,UAAUI,mBAC1CuC,MAAAA,eAAAA,QAASC,QAAQxC,aAAc,MAUKyC,SACrCb,IAAMJ,SAASC,cAAc7B,UAAU8B,WAAWvB,cACnDyB,iBAICc,eAAiB,IAAIC,iBAAQ,6BAA+BxC,6CAE/CyB,KAClBlB,MAAK,SACEkC,QAAU,IAAIhB,IAAIY,uBACfI,QAAQC,gBACRD,QAAQ5C,YACR,4BAAW4B,IAAIY,QAAQK,SAAUC,KAAKC,UAAUH,aAE1DlC,MAAKsC,UAAYC,QAAQC,IAAI,CAC1BC,gBAAEC,UAAUJ,SAASK,WAAY,MAAM,GAAMC,KAAIC,MAAQA,KAAKC,YAAWC,KAAK,MAC9EC,mBAAUC,iBAAiBX,SAASY,SAAUd,KAAKe,MAAMb,SAASc,cAErEpD,MAAKqD,YAAEC,YAAYC,KAACA,KAADC,GAAOA,kBAASR,mBAAUS,oBAAoBvC,IAAKqC,KAAMC,GAAKF,eACjFtD,MAAK,IAAMgC,eAAe0B,YAC1BhD,MAAML,sBAAaM,YA6BlBc,QAAWhC,gBACPyB,IArBMzB,CAAAA,SACLqB,SAASC,cAAc7B,UAAUyE,SAASlE,UAoBrCmE,CAAOnE,iBACdyB,MAILI,QAAQ7B,SACRyB,IAAIS,UAAUC,IAAI,UAjBFnC,CAAAA,SACTqB,SAAS+C,eAAepE,SAiB/BqE,CAAWrE,SAASkC,UAAUC,IAAI,SAAU,SACrC,IAQLL,gBAAkB,WACdwC,KAAOjD,SAASkD,SAASD,aAC3BA,KAAKE,MAAM,YACJxC,QAAQsC,KAAKG,QAAQ,MAAO"} \ No newline at end of file +{"version":3,"file":"dynamic_tabs.min.js","sources":["../src/dynamic_tabs.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 * Dynamic Tabs UI element with AJAX loading of tabs content\n *\n * @module core/dynamic_tabs\n * @copyright 2021 David Matamoros based on code from Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Templates from 'core/templates';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {getStrings} from 'core/str';\nimport {getContent} from 'core/local/repository/dynamic_tabs';\nimport {isAnyWatchedFormDirty, resetAllFormDirtyStates} from 'core_form/changechecker';\n\nconst SELECTORS = {\n dynamicTabs: '.dynamictabs',\n activeTab: '.dynamictabs .nav-link.active',\n allActiveTabs: '.dynamictabs .nav-link[data-toggle=\"tab\"]:not(.disabled)',\n tabContent: '.dynamictabs .tab-pane [data-tab-content]',\n tabToggle: 'a[data-toggle=\"tab\"]',\n tabPane: '.dynamictabs .tab-pane',\n};\n\nSELECTORS.forTabName = tabName => `.dynamictabs [data-tab-content=\"${tabName}\"]`;\nSELECTORS.forTabId = tabName => `.dynamictabs [data-toggle=\"tab\"][href=\"#${tabName}\"]`;\n\n/**\n * Initialises the tabs view on the page (only one tabs view per page is supported)\n */\nexport const init = () => {\n const tabToggle = $(SELECTORS.tabToggle);\n\n // Listen to click, warn user if they are navigating away with unsaved form changes.\n tabToggle.on('click', (event) => {\n if (!isAnyWatchedFormDirty()) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n\n getStrings([\n {key: 'changesmade', component: 'moodle'},\n {key: 'changesmadereallygoaway', component: 'moodle'},\n {key: 'confirm', component: 'moodle'},\n ]).then(([strChangesMade, strChangesMadeReally, strConfirm]) =>\n // Reset form dirty state on confirmation, re-trigger the event.\n Notification.confirm(strChangesMade, strChangesMadeReally, strConfirm, null, () => {\n resetAllFormDirtyStates();\n $(event.target).trigger(event.type);\n })\n ).catch(Notification.exception);\n });\n\n // This code listens to Bootstrap events 'show.bs.tab' and 'shown.bs.tab' which is triggered using JQuery and\n // can not be converted yet to native events.\n tabToggle\n .on('show.bs.tab', function() {\n // Clean content from previous tab.\n const previousTabName = getActiveTabName();\n if (previousTabName) {\n const previousTab = document.querySelector(SELECTORS.forTabName(previousTabName));\n previousTab.textContent = '';\n }\n })\n .on('shown.bs.tab', function() {\n const tab = $($(this).attr('href'));\n if (tab.length !== 1) {\n return;\n }\n loadTab(tab.attr('id'));\n });\n\n if (!openTabFromHash()) {\n const tabs = document.querySelector(SELECTORS.allActiveTabs);\n if (tabs) {\n openTab(tabs.getAttribute('aria-controls'));\n } else {\n // We may hide tabs if there is only one available, just load the contents of the first tab.\n const tabPane = document.querySelector(SELECTORS.tabPane);\n if (tabPane) {\n tabPane.classList.add('active', 'show');\n loadTab(tabPane.getAttribute('id'));\n }\n }\n }\n};\n\n/**\n * Returns id/name of the currently active tab\n *\n * @return {String|null}\n */\nconst getActiveTabName = () => {\n const element = document.querySelector(SELECTORS.activeTab);\n return element?.getAttribute('aria-controls') || null;\n};\n\n/**\n * Returns the id/name of the first tab\n *\n * @return {String|null}\n */\nconst getFirstTabName = () => {\n const element = document.querySelector(SELECTORS.tabContent);\n return element?.dataset.tabContent || null;\n};\n\n/**\n * Loads contents of a tab using an AJAX request\n *\n * @param {String} tabName\n */\nconst loadTab = (tabName) => {\n // If tabName is not specified find the active tab, or if is not defined, the first available tab.\n tabName = tabName ?? getActiveTabName() ?? getFirstTabName();\n const tab = document.querySelector(SELECTORS.forTabName(tabName));\n if (!tab) {\n return;\n }\n\n const pendingPromise = new Pending('core/dynamic_tabs:loadTab:' + tabName);\n\n addIconToContainer(tab)\n .then(() => {\n let tabArgs = {...tab.dataset};\n delete tabArgs.tabClass;\n delete tabArgs.tabContent;\n return getContent(tab.dataset.tabClass, JSON.stringify(tabArgs));\n })\n .then(response => Promise.all([\n $.parseHTML(response.javascript, null, true).map(node => node.innerHTML).join(\"\\n\"),\n Templates.renderForPromise(response.template, JSON.parse(response.content)),\n ]))\n .then(([responseJs, {html, js}]) => Templates.replaceNodeContents(tab, html, js + responseJs))\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n};\n\n/**\n * Return the tab given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTab = (tabName) => {\n return document.querySelector(SELECTORS.forTabId(tabName));\n};\n\n/**\n * Return the tab pane given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTabPane = (tabName) => {\n return document.getElementById(tabName);\n};\n\n/**\n * Open the tab on page load. If this script loads before theme_boost/tab we need to open tab ourselves\n *\n * @param {String} tabName\n * @return {Boolean}\n */\nconst openTab = (tabName) => {\n const tab = getTab(tabName);\n if (!tab) {\n return false;\n }\n\n loadTab(tabName);\n tab.classList.add('active');\n getTabPane(tabName).classList.add('active', 'show');\n return true;\n};\n\n/**\n * If there is a location hash that is the same as the tab name - open this tab.\n *\n * @return {Boolean}\n */\nconst openTabFromHash = () => {\n const hash = document.location.hash;\n if (hash.match(/^#\\w+$/g)) {\n return openTab(hash.replace(/^#/g, ''));\n }\n\n return false;\n};\n"],"names":["SELECTORS","dynamicTabs","activeTab","allActiveTabs","tabContent","tabToggle","tabPane","tabName","on","event","preventDefault","stopPropagation","key","component","then","_ref","strChangesMade","strChangesMadeReally","strConfirm","Notification","confirm","target","trigger","type","catch","exception","previousTabName","getActiveTabName","document","querySelector","forTabName","textContent","tab","this","attr","length","loadTab","openTabFromHash","tabs","openTab","getAttribute","classList","add","element","dataset","getFirstTabName","pendingPromise","Pending","tabArgs","tabClass","JSON","stringify","response","Promise","all","$","parseHTML","javascript","map","node","innerHTML","join","Templates","renderForPromise","template","parse","content","_ref3","responseJs","html","js","replaceNodeContents","resolve","forTabId","getTab","getElementById","getTabPane","hash","location","match","replace"],"mappings":";;;;;;;4QAgCMA,UAAY,CACdC,YAAa,eACbC,UAAW,gCACXC,cAAe,2DACfC,WAAY,4CACZC,UAAW,uBACXC,QAAS,yBAGbN,WAAuBO,mDAA8CA,cACrEP,SAAqBO,2DAAsDA,6BAKvD,WACVF,WAAY,mBAAEL,UAAUK,cAG9BA,UAAUG,GAAG,SAAUC,SACd,4CAILA,MAAMC,iBACND,MAAME,sCAEK,CACP,CAACC,IAAK,cAAeC,UAAW,UAChC,CAACD,IAAK,0BAA2BC,UAAW,UAC5C,CAACD,IAAK,UAAWC,UAAW,YAC7BC,MAAKC,WAAEC,eAAgBC,qBAAsBC,wBAE5CC,sBAAaC,QAAQJ,eAAgBC,qBAAsBC,WAAY,MAAM,sEAEvET,MAAMY,QAAQC,QAAQb,MAAMc,YAEpCC,MAAML,sBAAaM,eAKzBpB,UACKG,GAAG,eAAe,iBAETkB,gBAAkBC,sBACpBD,gBAAiB,CACGE,SAASC,cAAc7B,UAAU8B,WAAWJ,kBACpDK,YAAc,OAGjCvB,GAAG,gBAAgB,iBACVwB,KAAM,oBAAE,mBAAEC,MAAMC,KAAK,SACR,IAAfF,IAAIG,QAGRC,QAAQJ,IAAIE,KAAK,WAGpBG,kBAAmB,OACdC,KAAOV,SAASC,cAAc7B,UAAUG,kBAC1CmC,KACAC,QAAQD,KAAKE,aAAa,sBACvB,OAEGlC,QAAUsB,SAASC,cAAc7B,UAAUM,SAC7CA,UACAA,QAAQmC,UAAUC,IAAI,SAAU,QAChCN,QAAQ9B,QAAQkC,aAAa,iBAWvCb,iBAAmB,WACfgB,QAAUf,SAASC,cAAc7B,UAAUE,kBAC1CyC,MAAAA,eAAAA,QAASH,aAAa,mBAAoB,MAkB/CJ,QAAW7B,6BAEbA,uCAAUA,qCAAWoB,0CAZD,YACdgB,QAAUf,SAASC,cAAc7B,UAAUI,mBAC1CuC,MAAAA,eAAAA,QAASC,QAAQxC,aAAc,MAUKyC,SACrCb,IAAMJ,SAASC,cAAc7B,UAAU8B,WAAWvB,cACnDyB,iBAICc,eAAiB,IAAIC,iBAAQ,6BAA+BxC,6CAE/CyB,KAClBlB,MAAK,SACEkC,QAAU,IAAIhB,IAAIY,uBACfI,QAAQC,gBACRD,QAAQ5C,YACR,4BAAW4B,IAAIY,QAAQK,SAAUC,KAAKC,UAAUH,aAE1DlC,MAAKsC,UAAYC,QAAQC,IAAI,CAC1BC,gBAAEC,UAAUJ,SAASK,WAAY,MAAM,GAAMC,KAAIC,MAAQA,KAAKC,YAAWC,KAAK,MAC9EC,mBAAUC,iBAAiBX,SAASY,SAAUd,KAAKe,MAAMb,SAASc,cAErEpD,MAAKqD,YAAEC,YAAYC,KAACA,KAADC,GAAOA,kBAASR,mBAAUS,oBAAoBvC,IAAKqC,KAAMC,GAAKF,eACjFtD,MAAK,IAAMgC,eAAe0B,YAC1BhD,MAAML,sBAAaM,YA6BlBc,QAAWhC,gBACPyB,IArBMzB,CAAAA,SACLqB,SAASC,cAAc7B,UAAUyE,SAASlE,UAoBrCmE,CAAOnE,iBACdyB,MAILI,QAAQ7B,SACRyB,IAAIS,UAAUC,IAAI,UAjBFnC,CAAAA,SACTqB,SAAS+C,eAAepE,SAiB/BqE,CAAWrE,SAASkC,UAAUC,IAAI,SAAU,SACrC,IAQLL,gBAAkB,WACdwC,KAAOjD,SAASkD,SAASD,aAC3BA,KAAKE,MAAM,YACJxC,QAAQsC,KAAKG,QAAQ,MAAO"} \ No newline at end of file diff --git a/lib/amd/build/emoji/picker.min.js b/lib/amd/build/emoji/picker.min.js index 56b02784e89..2409d6090be 100644 --- a/lib/amd/build/emoji/picker.min.js +++ b/lib/amd/build/emoji/picker.min.js @@ -5,6 +5,6 @@ define("core/emoji/picker",["exports","core/localstorage","core/emoji/data","cor * @module core/emoji/picker * @copyright 2019 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_localstorage=(obj=_localstorage)&&obj.__esModule?obj:{default:obj},EmojiData=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(EmojiData);const ROW_TYPE_EMOJI=0,ROW_TYPE_HEADER=1,SELECTORS_CATEGORY_SELECTOR='[data-action="show-category"]',SELECTORS_EMOJIS_CONTAINER='[data-region="emojis-container"]',SELECTORS_EMOJI_PREVIEW='[data-region="emoji-preview"]',SELECTORS_EMOJI_SHORT_NAME='[data-region="emoji-short-name"]',SELECTORS_ROW_CONTAINER='[data-region="row-container"]',SELECTORS_SEARCH_INPUT='[data-region="search-input"]',SELECTORS_SEARCH_RESULTS_CONTAINER='[data-region="search-results-container"]',createRowDataForCategory=(categoryName,categoryDisplayName,emojis,totalRowCount)=>{const rowData=[];rowData.push({index:totalRowCount+rowData.length,type:ROW_TYPE_HEADER,data:{name:categoryName,displayName:categoryDisplayName}});for(let i=0;irowData.map(((data,index)=>({...data,index:index}))),getCategoryScrollPositionsFromRowData=rowData=>rowData.reduce(((carry,row,index)=>(row.type===ROW_TYPE_HEADER&&(carry[row.data.name]=40*index),carry)),{}),isEmojiElement=element=>null!==element.getAttribute("data-short-names"),findCategorySelectorFromElement=element=>element?"show-category"===element.getAttribute("data-action")?element:findCategorySelectorFromElement(element.parentElement):null,getCategorySelectorByCategoryName=(root,name)=>root.querySelector('[data-category="'.concat(name,'"]')),getCategoryByScrollPosition=(root,position,categoryScrollPositions)=>{let positions=[];position<0&&(position=0);for(const categoryName in categoryScrollPositions){const categoryPosition=categoryScrollPositions[categoryName];positions.push([categoryPosition,categoryName])}positions.sort(((_ref,_ref2)=>{let[a]=_ref,[b]=_ref2;return ab?1:0}));const{categoryName:categoryName,previousPosition:previousPosition,nextPosition:nextPosition}=positions.reduce(((carry,candidate)=>{const[categoryPosition,categoryName]=candidate;return categoryPosition<=position?(carry.categoryName=categoryName,carry.previousPosition=carry.currentPosition,carry.currentPosition=position):null===carry.nextPosition&&(carry.nextPosition=categoryPosition),carry}),{categoryName:null,currentPosition:null,previousPosition:null,nextPosition:null});return[getCategorySelectorByCategoryName(root,categoryName),previousPosition,nextPosition]},getRecentEmojis=()=>{const storedData=_localstorage.default.get("moodle-recent-emojis");return storedData?JSON.parse(storedData):[]},addRecentEmoji=(rowData,recentEmojiRowCount,newEmoji)=>{const categoryName=rowData[0].data.name,categoryDisplayName=rowData[0].data.displayName,recentEmojis=getRecentEmojis();let newRecentEmojis=[newEmoji,...recentEmojis.filter((emoji=>emoji.unified!=newEmoji.unified))];newRecentEmojis=newRecentEmojis.slice(0,21);const newRecentEmojiRowData=createRowDataForCategory(categoryName,categoryDisplayName,newRecentEmojis);return(recentEmojis=>{_localstorage.default.set("moodle-recent-emojis",JSON.stringify(recentEmojis))})(newRecentEmojis),[addIndexesToRowData(newRecentEmojiRowData.concat(rowData.slice(recentEmojiRowCount))),newRecentEmojiRowData.length]},getRowsToRender=(scrollPosition,visibleRowCount,rowData)=>{const minVisibleRow=scrollPosition>40?Math.floor(scrollPosition/40):0,start=minVisibleRow>=5?minVisibleRow-5:minVisibleRow,end=minVisibleRow+visibleRowCount+5;return rowData.slice(start,end)},createRowElement=async rowData=>{let row=null;return row=rowData.type===ROW_TYPE_HEADER?await(async(rowIndex,name)=>{const context={index:rowIndex,text:name},html=await(0,_templates.render)("core/emoji/header_row",context),temp=document.createElement("div");return temp.innerHTML=html,temp.firstChild})(rowData.index,rowData.data.displayName):await(async(rowIndex,emojis)=>{const context={index:rowIndex,emojis:emojis.map((emojiData=>{const charCodes=emojiData.unified.split("-").map((code=>"0x".concat(code))),emojiText=String.fromCodePoint.apply(null,charCodes);return{shortnames:":".concat(emojiData.shortnames.join(": :"),":"),unified:emojiData.unified,text:emojiText,spacer:!1}})),spacers:Array(7-emojis.length).fill(!0)},html=await(0,_templates.render)("core/emoji/emoji_row",context),temp=document.createElement("div");return temp.innerHTML=html,temp.firstChild})(rowData.index,rowData.data),row.style.position="absolute",row.style.left=0,row.style.right=0,row.style.top="".concat(40*rowData.index,"px"),row},doRowsMatch=(a,b)=>{if(a.index!==b.index)return!1;if(a.type!==b.type)return!1;if(typeof a.data!=typeof b.data)return!1;if(a.type===ROW_TYPE_HEADER)return a.data.name===b.data.name;if(a.data.length!==b.data.length)return!1;for(let i=0;i{let currentRows=[],nextRows=[],rowCount=0,isRendering=!1;const renderNextRows=async()=>{if(!nextRows.length)return;if(isRendering)return;isRendering=!0;const nextRowsToRender=nextRows.slice();nextRows=[],await(async(rowContainer,currentRows,nextRows)=>{const toAdd=nextRows.filter((nextRow=>!currentRows.some((currentRow=>doRowsMatch(currentRow,nextRow)))));let toKeep=currentRows.filter((currentRow=>nextRows.some((nextRow=>doRowsMatch(currentRow,nextRow)))));const toRemoveElements=currentRows.filter((currentRow=>!nextRows.some((nextRow=>doRowsMatch(currentRow,nextRow))))).map((rowData=>rowContainer.querySelectorAll('[data-row="'.concat(rowData.index,'"]'))));(await Promise.all(toAdd.map((rowData=>createRowElement(rowData))))).forEach(((row,index)=>{const rowData=toAdd[index];let nextRowIndex=null;for(let i=0;irowData.index){nextRowIndex=i;break}if(null!==nextRowIndex){const nextRowData=toKeep[nextRowIndex],nextRowNode=rowContainer.querySelector('[data-row="'.concat(nextRowData.index,'"]'));rowContainer.insertBefore(row,nextRowNode),toKeep.splice(nextRowIndex,0,toKeep)}else toKeep.push(rowData),rowContainer.appendChild(row)})),toRemoveElements.forEach((rows=>{for(let i=0;i2&&void 0!==arguments[2]?arguments[2]:10;nextRows=getRowsToRender(scrollPosition,rowLimit,rowData),renderNextRows(),rowCount!==rowData.length&&(rowContainer.style.height="".concat(40*rowData.length,"px")),rowCount=rowData.length}},clearSearch=(emojiContainer,searchResultsContainer,searchInput)=>{searchResultsContainer.classList.add("hidden"),emojiContainer.classList.remove("hidden"),searchInput.value=""},getHandleMouseEnter=(emojiPreview,emojiShortName)=>e=>{const target=e.target;isEmojiElement(target)&&(emojiShortName.textContent=target.getAttribute("data-short-names"),emojiPreview.textContent=target.textContent)},getHandleMouseLeave=(emojiPreview,emojiShortName)=>e=>{const target=e.target;isEmojiElement(target)&&(emojiShortName.textContent="",emojiPreview.textContent="")},getHandleScroll=(root,currentVisibleRowScrollPosition,emojiContainer,initialCategoryScrollPositions,renderAtPosition)=>{let[currentCategoryElement,previousCategoryPosition,nextCategoryPosition]=getCategoryByScrollPosition(root,emojiContainer.scrollTop,initialCategoryScrollPositions);return(categoryScrollPositions,rowData)=>{const newScrollPosition=emojiContainer.scrollTop,updateRenderRows=newScrollPositioncurrentVisibleRowScrollPosition+40;(newScrollPosition>=nextCategoryPosition||newScrollPosition{const allCategorySelectors=root.querySelectorAll(SELECTORS_CATEGORY_SELECTOR);for(let i=0;i{renderAtPosition(newScrollPosition,rowData),currentVisibleRowScrollPosition=newScrollPosition}))}},registerEventListeners=(root,emojiContainer,renderAtPosition,currentVisibleRowScrollPosition,selectCallback,categoryScrollPositions,rowData,recentEmojiRowCount)=>{const searchInput=root.querySelector(SELECTORS_SEARCH_INPUT),searchResultsContainer=root.querySelector(SELECTORS_SEARCH_RESULTS_CONTAINER),emojiPreview=root.querySelector(SELECTORS_EMOJI_PREVIEW),emojiShortName=root.querySelector(SELECTORS_EMOJI_SHORT_NAME),clickHandler=((recentEmojiRowCount,emojiContainer,searchResultsContainer,searchInput,selectCallback,renderAtPosition)=>(e,rowData,categoryScrollPositions)=>{const target=e.target;let newRowData=rowData,newCategoryScrollPositions=categoryScrollPositions;if(clearSearch(emojiContainer,searchResultsContainer,searchInput),isEmojiElement(target)){const emojiData={unified:target.getAttribute("data-unified"),shortnames:target.getAttribute("data-short-names").replace(/:/g,"").split(" ")},currentScrollTop=emojiContainer.scrollTop,isRecentEmojiRowVisible=null!==emojiContainer.querySelector('[data-row="'.concat(recentEmojiRowCount-1,'"]'));return[newRowData,recentEmojiRowCount]=addRecentEmoji(rowData,recentEmojiRowCount,emojiData),newCategoryScrollPositions=getCategoryScrollPositionsFromRowData(newRowData),isRecentEmojiRowVisible&&renderAtPosition(currentScrollTop,newRowData),selectCallback(target.textContent),[newRowData,newCategoryScrollPositions]}const categorySelector=findCategorySelectorFromElement(target);if(categorySelector){const position=categoryScrollPositions[categorySelector.getAttribute("data-category")];emojiContainer.scrollTop=position}return[newRowData,newCategoryScrollPositions]})(recentEmojiRowCount,emojiContainer,searchResultsContainer,searchInput,selectCallback,renderAtPosition),scrollHandler=getHandleScroll(root,currentVisibleRowScrollPosition,emojiContainer,categoryScrollPositions,renderAtPosition),searchHandler=((searchInput,searchResultsContainer,emojiContainer)=>{const rowContainer=searchResultsContainer.querySelector(SELECTORS_ROW_CONTAINER),renderSearchResultsAtPosition=generateRenderRowsAtPositionFunction(rowContainer);return searchResultsContainer.appendChild(rowContainer),async()=>{const searchTerm=searchInput.value.toLowerCase();if(searchTerm){((emojiContainer,searchResultsContainer)=>{searchResultsContainer.classList.remove("hidden"),emojiContainer.classList.add("hidden")})(emojiContainer,searchResultsContainer);const matchingEmojis=Object.keys(EmojiData.byShortName).reduce(((carry,shortName)=>(shortName.includes(searchTerm)&&carry.push({shortnames:[shortName],unified:EmojiData.byShortName[shortName]}),carry)),[]),searchResultsString=await(0,_str.get_string)("searchresults","core"),rowData=createRowDataForCategory(searchResultsString,searchResultsString,matchingEmojis,0);renderSearchResultsAtPosition(0,rowData,rowData.length)}else clearSearch(emojiContainer,searchResultsContainer,searchInput)}})(searchInput,searchResultsContainer,emojiContainer);root.addEventListener("focus",getHandleMouseEnter(emojiPreview,emojiShortName),!0),root.addEventListener("blur",getHandleMouseLeave(emojiPreview,emojiShortName),!0),root.addEventListener("mouseenter",getHandleMouseEnter(emojiPreview,emojiShortName),!0),root.addEventListener("mouseleave",getHandleMouseLeave(emojiPreview,emojiShortName),!0),root.addEventListener("click",(e=>{[rowData,categoryScrollPositions]=clickHandler(e,rowData,categoryScrollPositions)})),emojiContainer.addEventListener("scroll",(0,_utils.throttle)((()=>scrollHandler(categoryScrollPositions,rowData)),50)),searchInput.addEventListener("input",(0,_utils.debounce)(searchHandler,200))};return _exports.default=(root,selectCallback)=>{const emojiContainer=root.querySelector(SELECTORS_EMOJIS_CONTAINER),rowContainer=emojiContainer.querySelector(SELECTORS_ROW_CONTAINER),allData=[{name:"Recent",emojis:getRecentEmojis()},...EmojiData.byCategory];let rowData=[],recentEmojiRowCount=0;allData.forEach((category=>{const categoryDisplayName=getCategorySelectorByCategoryName(root,category.name).title,categoryRowData=createRowDataForCategory(category.name,categoryDisplayName,category.emojis,rowData.length);"Recent"===category.name&&(recentEmojiRowCount=categoryRowData.length),rowData=rowData.concat(categoryRowData)})),rowData=addIndexesToRowData(rowData);const categoryScrollPositions=getCategoryScrollPositionsFromRowData(rowData),renderAtPosition=generateRenderRowsAtPositionFunction(rowContainer);renderAtPosition(0,rowData),registerEventListeners(root,emojiContainer,renderAtPosition,0,selectCallback,categoryScrollPositions,rowData,recentEmojiRowCount)},_exports.default})); + */function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_localstorage=(obj=_localstorage)&&obj.__esModule?obj:{default:obj},EmojiData=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(EmojiData);const ROW_TYPE_EMOJI=0,ROW_TYPE_HEADER=1,SELECTORS_CATEGORY_SELECTOR='[data-action="show-category"]',SELECTORS_EMOJIS_CONTAINER='[data-region="emojis-container"]',SELECTORS_EMOJI_PREVIEW='[data-region="emoji-preview"]',SELECTORS_EMOJI_SHORT_NAME='[data-region="emoji-short-name"]',SELECTORS_ROW_CONTAINER='[data-region="row-container"]',SELECTORS_SEARCH_INPUT='[data-region="search-input"]',SELECTORS_SEARCH_RESULTS_CONTAINER='[data-region="search-results-container"]',createRowDataForCategory=(categoryName,categoryDisplayName,emojis,totalRowCount)=>{const rowData=[];rowData.push({index:totalRowCount+rowData.length,type:ROW_TYPE_HEADER,data:{name:categoryName,displayName:categoryDisplayName}});for(let i=0;irowData.map(((data,index)=>({...data,index:index}))),getCategoryScrollPositionsFromRowData=rowData=>rowData.reduce(((carry,row,index)=>(row.type===ROW_TYPE_HEADER&&(carry[row.data.name]=40*index),carry)),{}),isEmojiElement=element=>null!==element.getAttribute("data-short-names"),findCategorySelectorFromElement=element=>element?"show-category"===element.getAttribute("data-action")?element:findCategorySelectorFromElement(element.parentElement):null,getCategorySelectorByCategoryName=(root,name)=>root.querySelector('[data-category="'.concat(name,'"]')),getCategoryByScrollPosition=(root,position,categoryScrollPositions)=>{let positions=[];position<0&&(position=0);for(const categoryName in categoryScrollPositions){const categoryPosition=categoryScrollPositions[categoryName];positions.push([categoryPosition,categoryName])}positions.sort(((_ref,_ref2)=>{let[a]=_ref,[b]=_ref2;return ab?1:0}));const{categoryName:categoryName,previousPosition:previousPosition,nextPosition:nextPosition}=positions.reduce(((carry,candidate)=>{const[categoryPosition,categoryName]=candidate;return categoryPosition<=position?(carry.categoryName=categoryName,carry.previousPosition=carry.currentPosition,carry.currentPosition=position):null===carry.nextPosition&&(carry.nextPosition=categoryPosition),carry}),{categoryName:null,currentPosition:null,previousPosition:null,nextPosition:null});return[getCategorySelectorByCategoryName(root,categoryName),previousPosition,nextPosition]},getRecentEmojis=()=>{const storedData=_localstorage.default.get("moodle-recent-emojis");return storedData?JSON.parse(storedData):[]},addRecentEmoji=(rowData,recentEmojiRowCount,newEmoji)=>{const categoryName=rowData[0].data.name,categoryDisplayName=rowData[0].data.displayName,recentEmojis=getRecentEmojis();let newRecentEmojis=[newEmoji,...recentEmojis.filter((emoji=>emoji.unified!=newEmoji.unified))];newRecentEmojis=newRecentEmojis.slice(0,21);const newRecentEmojiRowData=createRowDataForCategory(categoryName,categoryDisplayName,newRecentEmojis);return(recentEmojis=>{_localstorage.default.set("moodle-recent-emojis",JSON.stringify(recentEmojis))})(newRecentEmojis),[addIndexesToRowData(newRecentEmojiRowData.concat(rowData.slice(recentEmojiRowCount))),newRecentEmojiRowData.length]},getRowsToRender=(scrollPosition,visibleRowCount,rowData)=>{const minVisibleRow=scrollPosition>40?Math.floor(scrollPosition/40):0,start=minVisibleRow>=5?minVisibleRow-5:minVisibleRow,end=minVisibleRow+visibleRowCount+5;return rowData.slice(start,end)},createRowElement=async rowData=>{let row=null;return row=rowData.type===ROW_TYPE_HEADER?await(async(rowIndex,name)=>{const context={index:rowIndex,text:name},html=await(0,_templates.render)("core/emoji/header_row",context),temp=document.createElement("div");return temp.innerHTML=html,temp.firstChild})(rowData.index,rowData.data.displayName):await(async(rowIndex,emojis)=>{const context={index:rowIndex,emojis:emojis.map((emojiData=>{const charCodes=emojiData.unified.split("-").map((code=>"0x".concat(code))),emojiText=String.fromCodePoint.apply(null,charCodes);return{shortnames:":".concat(emojiData.shortnames.join(": :"),":"),unified:emojiData.unified,text:emojiText,spacer:!1}})),spacers:Array(7-emojis.length).fill(!0)},html=await(0,_templates.render)("core/emoji/emoji_row",context),temp=document.createElement("div");return temp.innerHTML=html,temp.firstChild})(rowData.index,rowData.data),row.style.position="absolute",row.style.left=0,row.style.right=0,row.style.top="".concat(40*rowData.index,"px"),row},doRowsMatch=(a,b)=>{if(a.index!==b.index)return!1;if(a.type!==b.type)return!1;if(typeof a.data!=typeof b.data)return!1;if(a.type===ROW_TYPE_HEADER)return a.data.name===b.data.name;if(a.data.length!==b.data.length)return!1;for(let i=0;i{let currentRows=[],nextRows=[],rowCount=0,isRendering=!1;const renderNextRows=async()=>{if(!nextRows.length)return;if(isRendering)return;isRendering=!0;const nextRowsToRender=nextRows.slice();nextRows=[],await(async(rowContainer,currentRows,nextRows)=>{const toAdd=nextRows.filter((nextRow=>!currentRows.some((currentRow=>doRowsMatch(currentRow,nextRow)))));let toKeep=currentRows.filter((currentRow=>nextRows.some((nextRow=>doRowsMatch(currentRow,nextRow)))));const toRemoveElements=currentRows.filter((currentRow=>!nextRows.some((nextRow=>doRowsMatch(currentRow,nextRow))))).map((rowData=>rowContainer.querySelectorAll('[data-row="'.concat(rowData.index,'"]'))));(await Promise.all(toAdd.map((rowData=>createRowElement(rowData))))).forEach(((row,index)=>{const rowData=toAdd[index];let nextRowIndex=null;for(let i=0;irowData.index){nextRowIndex=i;break}if(null!==nextRowIndex){const nextRowData=toKeep[nextRowIndex],nextRowNode=rowContainer.querySelector('[data-row="'.concat(nextRowData.index,'"]'));rowContainer.insertBefore(row,nextRowNode),toKeep.splice(nextRowIndex,0,toKeep)}else toKeep.push(rowData),rowContainer.appendChild(row)})),toRemoveElements.forEach((rows=>{for(let i=0;i2&&void 0!==arguments[2]?arguments[2]:10;nextRows=getRowsToRender(scrollPosition,rowLimit,rowData),renderNextRows(),rowCount!==rowData.length&&(rowContainer.style.height="".concat(40*rowData.length,"px")),rowCount=rowData.length}},clearSearch=(emojiContainer,searchResultsContainer,searchInput)=>{searchResultsContainer.classList.add("hidden"),emojiContainer.classList.remove("hidden"),searchInput.value=""},getHandleMouseEnter=(emojiPreview,emojiShortName)=>e=>{const target=e.target;isEmojiElement(target)&&(emojiShortName.textContent=target.getAttribute("data-short-names"),emojiPreview.textContent=target.textContent)},getHandleMouseLeave=(emojiPreview,emojiShortName)=>e=>{const target=e.target;isEmojiElement(target)&&(emojiShortName.textContent="",emojiPreview.textContent="")},getHandleScroll=(root,currentVisibleRowScrollPosition,emojiContainer,initialCategoryScrollPositions,renderAtPosition)=>{let[currentCategoryElement,previousCategoryPosition,nextCategoryPosition]=getCategoryByScrollPosition(root,emojiContainer.scrollTop,initialCategoryScrollPositions);return(categoryScrollPositions,rowData)=>{const newScrollPosition=emojiContainer.scrollTop,updateRenderRows=newScrollPositioncurrentVisibleRowScrollPosition+40;(newScrollPosition>=nextCategoryPosition||newScrollPosition{const allCategorySelectors=root.querySelectorAll(SELECTORS_CATEGORY_SELECTOR);for(let i=0;i{renderAtPosition(newScrollPosition,rowData),currentVisibleRowScrollPosition=newScrollPosition}))}},registerEventListeners=(root,emojiContainer,renderAtPosition,currentVisibleRowScrollPosition,selectCallback,categoryScrollPositions,rowData,recentEmojiRowCount)=>{const searchInput=root.querySelector(SELECTORS_SEARCH_INPUT),searchResultsContainer=root.querySelector(SELECTORS_SEARCH_RESULTS_CONTAINER),emojiPreview=root.querySelector(SELECTORS_EMOJI_PREVIEW),emojiShortName=root.querySelector(SELECTORS_EMOJI_SHORT_NAME),clickHandler=((recentEmojiRowCount,emojiContainer,searchResultsContainer,searchInput,selectCallback,renderAtPosition)=>(e,rowData,categoryScrollPositions)=>{const target=e.target;let newRowData=rowData,newCategoryScrollPositions=categoryScrollPositions;if(clearSearch(emojiContainer,searchResultsContainer,searchInput),isEmojiElement(target)){const emojiData={unified:target.getAttribute("data-unified"),shortnames:target.getAttribute("data-short-names").replace(/:/g,"").split(" ")},currentScrollTop=emojiContainer.scrollTop,isRecentEmojiRowVisible=null!==emojiContainer.querySelector('[data-row="'.concat(recentEmojiRowCount-1,'"]'));return[newRowData,recentEmojiRowCount]=addRecentEmoji(rowData,recentEmojiRowCount,emojiData),newCategoryScrollPositions=getCategoryScrollPositionsFromRowData(newRowData),isRecentEmojiRowVisible&&renderAtPosition(currentScrollTop,newRowData),selectCallback(target.textContent),[newRowData,newCategoryScrollPositions]}const categorySelector=findCategorySelectorFromElement(target);if(categorySelector){const position=categoryScrollPositions[categorySelector.getAttribute("data-category")];emojiContainer.scrollTop=position}return[newRowData,newCategoryScrollPositions]})(recentEmojiRowCount,emojiContainer,searchResultsContainer,searchInput,selectCallback,renderAtPosition),scrollHandler=getHandleScroll(root,currentVisibleRowScrollPosition,emojiContainer,categoryScrollPositions,renderAtPosition),searchHandler=((searchInput,searchResultsContainer,emojiContainer)=>{const rowContainer=searchResultsContainer.querySelector(SELECTORS_ROW_CONTAINER),renderSearchResultsAtPosition=generateRenderRowsAtPositionFunction(rowContainer);return searchResultsContainer.appendChild(rowContainer),async()=>{const searchTerm=searchInput.value.toLowerCase();if(searchTerm){((emojiContainer,searchResultsContainer)=>{searchResultsContainer.classList.remove("hidden"),emojiContainer.classList.add("hidden")})(emojiContainer,searchResultsContainer);const matchingEmojis=Object.keys(EmojiData.byShortName).reduce(((carry,shortName)=>(shortName.includes(searchTerm)&&carry.push({shortnames:[shortName],unified:EmojiData.byShortName[shortName]}),carry)),[]),searchResultsString=await(0,_str.getString)("searchresults","core"),rowData=createRowDataForCategory(searchResultsString,searchResultsString,matchingEmojis,0);renderSearchResultsAtPosition(0,rowData,rowData.length)}else clearSearch(emojiContainer,searchResultsContainer,searchInput)}})(searchInput,searchResultsContainer,emojiContainer);root.addEventListener("focus",getHandleMouseEnter(emojiPreview,emojiShortName),!0),root.addEventListener("blur",getHandleMouseLeave(emojiPreview,emojiShortName),!0),root.addEventListener("mouseenter",getHandleMouseEnter(emojiPreview,emojiShortName),!0),root.addEventListener("mouseleave",getHandleMouseLeave(emojiPreview,emojiShortName),!0),root.addEventListener("click",(e=>{[rowData,categoryScrollPositions]=clickHandler(e,rowData,categoryScrollPositions)})),emojiContainer.addEventListener("scroll",(0,_utils.throttle)((()=>scrollHandler(categoryScrollPositions,rowData)),50)),searchInput.addEventListener("input",(0,_utils.debounce)(searchHandler,200))};return _exports.default=(root,selectCallback)=>{const emojiContainer=root.querySelector(SELECTORS_EMOJIS_CONTAINER),rowContainer=emojiContainer.querySelector(SELECTORS_ROW_CONTAINER),allData=[{name:"Recent",emojis:getRecentEmojis()},...EmojiData.byCategory];let rowData=[],recentEmojiRowCount=0;allData.forEach((category=>{const categoryDisplayName=getCategorySelectorByCategoryName(root,category.name).title,categoryRowData=createRowDataForCategory(category.name,categoryDisplayName,category.emojis,rowData.length);"Recent"===category.name&&(recentEmojiRowCount=categoryRowData.length),rowData=rowData.concat(categoryRowData)})),rowData=addIndexesToRowData(rowData);const categoryScrollPositions=getCategoryScrollPositionsFromRowData(rowData),renderAtPosition=generateRenderRowsAtPositionFunction(rowContainer);renderAtPosition(0,rowData),registerEventListeners(root,emojiContainer,renderAtPosition,0,selectCallback,categoryScrollPositions,rowData,recentEmojiRowCount)},_exports.default})); //# sourceMappingURL=picker.min.js.map \ No newline at end of file diff --git a/lib/amd/build/emoji/picker.min.js.map b/lib/amd/build/emoji/picker.min.js.map index 39ec71df3b8..a0c5dbaa4be 100644 --- a/lib/amd/build/emoji/picker.min.js.map +++ b/lib/amd/build/emoji/picker.min.js.map @@ -1 +1 @@ -{"version":3,"file":"picker.min.js","sources":["../../src/emoji/picker.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 * Emoji picker.\n *\n * @module core/emoji/picker\n * @copyright 2019 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport LocalStorage from 'core/localstorage';\nimport * as EmojiData from 'core/emoji/data';\nimport {throttle, debounce} from 'core/utils';\nimport {get_string as getString} from 'core/str';\nimport {render as renderTemplate} from 'core/templates';\n\nconst VISIBLE_ROW_COUNT = 10;\nconst ROW_RENDER_BUFFER_COUNT = 5;\nconst RECENT_EMOJIS_STORAGE_KEY = 'moodle-recent-emojis';\nconst ROW_HEIGHT_RAW = 40;\nconst EMOJIS_PER_ROW = 7;\nconst MAX_RECENT_COUNT = EMOJIS_PER_ROW * 3;\nconst ROW_TYPE = {\n EMOJI: 0,\n HEADER: 1\n};\nconst SELECTORS = {\n CATEGORY_SELECTOR: '[data-action=\"show-category\"]',\n EMOJIS_CONTAINER: '[data-region=\"emojis-container\"]',\n EMOJI_PREVIEW: '[data-region=\"emoji-preview\"]',\n EMOJI_SHORT_NAME: '[data-region=\"emoji-short-name\"]',\n ROW_CONTAINER: '[data-region=\"row-container\"]',\n SEARCH_INPUT: '[data-region=\"search-input\"]',\n SEARCH_RESULTS_CONTAINER: '[data-region=\"search-results-container\"]'\n};\n\n/**\n * Create the row data for a category.\n *\n * @method\n * @param {String} categoryName The category name\n * @param {String} categoryDisplayName The category display name\n * @param {Array} emojis The emoji data\n * @param {Number} totalRowCount The total number of rows generated so far\n * @return {Array}\n */\nconst createRowDataForCategory = (categoryName, categoryDisplayName, emojis, totalRowCount) => {\n const rowData = [];\n rowData.push({\n index: totalRowCount + rowData.length,\n type: ROW_TYPE.HEADER,\n data: {\n name: categoryName,\n displayName: categoryDisplayName\n }\n });\n\n for (let i = 0; i < emojis.length; i += EMOJIS_PER_ROW) {\n const rowEmojis = emojis.slice(i, i + EMOJIS_PER_ROW);\n rowData.push({\n index: totalRowCount + rowData.length,\n type: ROW_TYPE.EMOJI,\n data: rowEmojis\n });\n }\n\n return rowData;\n};\n\n/**\n * Add each row's index to it's value in the row data.\n *\n * @method\n * @param {Array} rowData List of emoji row data\n * @return {Array}\n */\nconst addIndexesToRowData = (rowData) => {\n return rowData.map((data, index) => {\n return {...data, index};\n });\n};\n\n/**\n * Calculate the scroll position for the beginning of each category from\n * the row data.\n *\n * @method\n * @param {Array} rowData List of emoji row data\n * @return {Object}\n */\nconst getCategoryScrollPositionsFromRowData = (rowData) => {\n return rowData.reduce((carry, row, index) => {\n if (row.type === ROW_TYPE.HEADER) {\n carry[row.data.name] = index * ROW_HEIGHT_RAW;\n }\n return carry;\n }, {});\n};\n\n/**\n * Create a header row element for the category name.\n *\n * @method\n * @param {Number} rowIndex Index of the row in the row data\n * @param {String} name The category display name\n * @return {Element}\n */\nconst createHeaderRow = async(rowIndex, name) => {\n const context = {\n index: rowIndex,\n text: name\n };\n const html = await renderTemplate('core/emoji/header_row', context);\n const temp = document.createElement('div');\n temp.innerHTML = html;\n return temp.firstChild;\n};\n\n/**\n * Create an emoji row element.\n *\n * @method\n * @param {Number} rowIndex Index of the row in the row data\n * @param {Array} emojis The list of emoji data for the row\n * @return {Element}\n */\nconst createEmojiRow = async(rowIndex, emojis) => {\n const context = {\n index: rowIndex,\n emojis: emojis.map(emojiData => {\n const charCodes = emojiData.unified.split('-').map(code => `0x${code}`);\n const emojiText = String.fromCodePoint.apply(null, charCodes);\n return {\n shortnames: `:${emojiData.shortnames.join(': :')}:`,\n unified: emojiData.unified,\n text: emojiText,\n spacer: false\n };\n }),\n spacers: Array(EMOJIS_PER_ROW - emojis.length).fill(true)\n };\n const html = await renderTemplate('core/emoji/emoji_row', context);\n const temp = document.createElement('div');\n temp.innerHTML = html;\n return temp.firstChild;\n};\n\n/**\n * Check if the element is an emoji element.\n *\n * @method\n * @param {Element} element Element to check\n * @return {Bool}\n */\nconst isEmojiElement = element => element.getAttribute('data-short-names') !== null;\n\n/**\n * Search from an element and up through it's ancestors to fine the category\n * selector element and return it.\n *\n * @method\n * @param {Element} element Element to begin searching from\n * @return {Element|null}\n */\nconst findCategorySelectorFromElement = element => {\n if (!element) {\n return null;\n }\n\n if (element.getAttribute('data-action') === 'show-category') {\n return element;\n } else {\n return findCategorySelectorFromElement(element.parentElement);\n }\n};\n\nconst getCategorySelectorByCategoryName = (root, name) => {\n return root.querySelector(`[data-category=\"${name}\"]`);\n};\n\n/**\n * Sets the given category selector element as active.\n *\n * @method\n * @param {Element} root The root picker element\n * @param {Element} element The category selector element to make active\n */\nconst setCategorySelectorActive = (root, element) => {\n const allCategorySelectors = root.querySelectorAll(SELECTORS.CATEGORY_SELECTOR);\n\n for (let i = 0; i < allCategorySelectors.length; i++) {\n const selector = allCategorySelectors[i];\n selector.classList.remove('selected');\n }\n\n element.classList.add('selected');\n};\n\n/**\n * Get the category selector element and the scroll positions for the previous and\n * next categories for the given scroll position.\n *\n * @method\n * @param {Element} root The picker root element\n * @param {Number} position The position to get the category for\n * @param {Object} categoryScrollPositions Set of scroll positions for all categories\n * @return {Array}\n */\nconst getCategoryByScrollPosition = (root, position, categoryScrollPositions) => {\n let positions = [];\n\n if (position < 0) {\n position = 0;\n }\n\n // Get all of the category positions.\n for (const categoryName in categoryScrollPositions) {\n const categoryPosition = categoryScrollPositions[categoryName];\n positions.push([categoryPosition, categoryName]);\n }\n\n // Sort the positions in ascending order.\n positions.sort(([a], [b]) => {\n if (a < b) {\n return -1;\n } else if (a > b) {\n return 1;\n } else {\n return 0;\n }\n });\n\n // Get the current category name as well as the previous and next category\n // positions from the sorted list of positions.\n const {categoryName, previousPosition, nextPosition} = positions.reduce(\n (carry, candidate) => {\n const [categoryPosition, categoryName] = candidate;\n\n if (categoryPosition <= position) {\n carry.categoryName = categoryName;\n carry.previousPosition = carry.currentPosition;\n carry.currentPosition = position;\n } else if (carry.nextPosition === null) {\n carry.nextPosition = categoryPosition;\n }\n\n return carry;\n },\n {\n categoryName: null,\n currentPosition: null,\n previousPosition: null,\n nextPosition: null\n }\n );\n\n return [getCategorySelectorByCategoryName(root, categoryName), previousPosition, nextPosition];\n};\n\n/**\n * Get the list of recent emojis data from local storage.\n *\n * @method\n * @return {Array}\n */\nconst getRecentEmojis = () => {\n const storedData = LocalStorage.get(RECENT_EMOJIS_STORAGE_KEY);\n return storedData ? JSON.parse(storedData) : [];\n};\n\n/**\n * Save the list of recent emojis in local storage.\n *\n * @method\n * @param {Array} recentEmojis List of emoji data to save\n */\nconst saveRecentEmoji = (recentEmojis) => {\n LocalStorage.set(RECENT_EMOJIS_STORAGE_KEY, JSON.stringify(recentEmojis));\n};\n\n/**\n * Add an emoji data to the set of recent emojis. This function will update the row\n * data to ensure that the recent emoji rows are correct and all of the rows are\n * re-indexed.\n *\n * The new set of recent emojis are saved in local storage and the full set of updated\n * row data and new emoji row count are returned.\n *\n * @method\n * @param {Array} rowData The emoji rows data\n * @param {Number} recentEmojiRowCount Count of the recent emoji rows\n * @param {Object} newEmoji The emoji data for the emoji to add to the recent emoji list\n * @return {Array}\n */\nconst addRecentEmoji = (rowData, recentEmojiRowCount, newEmoji) => {\n // The first set of rows is always the recent emojis.\n const categoryName = rowData[0].data.name;\n const categoryDisplayName = rowData[0].data.displayName;\n const recentEmojis = getRecentEmojis();\n // Add the new emoji to the start of the list of recent emojis.\n let newRecentEmojis = [newEmoji, ...recentEmojis.filter(emoji => emoji.unified != newEmoji.unified)];\n // Limit the number of recent emojis.\n newRecentEmojis = newRecentEmojis.slice(0, MAX_RECENT_COUNT);\n const newRecentEmojiRowData = createRowDataForCategory(categoryName, categoryDisplayName, newRecentEmojis);\n\n // Save the new list in local storage.\n saveRecentEmoji(newRecentEmojis);\n\n return [\n // Return the new rowData and re-index it to make sure it's all correct.\n addIndexesToRowData(newRecentEmojiRowData.concat(rowData.slice(recentEmojiRowCount))),\n newRecentEmojiRowData.length\n ];\n};\n\n/**\n * Calculate which rows should be visible based on the given scroll position. Adds a\n * buffer to amount to either side of the total number of requested rows so that\n * scrolling the emoji rows container is smooth.\n *\n * @method\n * @param {Number} scrollPosition Scroll position within the emoji container\n * @param {Number} visibleRowCount How many rows should be visible\n * @param {Array} rowData The emoji rows data\n * @return {Array}\n */\nconst getRowsToRender = (scrollPosition, visibleRowCount, rowData) => {\n const minVisibleRow = scrollPosition > ROW_HEIGHT_RAW ? Math.floor(scrollPosition / ROW_HEIGHT_RAW) : 0;\n const start = minVisibleRow >= ROW_RENDER_BUFFER_COUNT ? minVisibleRow - ROW_RENDER_BUFFER_COUNT : minVisibleRow;\n const end = minVisibleRow + visibleRowCount + ROW_RENDER_BUFFER_COUNT;\n const rows = rowData.slice(start, end);\n return rows;\n};\n\n/**\n * Create a row element from the row data.\n *\n * @method\n * @param {Object} rowData The emoji row data\n * @return {Element}\n */\nconst createRowElement = async(rowData) => {\n let row = null;\n if (rowData.type === ROW_TYPE.HEADER) {\n row = await createHeaderRow(rowData.index, rowData.data.displayName);\n } else {\n row = await createEmojiRow(rowData.index, rowData.data);\n }\n\n row.style.position = 'absolute';\n row.style.left = 0;\n row.style.right = 0;\n row.style.top = `${rowData.index * ROW_HEIGHT_RAW}px`;\n\n return row;\n};\n\n/**\n * Check if the given rows match.\n *\n * @method\n * @param {Object} a The first row\n * @param {Object} b The second row\n * @return {Bool}\n */\nconst doRowsMatch = (a, b) => {\n if (a.index !== b.index) {\n return false;\n }\n\n if (a.type !== b.type) {\n return false;\n }\n\n if (typeof a.data != typeof b.data) {\n return false;\n }\n\n if (a.type === ROW_TYPE.HEADER) {\n return a.data.name === b.data.name;\n } else {\n if (a.data.length !== b.data.length) {\n return false;\n }\n\n for (let i = 0; i < a.data.length; i++) {\n if (a.data[i].unified != b.data[i].unified) {\n return false;\n }\n }\n }\n\n return true;\n};\n\n/**\n * Update the visible rows. Deletes any row elements that should no longer\n * be visible and creates the newly visible row elements. Any rows that haven't\n * changed visibility will be left untouched.\n *\n * @method\n * @param {Element} rowContainer The container element for the emoji rows\n * @param {Array} currentRows List of row data that matches the currently visible rows\n * @param {Array} nextRows List of row data containing the new list of rows to be made visible\n */\nconst renderRows = async(rowContainer, currentRows, nextRows) => {\n // We need to add any rows that are in nextRows but not in currentRows.\n const toAdd = nextRows.filter(nextRow => !currentRows.some(currentRow => doRowsMatch(currentRow, nextRow)));\n // Remember which rows will still be visible so that we can insert our element in the correct place in the DOM.\n let toKeep = currentRows.filter(currentRow => nextRows.some(nextRow => doRowsMatch(currentRow, nextRow)));\n // We need to remove any rows that are in currentRows but not in nextRows.\n const toRemove = currentRows.filter(currentRow => !nextRows.some(nextRow => doRowsMatch(currentRow, nextRow)));\n const toRemoveElements = toRemove.map(rowData => rowContainer.querySelectorAll(`[data-row=\"${rowData.index}\"]`));\n\n // Render all of the templates first.\n const rows = await Promise.all(toAdd.map(rowData => createRowElement(rowData)));\n\n rows.forEach((row, index) => {\n const rowData = toAdd[index];\n let nextRowIndex = null;\n\n for (let i = 0; i < toKeep.length; i++) {\n const candidate = toKeep[i];\n if (candidate.index > rowData.index) {\n nextRowIndex = i;\n break;\n }\n }\n\n // Make sure the elements get added to the DOM in the correct order (ascending by row data index)\n // so that they appear naturally in the tab order.\n if (nextRowIndex !== null) {\n const nextRowData = toKeep[nextRowIndex];\n const nextRowNode = rowContainer.querySelector(`[data-row=\"${nextRowData.index}\"]`);\n\n rowContainer.insertBefore(row, nextRowNode);\n toKeep.splice(nextRowIndex, 0, toKeep);\n } else {\n toKeep.push(rowData);\n rowContainer.appendChild(row);\n }\n });\n\n toRemoveElements.forEach(rows => {\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i];\n rowContainer.removeChild(row);\n }\n });\n};\n\n/**\n * Build a function to render the visible emoji rows for a given scroll\n * position.\n *\n * @method\n * @param {Element} rowContainer The container element for the emoji rows\n * @return {Function}\n */\nconst generateRenderRowsAtPositionFunction = (rowContainer) => {\n let currentRows = [];\n let nextRows = [];\n let rowCount = 0;\n let isRendering = false;\n const renderNextRows = async() => {\n if (!nextRows.length) {\n return;\n }\n\n if (isRendering) {\n return;\n }\n\n isRendering = true;\n const nextRowsToRender = nextRows.slice();\n nextRows = [];\n\n await renderRows(rowContainer, currentRows, nextRowsToRender);\n currentRows = nextRowsToRender;\n isRendering = false;\n renderNextRows();\n };\n\n return (scrollPosition, rowData, rowLimit = VISIBLE_ROW_COUNT) => {\n nextRows = getRowsToRender(scrollPosition, rowLimit, rowData);\n renderNextRows();\n\n if (rowCount !== rowData.length) {\n // Adjust the height of the container to match the number of rows.\n rowContainer.style.height = `${rowData.length * ROW_HEIGHT_RAW}px`;\n }\n\n rowCount = rowData.length;\n };\n};\n\n/**\n * Show the search results container and hide the emoji container.\n *\n * @method\n * @param {Element} emojiContainer The emojis container\n * @param {Element} searchResultsContainer The search results container\n */\nconst showSearchResults = (emojiContainer, searchResultsContainer) => {\n searchResultsContainer.classList.remove('hidden');\n emojiContainer.classList.add('hidden');\n};\n\n/**\n * Hide the search result container and show the emojis container.\n *\n * @method\n * @param {Element} emojiContainer The emojis container\n * @param {Element} searchResultsContainer The search results container\n * @param {Element} searchInput The search input\n */\nconst clearSearch = (emojiContainer, searchResultsContainer, searchInput) => {\n searchResultsContainer.classList.add('hidden');\n emojiContainer.classList.remove('hidden');\n searchInput.value = '';\n};\n\n/**\n * Build function to handle mouse hovering an emoji. Shows the preview.\n *\n * @method\n * @param {Element} emojiPreview The emoji preview element\n * @param {Element} emojiShortName The emoji short name element\n * @return {Function}\n */\nconst getHandleMouseEnter = (emojiPreview, emojiShortName) => {\n return (e) => {\n const target = e.target;\n if (isEmojiElement(target)) {\n emojiShortName.textContent = target.getAttribute('data-short-names');\n emojiPreview.textContent = target.textContent;\n }\n };\n};\n\n/**\n * Build function to handle mouse leaving an emoji. Removes the preview.\n *\n * @method\n * @param {Element} emojiPreview The emoji preview element\n * @param {Element} emojiShortName The emoji short name element\n * @return {Function}\n */\nconst getHandleMouseLeave = (emojiPreview, emojiShortName) => {\n return (e) => {\n const target = e.target;\n if (isEmojiElement(target)) {\n emojiShortName.textContent = '';\n emojiPreview.textContent = '';\n }\n };\n};\n\n/**\n * Build the function to handle a user clicking something in the picker.\n *\n * The function currently handles clicking on the category selector or selecting\n * a specific emoji.\n *\n * @method\n * @param {Number} recentEmojiRowCount Number of rows of recent emojis\n * @param {Element} emojiContainer Container element for the visible of emojis\n * @param {Element} searchResultsContainer Contaienr element for the search results\n * @param {Element} searchInput Search input element\n * @param {Function} selectCallback Callback function to execute when a user selects an emoji\n * @param {Function} renderAtPosition Render function to display current visible emojis\n * @return {Function}\n */\nconst getHandleClick = (\n recentEmojiRowCount,\n emojiContainer,\n searchResultsContainer,\n searchInput,\n selectCallback,\n renderAtPosition\n) => {\n return (e, rowData, categoryScrollPositions) => {\n const target = e.target;\n let newRowData = rowData;\n let newCategoryScrollPositions = categoryScrollPositions;\n\n // Hide the search results if they are visible.\n clearSearch(emojiContainer, searchResultsContainer, searchInput);\n\n if (isEmojiElement(target)) {\n // Emoji selected.\n const unified = target.getAttribute('data-unified');\n const shortnames = target.getAttribute('data-short-names').replace(/:/g, '').split(' ');\n // Build the emoji data from the selected element.\n const emojiData = {unified, shortnames};\n const currentScrollTop = emojiContainer.scrollTop;\n const isRecentEmojiRowVisible = emojiContainer.querySelector(`[data-row=\"${recentEmojiRowCount - 1}\"]`) !== null;\n // Save the selected emoji in the recent emojis list.\n [newRowData, recentEmojiRowCount] = addRecentEmoji(rowData, recentEmojiRowCount, emojiData);\n // Re-index the category scroll positions because the additional recent emoji may have\n // changed their positions.\n newCategoryScrollPositions = getCategoryScrollPositionsFromRowData(newRowData);\n\n if (isRecentEmojiRowVisible) {\n // If the list of recent emojis is currently visible then we need to re-render the emojis\n // to update the display and show the newly selected recent emoji.\n renderAtPosition(currentScrollTop, newRowData);\n }\n\n // Call the client's callback function with the selected emoji.\n selectCallback(target.textContent);\n // Return the newly calculated row data and scroll positions.\n return [newRowData, newCategoryScrollPositions];\n }\n\n const categorySelector = findCategorySelectorFromElement(target);\n if (categorySelector) {\n // Category selector.\n const selectedCategory = categorySelector.getAttribute('data-category');\n const position = categoryScrollPositions[selectedCategory];\n // Scroll the container to the selected category. This will trigger the\n // on scroll handler to re-render the visibile emojis.\n emojiContainer.scrollTop = position;\n }\n\n return [newRowData, newCategoryScrollPositions];\n };\n};\n\n/**\n * Build the function that handles scrolling of the emoji container to display the\n * correct emojis.\n *\n * We render the emoji rows as they are needed rather than all up front so that we\n * can avoid adding tends of thousands of elements to the DOM unnecessarily which\n * would bog down performance.\n *\n * @method\n * @param {Element} root The picker root element\n * @param {Number} currentVisibleRowScrollPosition The current scroll position of the container\n * @param {Element} emojiContainer The emojis container element\n * @param {Object} initialCategoryScrollPositions Scroll positions for each category\n * @param {Function} renderAtPosition Function to render the appropriate emojis for a scroll position\n * @return {Function}\n */\nconst getHandleScroll = (\n root,\n currentVisibleRowScrollPosition,\n emojiContainer,\n initialCategoryScrollPositions,\n renderAtPosition\n) => {\n // Scope some local variables to track the scroll positions of the categories. We need to\n // recalculate these because adding recent emojis can change those positions by adding\n // additional rows.\n let [\n currentCategoryElement,\n previousCategoryPosition,\n nextCategoryPosition\n ] = getCategoryByScrollPosition(root, emojiContainer.scrollTop, initialCategoryScrollPositions);\n\n return (categoryScrollPositions, rowData) => {\n const newScrollPosition = emojiContainer.scrollTop;\n const upperScrollBound = currentVisibleRowScrollPosition + ROW_HEIGHT_RAW;\n const lowerScrollBound = currentVisibleRowScrollPosition - ROW_HEIGHT_RAW;\n // We only need to update the active category indicator if the user has scrolled into a\n // new category scroll position.\n const updateActiveCategory = (newScrollPosition >= nextCategoryPosition) ||\n (newScrollPosition < previousCategoryPosition);\n // We only need to render new emoji rows if the user has scrolled far enough that a new row\n // would be visible (i.e. they've scrolled up or down more than 40px - the height of a row).\n const updateRenderRows = (newScrollPosition < lowerScrollBound) || (newScrollPosition > upperScrollBound);\n\n if (updateActiveCategory) {\n // New category is visible so update the active category selector and re-index the\n // positions incase anything has changed.\n [\n currentCategoryElement,\n previousCategoryPosition,\n nextCategoryPosition\n ] = getCategoryByScrollPosition(root, newScrollPosition, categoryScrollPositions);\n setCategorySelectorActive(root, currentCategoryElement);\n }\n\n if (updateRenderRows) {\n // A new row should be visible so re-render the visible emojis at this new position.\n // We request an animation frame from the browser so that we're not blocking anything.\n // The animation only needs to occur as soon as the browser is ready not immediately.\n requestAnimationFrame(() => {\n renderAtPosition(newScrollPosition, rowData);\n // Remember the updated position.\n currentVisibleRowScrollPosition = newScrollPosition;\n });\n }\n };\n};\n\n/**\n * Build the function that handles search input from the user.\n *\n * @method\n * @param {Element} searchInput The search input element\n * @param {Element} searchResultsContainer Container element to display the search results\n * @param {Element} emojiContainer Container element for the emoji rows\n * @return {Function}\n */\nconst getHandleSearch = (searchInput, searchResultsContainer, emojiContainer) => {\n const rowContainer = searchResultsContainer.querySelector(SELECTORS.ROW_CONTAINER);\n // Build a render function for the search results.\n const renderSearchResultsAtPosition = generateRenderRowsAtPositionFunction(rowContainer);\n searchResultsContainer.appendChild(rowContainer);\n\n return async() => {\n const searchTerm = searchInput.value.toLowerCase();\n\n if (searchTerm) {\n // Display the search results container and hide the emojis container.\n showSearchResults(emojiContainer, searchResultsContainer);\n\n // Find which emojis match the user's search input.\n const matchingEmojis = Object.keys(EmojiData.byShortName).reduce((carry, shortName) => {\n if (shortName.includes(searchTerm)) {\n carry.push({\n shortnames: [shortName],\n unified: EmojiData.byShortName[shortName]\n });\n }\n return carry;\n }, []);\n\n const searchResultsString = await getString('searchresults', 'core');\n const rowData = createRowDataForCategory(searchResultsString, searchResultsString, matchingEmojis, 0);\n // Show the emoji rows for the search results.\n renderSearchResultsAtPosition(0, rowData, rowData.length);\n } else {\n // Hide the search container and show the emojis container.\n clearSearch(emojiContainer, searchResultsContainer, searchInput);\n }\n };\n};\n\n/**\n * Register the emoji picker event listeners.\n *\n * @method\n * @param {Element} root The picker root element\n * @param {Element} emojiContainer Root element containing the list of visible emojis\n * @param {Function} renderAtPosition Function to render the visible emojis at a given scroll position\n * @param {Number} currentVisibleRowScrollPosition What is the current scroll position\n * @param {Function} selectCallback Function to execute when the user picks an emoji\n * @param {Object} categoryScrollPositions Scroll positions for where each of the emoji categories begin\n * @param {Array} rowData Data representing each of the display rows for hte emoji container\n * @param {Number} recentEmojiRowCount Number of rows of recent emojis\n */\nconst registerEventListeners = (\n root,\n emojiContainer,\n renderAtPosition,\n currentVisibleRowScrollPosition,\n selectCallback,\n categoryScrollPositions,\n rowData,\n recentEmojiRowCount\n) => {\n const searchInput = root.querySelector(SELECTORS.SEARCH_INPUT);\n const searchResultsContainer = root.querySelector(SELECTORS.SEARCH_RESULTS_CONTAINER);\n const emojiPreview = root.querySelector(SELECTORS.EMOJI_PREVIEW);\n const emojiShortName = root.querySelector(SELECTORS.EMOJI_SHORT_NAME);\n // Build the click handler function.\n const clickHandler = getHandleClick(\n recentEmojiRowCount,\n emojiContainer,\n searchResultsContainer,\n searchInput,\n selectCallback,\n renderAtPosition\n );\n // Build the scroll handler function.\n const scrollHandler = getHandleScroll(\n root,\n currentVisibleRowScrollPosition,\n emojiContainer,\n categoryScrollPositions,\n renderAtPosition\n );\n const searchHandler = getHandleSearch(searchInput, searchResultsContainer, emojiContainer);\n\n // Mouse enter/leave events to show the emoji preview on hover or focus.\n root.addEventListener('focus', getHandleMouseEnter(emojiPreview, emojiShortName), true);\n root.addEventListener('blur', getHandleMouseLeave(emojiPreview, emojiShortName), true);\n root.addEventListener('mouseenter', getHandleMouseEnter(emojiPreview, emojiShortName), true);\n root.addEventListener('mouseleave', getHandleMouseLeave(emojiPreview, emojiShortName), true);\n // User selects an emoji or clicks on one of the emoji category selectors.\n root.addEventListener('click', e => {\n // Update the row data and category scroll positions because they may have changes if the\n // user selects an emoji which updates the recent emojis list.\n [rowData, categoryScrollPositions] = clickHandler(e, rowData, categoryScrollPositions);\n });\n // Throttle the scroll event to only execute once every 50 milliseconds to prevent performance issues\n // in the browser when re-rendering the picker emojis. The scroll event fires a lot otherwise.\n emojiContainer.addEventListener('scroll', throttle(() => scrollHandler(categoryScrollPositions, rowData), 50));\n // Debounce the search input so that it only executes 200 milliseconds after the user has finished typing.\n searchInput.addEventListener('input', debounce(searchHandler, 200));\n};\n\n/**\n * Initialise the emoji picker.\n *\n * @method\n * @param {Element} root The root element for the picker\n * @param {Function} selectCallback Callback for when the user selects an emoji\n */\nexport default (root, selectCallback) => {\n const emojiContainer = root.querySelector(SELECTORS.EMOJIS_CONTAINER);\n const rowContainer = emojiContainer.querySelector(SELECTORS.ROW_CONTAINER);\n const recentEmojis = getRecentEmojis();\n // Add the recent emojis category to the list of standard categories.\n const allData = [{\n name: 'Recent',\n emojis: recentEmojis\n }, ...EmojiData.byCategory];\n let rowData = [];\n let recentEmojiRowCount = 0;\n\n /**\n * Split categories data into rows which represent how they will be displayed in the\n * picker. Each category will add a row containing the display name for the category\n * and a row for every 9 emojis in the category. The row data will be used to calculate\n * which emojis should be visible in the picker at any given time.\n *\n * E.g.\n * input = [\n * {name: 'example1', emojis: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]},\n * {name: 'example2', emojis: [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]},\n * ]\n * output = [\n * {type: 'categoryName': data: 'Example 1'},\n * {type: 'emojiRow': data: [1, 2, 3, 4, 5, 6, 7, 8, 9]},\n * {type: 'emojiRow': data: [10, 11, 12]},\n * {type: 'categoryName': data: 'Example 2'},\n * {type: 'emojiRow': data: [13, 14, 15, 16, 17, 18, 19, 20, 21]},\n * {type: 'emojiRow': data: [22, 23]},\n * ]\n */\n allData.forEach(category => {\n const categorySelector = getCategorySelectorByCategoryName(root, category.name);\n // Get the display name from the category selector button so that we don't need to\n // send an ajax request for the string.\n const categoryDisplayName = categorySelector.title;\n const categoryRowData = createRowDataForCategory(category.name, categoryDisplayName, category.emojis, rowData.length);\n\n if (category.name === 'Recent') {\n // Remember how many recent emoji rows there are because it needs to be used to\n // re-index the row data later when we're adding more recent emojis.\n recentEmojiRowCount = categoryRowData.length;\n }\n\n rowData = rowData.concat(categoryRowData);\n });\n\n // Index the row data so that we can calculate which rows should be visible.\n rowData = addIndexesToRowData(rowData);\n // Calculate the scroll positions for each of the categories within the emoji container.\n // These are used to know where to jump to when the user selects a specific category.\n const categoryScrollPositions = getCategoryScrollPositionsFromRowData(rowData);\n const renderAtPosition = generateRenderRowsAtPositionFunction(rowContainer);\n // Display the initial set of emojis.\n renderAtPosition(0, rowData);\n\n registerEventListeners(\n root,\n emojiContainer,\n renderAtPosition,\n 0,\n selectCallback,\n categoryScrollPositions,\n rowData,\n recentEmojiRowCount\n );\n};\n"],"names":["ROW_TYPE","SELECTORS","createRowDataForCategory","categoryName","categoryDisplayName","emojis","totalRowCount","rowData","push","index","length","type","data","name","displayName","i","rowEmojis","slice","addIndexesToRowData","map","getCategoryScrollPositionsFromRowData","reduce","carry","row","isEmojiElement","element","getAttribute","findCategorySelectorFromElement","parentElement","getCategorySelectorByCategoryName","root","querySelector","getCategoryByScrollPosition","position","categoryScrollPositions","positions","categoryPosition","sort","a","b","previousPosition","nextPosition","candidate","currentPosition","getRecentEmojis","storedData","LocalStorage","get","JSON","parse","addRecentEmoji","recentEmojiRowCount","newEmoji","recentEmojis","newRecentEmojis","filter","emoji","unified","EMOJIS_PER_ROW","newRecentEmojiRowData","set","stringify","saveRecentEmoji","concat","getRowsToRender","scrollPosition","visibleRowCount","minVisibleRow","Math","floor","start","end","createRowElement","async","rowIndex","context","text","html","temp","document","createElement","innerHTML","firstChild","createHeaderRow","emojiData","charCodes","split","code","emojiText","String","fromCodePoint","apply","shortnames","join","spacer","spacers","Array","fill","createEmojiRow","style","left","right","top","doRowsMatch","generateRenderRowsAtPositionFunction","rowContainer","currentRows","nextRows","rowCount","isRendering","renderNextRows","nextRowsToRender","toAdd","nextRow","some","currentRow","toKeep","toRemoveElements","querySelectorAll","Promise","all","forEach","nextRowIndex","nextRowData","nextRowNode","insertBefore","splice","appendChild","rows","removeChild","renderRows","rowLimit","height","clearSearch","emojiContainer","searchResultsContainer","searchInput","classList","add","remove","value","getHandleMouseEnter","emojiPreview","emojiShortName","e","target","textContent","getHandleMouseLeave","getHandleScroll","currentVisibleRowScrollPosition","initialCategoryScrollPositions","renderAtPosition","currentCategoryElement","previousCategoryPosition","nextCategoryPosition","scrollTop","newScrollPosition","updateRenderRows","allCategorySelectors","setCategorySelectorActive","requestAnimationFrame","registerEventListeners","selectCallback","clickHandler","newRowData","newCategoryScrollPositions","replace","currentScrollTop","isRecentEmojiRowVisible","categorySelector","getHandleClick","scrollHandler","searchHandler","renderSearchResultsAtPosition","searchTerm","toLowerCase","showSearchResults","matchingEmojis","Object","keys","EmojiData","byShortName","shortName","includes","searchResultsString","getHandleSearch","addEventListener","allData","byCategory","category","title","categoryRowData"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"picker.min.js","sources":["../../src/emoji/picker.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 * Emoji picker.\n *\n * @module core/emoji/picker\n * @copyright 2019 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport LocalStorage from 'core/localstorage';\nimport * as EmojiData from 'core/emoji/data';\nimport {throttle, debounce} from 'core/utils';\nimport {getString} from 'core/str';\nimport {render as renderTemplate} from 'core/templates';\n\nconst VISIBLE_ROW_COUNT = 10;\nconst ROW_RENDER_BUFFER_COUNT = 5;\nconst RECENT_EMOJIS_STORAGE_KEY = 'moodle-recent-emojis';\nconst ROW_HEIGHT_RAW = 40;\nconst EMOJIS_PER_ROW = 7;\nconst MAX_RECENT_COUNT = EMOJIS_PER_ROW * 3;\nconst ROW_TYPE = {\n EMOJI: 0,\n HEADER: 1\n};\nconst SELECTORS = {\n CATEGORY_SELECTOR: '[data-action=\"show-category\"]',\n EMOJIS_CONTAINER: '[data-region=\"emojis-container\"]',\n EMOJI_PREVIEW: '[data-region=\"emoji-preview\"]',\n EMOJI_SHORT_NAME: '[data-region=\"emoji-short-name\"]',\n ROW_CONTAINER: '[data-region=\"row-container\"]',\n SEARCH_INPUT: '[data-region=\"search-input\"]',\n SEARCH_RESULTS_CONTAINER: '[data-region=\"search-results-container\"]'\n};\n\n/**\n * Create the row data for a category.\n *\n * @method\n * @param {String} categoryName The category name\n * @param {String} categoryDisplayName The category display name\n * @param {Array} emojis The emoji data\n * @param {Number} totalRowCount The total number of rows generated so far\n * @return {Array}\n */\nconst createRowDataForCategory = (categoryName, categoryDisplayName, emojis, totalRowCount) => {\n const rowData = [];\n rowData.push({\n index: totalRowCount + rowData.length,\n type: ROW_TYPE.HEADER,\n data: {\n name: categoryName,\n displayName: categoryDisplayName\n }\n });\n\n for (let i = 0; i < emojis.length; i += EMOJIS_PER_ROW) {\n const rowEmojis = emojis.slice(i, i + EMOJIS_PER_ROW);\n rowData.push({\n index: totalRowCount + rowData.length,\n type: ROW_TYPE.EMOJI,\n data: rowEmojis\n });\n }\n\n return rowData;\n};\n\n/**\n * Add each row's index to it's value in the row data.\n *\n * @method\n * @param {Array} rowData List of emoji row data\n * @return {Array}\n */\nconst addIndexesToRowData = (rowData) => {\n return rowData.map((data, index) => {\n return {...data, index};\n });\n};\n\n/**\n * Calculate the scroll position for the beginning of each category from\n * the row data.\n *\n * @method\n * @param {Array} rowData List of emoji row data\n * @return {Object}\n */\nconst getCategoryScrollPositionsFromRowData = (rowData) => {\n return rowData.reduce((carry, row, index) => {\n if (row.type === ROW_TYPE.HEADER) {\n carry[row.data.name] = index * ROW_HEIGHT_RAW;\n }\n return carry;\n }, {});\n};\n\n/**\n * Create a header row element for the category name.\n *\n * @method\n * @param {Number} rowIndex Index of the row in the row data\n * @param {String} name The category display name\n * @return {Element}\n */\nconst createHeaderRow = async(rowIndex, name) => {\n const context = {\n index: rowIndex,\n text: name\n };\n const html = await renderTemplate('core/emoji/header_row', context);\n const temp = document.createElement('div');\n temp.innerHTML = html;\n return temp.firstChild;\n};\n\n/**\n * Create an emoji row element.\n *\n * @method\n * @param {Number} rowIndex Index of the row in the row data\n * @param {Array} emojis The list of emoji data for the row\n * @return {Element}\n */\nconst createEmojiRow = async(rowIndex, emojis) => {\n const context = {\n index: rowIndex,\n emojis: emojis.map(emojiData => {\n const charCodes = emojiData.unified.split('-').map(code => `0x${code}`);\n const emojiText = String.fromCodePoint.apply(null, charCodes);\n return {\n shortnames: `:${emojiData.shortnames.join(': :')}:`,\n unified: emojiData.unified,\n text: emojiText,\n spacer: false\n };\n }),\n spacers: Array(EMOJIS_PER_ROW - emojis.length).fill(true)\n };\n const html = await renderTemplate('core/emoji/emoji_row', context);\n const temp = document.createElement('div');\n temp.innerHTML = html;\n return temp.firstChild;\n};\n\n/**\n * Check if the element is an emoji element.\n *\n * @method\n * @param {Element} element Element to check\n * @return {Bool}\n */\nconst isEmojiElement = element => element.getAttribute('data-short-names') !== null;\n\n/**\n * Search from an element and up through it's ancestors to fine the category\n * selector element and return it.\n *\n * @method\n * @param {Element} element Element to begin searching from\n * @return {Element|null}\n */\nconst findCategorySelectorFromElement = element => {\n if (!element) {\n return null;\n }\n\n if (element.getAttribute('data-action') === 'show-category') {\n return element;\n } else {\n return findCategorySelectorFromElement(element.parentElement);\n }\n};\n\nconst getCategorySelectorByCategoryName = (root, name) => {\n return root.querySelector(`[data-category=\"${name}\"]`);\n};\n\n/**\n * Sets the given category selector element as active.\n *\n * @method\n * @param {Element} root The root picker element\n * @param {Element} element The category selector element to make active\n */\nconst setCategorySelectorActive = (root, element) => {\n const allCategorySelectors = root.querySelectorAll(SELECTORS.CATEGORY_SELECTOR);\n\n for (let i = 0; i < allCategorySelectors.length; i++) {\n const selector = allCategorySelectors[i];\n selector.classList.remove('selected');\n }\n\n element.classList.add('selected');\n};\n\n/**\n * Get the category selector element and the scroll positions for the previous and\n * next categories for the given scroll position.\n *\n * @method\n * @param {Element} root The picker root element\n * @param {Number} position The position to get the category for\n * @param {Object} categoryScrollPositions Set of scroll positions for all categories\n * @return {Array}\n */\nconst getCategoryByScrollPosition = (root, position, categoryScrollPositions) => {\n let positions = [];\n\n if (position < 0) {\n position = 0;\n }\n\n // Get all of the category positions.\n for (const categoryName in categoryScrollPositions) {\n const categoryPosition = categoryScrollPositions[categoryName];\n positions.push([categoryPosition, categoryName]);\n }\n\n // Sort the positions in ascending order.\n positions.sort(([a], [b]) => {\n if (a < b) {\n return -1;\n } else if (a > b) {\n return 1;\n } else {\n return 0;\n }\n });\n\n // Get the current category name as well as the previous and next category\n // positions from the sorted list of positions.\n const {categoryName, previousPosition, nextPosition} = positions.reduce(\n (carry, candidate) => {\n const [categoryPosition, categoryName] = candidate;\n\n if (categoryPosition <= position) {\n carry.categoryName = categoryName;\n carry.previousPosition = carry.currentPosition;\n carry.currentPosition = position;\n } else if (carry.nextPosition === null) {\n carry.nextPosition = categoryPosition;\n }\n\n return carry;\n },\n {\n categoryName: null,\n currentPosition: null,\n previousPosition: null,\n nextPosition: null\n }\n );\n\n return [getCategorySelectorByCategoryName(root, categoryName), previousPosition, nextPosition];\n};\n\n/**\n * Get the list of recent emojis data from local storage.\n *\n * @method\n * @return {Array}\n */\nconst getRecentEmojis = () => {\n const storedData = LocalStorage.get(RECENT_EMOJIS_STORAGE_KEY);\n return storedData ? JSON.parse(storedData) : [];\n};\n\n/**\n * Save the list of recent emojis in local storage.\n *\n * @method\n * @param {Array} recentEmojis List of emoji data to save\n */\nconst saveRecentEmoji = (recentEmojis) => {\n LocalStorage.set(RECENT_EMOJIS_STORAGE_KEY, JSON.stringify(recentEmojis));\n};\n\n/**\n * Add an emoji data to the set of recent emojis. This function will update the row\n * data to ensure that the recent emoji rows are correct and all of the rows are\n * re-indexed.\n *\n * The new set of recent emojis are saved in local storage and the full set of updated\n * row data and new emoji row count are returned.\n *\n * @method\n * @param {Array} rowData The emoji rows data\n * @param {Number} recentEmojiRowCount Count of the recent emoji rows\n * @param {Object} newEmoji The emoji data for the emoji to add to the recent emoji list\n * @return {Array}\n */\nconst addRecentEmoji = (rowData, recentEmojiRowCount, newEmoji) => {\n // The first set of rows is always the recent emojis.\n const categoryName = rowData[0].data.name;\n const categoryDisplayName = rowData[0].data.displayName;\n const recentEmojis = getRecentEmojis();\n // Add the new emoji to the start of the list of recent emojis.\n let newRecentEmojis = [newEmoji, ...recentEmojis.filter(emoji => emoji.unified != newEmoji.unified)];\n // Limit the number of recent emojis.\n newRecentEmojis = newRecentEmojis.slice(0, MAX_RECENT_COUNT);\n const newRecentEmojiRowData = createRowDataForCategory(categoryName, categoryDisplayName, newRecentEmojis);\n\n // Save the new list in local storage.\n saveRecentEmoji(newRecentEmojis);\n\n return [\n // Return the new rowData and re-index it to make sure it's all correct.\n addIndexesToRowData(newRecentEmojiRowData.concat(rowData.slice(recentEmojiRowCount))),\n newRecentEmojiRowData.length\n ];\n};\n\n/**\n * Calculate which rows should be visible based on the given scroll position. Adds a\n * buffer to amount to either side of the total number of requested rows so that\n * scrolling the emoji rows container is smooth.\n *\n * @method\n * @param {Number} scrollPosition Scroll position within the emoji container\n * @param {Number} visibleRowCount How many rows should be visible\n * @param {Array} rowData The emoji rows data\n * @return {Array}\n */\nconst getRowsToRender = (scrollPosition, visibleRowCount, rowData) => {\n const minVisibleRow = scrollPosition > ROW_HEIGHT_RAW ? Math.floor(scrollPosition / ROW_HEIGHT_RAW) : 0;\n const start = minVisibleRow >= ROW_RENDER_BUFFER_COUNT ? minVisibleRow - ROW_RENDER_BUFFER_COUNT : minVisibleRow;\n const end = minVisibleRow + visibleRowCount + ROW_RENDER_BUFFER_COUNT;\n const rows = rowData.slice(start, end);\n return rows;\n};\n\n/**\n * Create a row element from the row data.\n *\n * @method\n * @param {Object} rowData The emoji row data\n * @return {Element}\n */\nconst createRowElement = async(rowData) => {\n let row = null;\n if (rowData.type === ROW_TYPE.HEADER) {\n row = await createHeaderRow(rowData.index, rowData.data.displayName);\n } else {\n row = await createEmojiRow(rowData.index, rowData.data);\n }\n\n row.style.position = 'absolute';\n row.style.left = 0;\n row.style.right = 0;\n row.style.top = `${rowData.index * ROW_HEIGHT_RAW}px`;\n\n return row;\n};\n\n/**\n * Check if the given rows match.\n *\n * @method\n * @param {Object} a The first row\n * @param {Object} b The second row\n * @return {Bool}\n */\nconst doRowsMatch = (a, b) => {\n if (a.index !== b.index) {\n return false;\n }\n\n if (a.type !== b.type) {\n return false;\n }\n\n if (typeof a.data != typeof b.data) {\n return false;\n }\n\n if (a.type === ROW_TYPE.HEADER) {\n return a.data.name === b.data.name;\n } else {\n if (a.data.length !== b.data.length) {\n return false;\n }\n\n for (let i = 0; i < a.data.length; i++) {\n if (a.data[i].unified != b.data[i].unified) {\n return false;\n }\n }\n }\n\n return true;\n};\n\n/**\n * Update the visible rows. Deletes any row elements that should no longer\n * be visible and creates the newly visible row elements. Any rows that haven't\n * changed visibility will be left untouched.\n *\n * @method\n * @param {Element} rowContainer The container element for the emoji rows\n * @param {Array} currentRows List of row data that matches the currently visible rows\n * @param {Array} nextRows List of row data containing the new list of rows to be made visible\n */\nconst renderRows = async(rowContainer, currentRows, nextRows) => {\n // We need to add any rows that are in nextRows but not in currentRows.\n const toAdd = nextRows.filter(nextRow => !currentRows.some(currentRow => doRowsMatch(currentRow, nextRow)));\n // Remember which rows will still be visible so that we can insert our element in the correct place in the DOM.\n let toKeep = currentRows.filter(currentRow => nextRows.some(nextRow => doRowsMatch(currentRow, nextRow)));\n // We need to remove any rows that are in currentRows but not in nextRows.\n const toRemove = currentRows.filter(currentRow => !nextRows.some(nextRow => doRowsMatch(currentRow, nextRow)));\n const toRemoveElements = toRemove.map(rowData => rowContainer.querySelectorAll(`[data-row=\"${rowData.index}\"]`));\n\n // Render all of the templates first.\n const rows = await Promise.all(toAdd.map(rowData => createRowElement(rowData)));\n\n rows.forEach((row, index) => {\n const rowData = toAdd[index];\n let nextRowIndex = null;\n\n for (let i = 0; i < toKeep.length; i++) {\n const candidate = toKeep[i];\n if (candidate.index > rowData.index) {\n nextRowIndex = i;\n break;\n }\n }\n\n // Make sure the elements get added to the DOM in the correct order (ascending by row data index)\n // so that they appear naturally in the tab order.\n if (nextRowIndex !== null) {\n const nextRowData = toKeep[nextRowIndex];\n const nextRowNode = rowContainer.querySelector(`[data-row=\"${nextRowData.index}\"]`);\n\n rowContainer.insertBefore(row, nextRowNode);\n toKeep.splice(nextRowIndex, 0, toKeep);\n } else {\n toKeep.push(rowData);\n rowContainer.appendChild(row);\n }\n });\n\n toRemoveElements.forEach(rows => {\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i];\n rowContainer.removeChild(row);\n }\n });\n};\n\n/**\n * Build a function to render the visible emoji rows for a given scroll\n * position.\n *\n * @method\n * @param {Element} rowContainer The container element for the emoji rows\n * @return {Function}\n */\nconst generateRenderRowsAtPositionFunction = (rowContainer) => {\n let currentRows = [];\n let nextRows = [];\n let rowCount = 0;\n let isRendering = false;\n const renderNextRows = async() => {\n if (!nextRows.length) {\n return;\n }\n\n if (isRendering) {\n return;\n }\n\n isRendering = true;\n const nextRowsToRender = nextRows.slice();\n nextRows = [];\n\n await renderRows(rowContainer, currentRows, nextRowsToRender);\n currentRows = nextRowsToRender;\n isRendering = false;\n renderNextRows();\n };\n\n return (scrollPosition, rowData, rowLimit = VISIBLE_ROW_COUNT) => {\n nextRows = getRowsToRender(scrollPosition, rowLimit, rowData);\n renderNextRows();\n\n if (rowCount !== rowData.length) {\n // Adjust the height of the container to match the number of rows.\n rowContainer.style.height = `${rowData.length * ROW_HEIGHT_RAW}px`;\n }\n\n rowCount = rowData.length;\n };\n};\n\n/**\n * Show the search results container and hide the emoji container.\n *\n * @method\n * @param {Element} emojiContainer The emojis container\n * @param {Element} searchResultsContainer The search results container\n */\nconst showSearchResults = (emojiContainer, searchResultsContainer) => {\n searchResultsContainer.classList.remove('hidden');\n emojiContainer.classList.add('hidden');\n};\n\n/**\n * Hide the search result container and show the emojis container.\n *\n * @method\n * @param {Element} emojiContainer The emojis container\n * @param {Element} searchResultsContainer The search results container\n * @param {Element} searchInput The search input\n */\nconst clearSearch = (emojiContainer, searchResultsContainer, searchInput) => {\n searchResultsContainer.classList.add('hidden');\n emojiContainer.classList.remove('hidden');\n searchInput.value = '';\n};\n\n/**\n * Build function to handle mouse hovering an emoji. Shows the preview.\n *\n * @method\n * @param {Element} emojiPreview The emoji preview element\n * @param {Element} emojiShortName The emoji short name element\n * @return {Function}\n */\nconst getHandleMouseEnter = (emojiPreview, emojiShortName) => {\n return (e) => {\n const target = e.target;\n if (isEmojiElement(target)) {\n emojiShortName.textContent = target.getAttribute('data-short-names');\n emojiPreview.textContent = target.textContent;\n }\n };\n};\n\n/**\n * Build function to handle mouse leaving an emoji. Removes the preview.\n *\n * @method\n * @param {Element} emojiPreview The emoji preview element\n * @param {Element} emojiShortName The emoji short name element\n * @return {Function}\n */\nconst getHandleMouseLeave = (emojiPreview, emojiShortName) => {\n return (e) => {\n const target = e.target;\n if (isEmojiElement(target)) {\n emojiShortName.textContent = '';\n emojiPreview.textContent = '';\n }\n };\n};\n\n/**\n * Build the function to handle a user clicking something in the picker.\n *\n * The function currently handles clicking on the category selector or selecting\n * a specific emoji.\n *\n * @method\n * @param {Number} recentEmojiRowCount Number of rows of recent emojis\n * @param {Element} emojiContainer Container element for the visible of emojis\n * @param {Element} searchResultsContainer Contaienr element for the search results\n * @param {Element} searchInput Search input element\n * @param {Function} selectCallback Callback function to execute when a user selects an emoji\n * @param {Function} renderAtPosition Render function to display current visible emojis\n * @return {Function}\n */\nconst getHandleClick = (\n recentEmojiRowCount,\n emojiContainer,\n searchResultsContainer,\n searchInput,\n selectCallback,\n renderAtPosition\n) => {\n return (e, rowData, categoryScrollPositions) => {\n const target = e.target;\n let newRowData = rowData;\n let newCategoryScrollPositions = categoryScrollPositions;\n\n // Hide the search results if they are visible.\n clearSearch(emojiContainer, searchResultsContainer, searchInput);\n\n if (isEmojiElement(target)) {\n // Emoji selected.\n const unified = target.getAttribute('data-unified');\n const shortnames = target.getAttribute('data-short-names').replace(/:/g, '').split(' ');\n // Build the emoji data from the selected element.\n const emojiData = {unified, shortnames};\n const currentScrollTop = emojiContainer.scrollTop;\n const isRecentEmojiRowVisible = emojiContainer.querySelector(`[data-row=\"${recentEmojiRowCount - 1}\"]`) !== null;\n // Save the selected emoji in the recent emojis list.\n [newRowData, recentEmojiRowCount] = addRecentEmoji(rowData, recentEmojiRowCount, emojiData);\n // Re-index the category scroll positions because the additional recent emoji may have\n // changed their positions.\n newCategoryScrollPositions = getCategoryScrollPositionsFromRowData(newRowData);\n\n if (isRecentEmojiRowVisible) {\n // If the list of recent emojis is currently visible then we need to re-render the emojis\n // to update the display and show the newly selected recent emoji.\n renderAtPosition(currentScrollTop, newRowData);\n }\n\n // Call the client's callback function with the selected emoji.\n selectCallback(target.textContent);\n // Return the newly calculated row data and scroll positions.\n return [newRowData, newCategoryScrollPositions];\n }\n\n const categorySelector = findCategorySelectorFromElement(target);\n if (categorySelector) {\n // Category selector.\n const selectedCategory = categorySelector.getAttribute('data-category');\n const position = categoryScrollPositions[selectedCategory];\n // Scroll the container to the selected category. This will trigger the\n // on scroll handler to re-render the visibile emojis.\n emojiContainer.scrollTop = position;\n }\n\n return [newRowData, newCategoryScrollPositions];\n };\n};\n\n/**\n * Build the function that handles scrolling of the emoji container to display the\n * correct emojis.\n *\n * We render the emoji rows as they are needed rather than all up front so that we\n * can avoid adding tends of thousands of elements to the DOM unnecessarily which\n * would bog down performance.\n *\n * @method\n * @param {Element} root The picker root element\n * @param {Number} currentVisibleRowScrollPosition The current scroll position of the container\n * @param {Element} emojiContainer The emojis container element\n * @param {Object} initialCategoryScrollPositions Scroll positions for each category\n * @param {Function} renderAtPosition Function to render the appropriate emojis for a scroll position\n * @return {Function}\n */\nconst getHandleScroll = (\n root,\n currentVisibleRowScrollPosition,\n emojiContainer,\n initialCategoryScrollPositions,\n renderAtPosition\n) => {\n // Scope some local variables to track the scroll positions of the categories. We need to\n // recalculate these because adding recent emojis can change those positions by adding\n // additional rows.\n let [\n currentCategoryElement,\n previousCategoryPosition,\n nextCategoryPosition\n ] = getCategoryByScrollPosition(root, emojiContainer.scrollTop, initialCategoryScrollPositions);\n\n return (categoryScrollPositions, rowData) => {\n const newScrollPosition = emojiContainer.scrollTop;\n const upperScrollBound = currentVisibleRowScrollPosition + ROW_HEIGHT_RAW;\n const lowerScrollBound = currentVisibleRowScrollPosition - ROW_HEIGHT_RAW;\n // We only need to update the active category indicator if the user has scrolled into a\n // new category scroll position.\n const updateActiveCategory = (newScrollPosition >= nextCategoryPosition) ||\n (newScrollPosition < previousCategoryPosition);\n // We only need to render new emoji rows if the user has scrolled far enough that a new row\n // would be visible (i.e. they've scrolled up or down more than 40px - the height of a row).\n const updateRenderRows = (newScrollPosition < lowerScrollBound) || (newScrollPosition > upperScrollBound);\n\n if (updateActiveCategory) {\n // New category is visible so update the active category selector and re-index the\n // positions incase anything has changed.\n [\n currentCategoryElement,\n previousCategoryPosition,\n nextCategoryPosition\n ] = getCategoryByScrollPosition(root, newScrollPosition, categoryScrollPositions);\n setCategorySelectorActive(root, currentCategoryElement);\n }\n\n if (updateRenderRows) {\n // A new row should be visible so re-render the visible emojis at this new position.\n // We request an animation frame from the browser so that we're not blocking anything.\n // The animation only needs to occur as soon as the browser is ready not immediately.\n requestAnimationFrame(() => {\n renderAtPosition(newScrollPosition, rowData);\n // Remember the updated position.\n currentVisibleRowScrollPosition = newScrollPosition;\n });\n }\n };\n};\n\n/**\n * Build the function that handles search input from the user.\n *\n * @method\n * @param {Element} searchInput The search input element\n * @param {Element} searchResultsContainer Container element to display the search results\n * @param {Element} emojiContainer Container element for the emoji rows\n * @return {Function}\n */\nconst getHandleSearch = (searchInput, searchResultsContainer, emojiContainer) => {\n const rowContainer = searchResultsContainer.querySelector(SELECTORS.ROW_CONTAINER);\n // Build a render function for the search results.\n const renderSearchResultsAtPosition = generateRenderRowsAtPositionFunction(rowContainer);\n searchResultsContainer.appendChild(rowContainer);\n\n return async() => {\n const searchTerm = searchInput.value.toLowerCase();\n\n if (searchTerm) {\n // Display the search results container and hide the emojis container.\n showSearchResults(emojiContainer, searchResultsContainer);\n\n // Find which emojis match the user's search input.\n const matchingEmojis = Object.keys(EmojiData.byShortName).reduce((carry, shortName) => {\n if (shortName.includes(searchTerm)) {\n carry.push({\n shortnames: [shortName],\n unified: EmojiData.byShortName[shortName]\n });\n }\n return carry;\n }, []);\n\n const searchResultsString = await getString('searchresults', 'core');\n const rowData = createRowDataForCategory(searchResultsString, searchResultsString, matchingEmojis, 0);\n // Show the emoji rows for the search results.\n renderSearchResultsAtPosition(0, rowData, rowData.length);\n } else {\n // Hide the search container and show the emojis container.\n clearSearch(emojiContainer, searchResultsContainer, searchInput);\n }\n };\n};\n\n/**\n * Register the emoji picker event listeners.\n *\n * @method\n * @param {Element} root The picker root element\n * @param {Element} emojiContainer Root element containing the list of visible emojis\n * @param {Function} renderAtPosition Function to render the visible emojis at a given scroll position\n * @param {Number} currentVisibleRowScrollPosition What is the current scroll position\n * @param {Function} selectCallback Function to execute when the user picks an emoji\n * @param {Object} categoryScrollPositions Scroll positions for where each of the emoji categories begin\n * @param {Array} rowData Data representing each of the display rows for hte emoji container\n * @param {Number} recentEmojiRowCount Number of rows of recent emojis\n */\nconst registerEventListeners = (\n root,\n emojiContainer,\n renderAtPosition,\n currentVisibleRowScrollPosition,\n selectCallback,\n categoryScrollPositions,\n rowData,\n recentEmojiRowCount\n) => {\n const searchInput = root.querySelector(SELECTORS.SEARCH_INPUT);\n const searchResultsContainer = root.querySelector(SELECTORS.SEARCH_RESULTS_CONTAINER);\n const emojiPreview = root.querySelector(SELECTORS.EMOJI_PREVIEW);\n const emojiShortName = root.querySelector(SELECTORS.EMOJI_SHORT_NAME);\n // Build the click handler function.\n const clickHandler = getHandleClick(\n recentEmojiRowCount,\n emojiContainer,\n searchResultsContainer,\n searchInput,\n selectCallback,\n renderAtPosition\n );\n // Build the scroll handler function.\n const scrollHandler = getHandleScroll(\n root,\n currentVisibleRowScrollPosition,\n emojiContainer,\n categoryScrollPositions,\n renderAtPosition\n );\n const searchHandler = getHandleSearch(searchInput, searchResultsContainer, emojiContainer);\n\n // Mouse enter/leave events to show the emoji preview on hover or focus.\n root.addEventListener('focus', getHandleMouseEnter(emojiPreview, emojiShortName), true);\n root.addEventListener('blur', getHandleMouseLeave(emojiPreview, emojiShortName), true);\n root.addEventListener('mouseenter', getHandleMouseEnter(emojiPreview, emojiShortName), true);\n root.addEventListener('mouseleave', getHandleMouseLeave(emojiPreview, emojiShortName), true);\n // User selects an emoji or clicks on one of the emoji category selectors.\n root.addEventListener('click', e => {\n // Update the row data and category scroll positions because they may have changes if the\n // user selects an emoji which updates the recent emojis list.\n [rowData, categoryScrollPositions] = clickHandler(e, rowData, categoryScrollPositions);\n });\n // Throttle the scroll event to only execute once every 50 milliseconds to prevent performance issues\n // in the browser when re-rendering the picker emojis. The scroll event fires a lot otherwise.\n emojiContainer.addEventListener('scroll', throttle(() => scrollHandler(categoryScrollPositions, rowData), 50));\n // Debounce the search input so that it only executes 200 milliseconds after the user has finished typing.\n searchInput.addEventListener('input', debounce(searchHandler, 200));\n};\n\n/**\n * Initialise the emoji picker.\n *\n * @method\n * @param {Element} root The root element for the picker\n * @param {Function} selectCallback Callback for when the user selects an emoji\n */\nexport default (root, selectCallback) => {\n const emojiContainer = root.querySelector(SELECTORS.EMOJIS_CONTAINER);\n const rowContainer = emojiContainer.querySelector(SELECTORS.ROW_CONTAINER);\n const recentEmojis = getRecentEmojis();\n // Add the recent emojis category to the list of standard categories.\n const allData = [{\n name: 'Recent',\n emojis: recentEmojis\n }, ...EmojiData.byCategory];\n let rowData = [];\n let recentEmojiRowCount = 0;\n\n /**\n * Split categories data into rows which represent how they will be displayed in the\n * picker. Each category will add a row containing the display name for the category\n * and a row for every 9 emojis in the category. The row data will be used to calculate\n * which emojis should be visible in the picker at any given time.\n *\n * E.g.\n * input = [\n * {name: 'example1', emojis: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]},\n * {name: 'example2', emojis: [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]},\n * ]\n * output = [\n * {type: 'categoryName': data: 'Example 1'},\n * {type: 'emojiRow': data: [1, 2, 3, 4, 5, 6, 7, 8, 9]},\n * {type: 'emojiRow': data: [10, 11, 12]},\n * {type: 'categoryName': data: 'Example 2'},\n * {type: 'emojiRow': data: [13, 14, 15, 16, 17, 18, 19, 20, 21]},\n * {type: 'emojiRow': data: [22, 23]},\n * ]\n */\n allData.forEach(category => {\n const categorySelector = getCategorySelectorByCategoryName(root, category.name);\n // Get the display name from the category selector button so that we don't need to\n // send an ajax request for the string.\n const categoryDisplayName = categorySelector.title;\n const categoryRowData = createRowDataForCategory(category.name, categoryDisplayName, category.emojis, rowData.length);\n\n if (category.name === 'Recent') {\n // Remember how many recent emoji rows there are because it needs to be used to\n // re-index the row data later when we're adding more recent emojis.\n recentEmojiRowCount = categoryRowData.length;\n }\n\n rowData = rowData.concat(categoryRowData);\n });\n\n // Index the row data so that we can calculate which rows should be visible.\n rowData = addIndexesToRowData(rowData);\n // Calculate the scroll positions for each of the categories within the emoji container.\n // These are used to know where to jump to when the user selects a specific category.\n const categoryScrollPositions = getCategoryScrollPositionsFromRowData(rowData);\n const renderAtPosition = generateRenderRowsAtPositionFunction(rowContainer);\n // Display the initial set of emojis.\n renderAtPosition(0, rowData);\n\n registerEventListeners(\n root,\n emojiContainer,\n renderAtPosition,\n 0,\n selectCallback,\n categoryScrollPositions,\n rowData,\n recentEmojiRowCount\n );\n};\n"],"names":["ROW_TYPE","SELECTORS","createRowDataForCategory","categoryName","categoryDisplayName","emojis","totalRowCount","rowData","push","index","length","type","data","name","displayName","i","rowEmojis","slice","addIndexesToRowData","map","getCategoryScrollPositionsFromRowData","reduce","carry","row","isEmojiElement","element","getAttribute","findCategorySelectorFromElement","parentElement","getCategorySelectorByCategoryName","root","querySelector","getCategoryByScrollPosition","position","categoryScrollPositions","positions","categoryPosition","sort","a","b","previousPosition","nextPosition","candidate","currentPosition","getRecentEmojis","storedData","LocalStorage","get","JSON","parse","addRecentEmoji","recentEmojiRowCount","newEmoji","recentEmojis","newRecentEmojis","filter","emoji","unified","EMOJIS_PER_ROW","newRecentEmojiRowData","set","stringify","saveRecentEmoji","concat","getRowsToRender","scrollPosition","visibleRowCount","minVisibleRow","Math","floor","start","end","createRowElement","async","rowIndex","context","text","html","temp","document","createElement","innerHTML","firstChild","createHeaderRow","emojiData","charCodes","split","code","emojiText","String","fromCodePoint","apply","shortnames","join","spacer","spacers","Array","fill","createEmojiRow","style","left","right","top","doRowsMatch","generateRenderRowsAtPositionFunction","rowContainer","currentRows","nextRows","rowCount","isRendering","renderNextRows","nextRowsToRender","toAdd","nextRow","some","currentRow","toKeep","toRemoveElements","querySelectorAll","Promise","all","forEach","nextRowIndex","nextRowData","nextRowNode","insertBefore","splice","appendChild","rows","removeChild","renderRows","rowLimit","height","clearSearch","emojiContainer","searchResultsContainer","searchInput","classList","add","remove","value","getHandleMouseEnter","emojiPreview","emojiShortName","e","target","textContent","getHandleMouseLeave","getHandleScroll","currentVisibleRowScrollPosition","initialCategoryScrollPositions","renderAtPosition","currentCategoryElement","previousCategoryPosition","nextCategoryPosition","scrollTop","newScrollPosition","updateRenderRows","allCategorySelectors","setCategorySelectorActive","requestAnimationFrame","registerEventListeners","selectCallback","clickHandler","newRowData","newCategoryScrollPositions","replace","currentScrollTop","isRecentEmojiRowVisible","categorySelector","getHandleClick","scrollHandler","searchHandler","renderSearchResultsAtPosition","searchTerm","toLowerCase","showSearchResults","matchingEmojis","Object","keys","EmojiData","byShortName","shortName","includes","searchResultsString","getHandleSearch","addEventListener","allData","byCategory","category","title","categoryRowData"],"mappings":"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"} \ No newline at end of file diff --git a/lib/amd/build/local/templates/renderer.min.js b/lib/amd/build/local/templates/renderer.min.js index b6d936949c2..8a94cf5ba29 100644 --- a/lib/amd/build/local/templates/renderer.min.js +++ b/lib/amd/build/local/templates/renderer.min.js @@ -9,6 +9,6 @@ define("core/local/templates/renderer",["exports","core/log","core/truncate","co * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 4.3 */ -class Renderer{constructor(){_defineProperty(this,"requiredStrings",null),_defineProperty(this,"requiredDates",[]),_defineProperty(this,"requiredJS",null),_defineProperty(this,"currentThemeName",""),_defineProperty(this,"iconSystem",null),this.requiredStrings=[],this.requiredJS=[],this.requiredDates=[],this.currentThemeName=""}static setLoader(loader){this.loader=loader}static getLoader(){return this.loader}async renderIcon(key,component,title){component=(0,_utils.getNormalisedComponent)(component),await this.setupIconSystem();const template=await Renderer.getLoader().getTemplate(this.iconSystem.getTemplateName(),this.currentThemeName);return this.iconSystem.renderIcon(key,component,title,template)}async setupIconSystem(){return this.iconSystem||(this.iconSystem=await _icon_system.default.instance()),this.iconSystem}pixHelper(context,sectionText,helper){const parts=sectionText.split(",");let key="",component="",text="";parts.length>0&&(key=helper(parts.shift().trim(),context)),parts.length>0&&(component=helper(parts.shift().trim(),context)),parts.length>0&&(text=helper(parts.join(",").trim(),context));const Loader=Renderer.getLoader(),templateName=this.iconSystem.getTemplateName(),searchKey=Loader.getSearchKey(this.currentThemeName,templateName),template=Loader.getTemplateFromCache(searchKey);return component=(0,_utils.getNormalisedComponent)(component),key=key.replace(///gi,"/"),this.iconSystem.renderIcon(key,component,text,template)}jsHelper(context,sectionText,helper){return this.requiredJS.push(helper(sectionText,context)),""}stringHelper(context,sectionText,helper){let parts=sectionText.split(",");const key=parts.length>0?parts.shift().trim():"",component=parts.length>0?(0,_utils.getNormalisedComponent)(parts.shift().trim()):"";let param=parts.length>0?parts.join(",").trim():"";if(""!==param&&(param=helper(param,context)),param.match(/^{\s*"/gm))try{const parsedParam=JSON.parse(param);parsedParam&&"object"==typeof parsedParam&&(param=parsedParam)}catch(err){window.console.warn(err.message)}const index=this.requiredStrings.length;return this.requiredStrings.push({key:key,component:component,param:param}),"[[_s".concat(index,"]]")}cleanStringHelper(context,sectionText,helper){return this.stringHelper(context,sectionText,helper).replace("s","c")}quoteHelper(context,sectionText,helper){let content=helper(sectionText.trim(),context);return content=JSON.stringify(content),content=content.replace(/([{}]{2,3})/g,"{{=<% %>=}}$1<%={{ }}=%>"),content}shortenTextHelper(context,sectionText,helper){const parts=sectionText.match(/(.*?),(.*)/),length=parts[1].trim(),content=helper(parts[2].trim(),context);return Truncate.truncate(content,{length:length,words:!0,ellipsis:"..."})}userDateHelper(context,sectionText,helper){const parts=sectionText.match(/(.*?),(.*)/),timestamp=helper(parts[1].trim(),context),format=helper(parts[2].trim(),context),index=this.requiredDates.length;return this.requiredDates.push({timestamp:timestamp,format:format}),"[[_t_".concat(index,"]]")}addHelperFunction(helperFunction,context){return function(){return function(sectionText,helper){const originalHelpers=Renderer.disallowedNestedHelpers.reduce(((carry,name)=>(context.hasOwnProperty(name)&&(carry[name]=context[name]),carry)),{});Renderer.disallowedNestedHelpers.forEach((helperName=>{context[helperName]=()=>""}));const result=helperFunction.apply(this,[context,sectionText,helper]);for(const name in originalHelpers)context[name]=originalHelpers[name];return result}.bind(this)}.bind(this)}addHelpers(context,themeName){this.currentThemeName=themeName,this.requiredStrings=[],this.requiredJS=[],context.uniqid=Renderer.uniqInstances++,context.str=this.addHelperFunction(this.stringHelper,context),context.cleanstr=this.addHelperFunction(this.cleanStringHelper,context),context.pix=this.addHelperFunction(this.pixHelper,context),context.js=this.addHelperFunction(this.jsHelper,context),context.quote=this.addHelperFunction(this.quoteHelper,context),context.shortentext=this.addHelperFunction(this.shortenTextHelper,context),context.userdate=this.addHelperFunction(this.userDateHelper,context),context.globals={config:_config.default},context.currentTheme=themeName}getJS(){return this.requiredJS.join(";\n")}treatStringsInContent(content,stringMap){const stringPattern=/(?\[\[_(?[cs])(?\d+)\]\])/g,getUpdatedString=_ref=>{let{placeholder:placeholder,stringType:stringType,stringIndex:stringIndex}=_ref;if(stringMap.has(placeholder))return stringMap.get(placeholder);if("c"===stringType){const uncleanString=stringMap.get("[[_s".concat(stringIndex,"]]"));if(uncleanString)return stringMap.set(placeholder,_mustache.default.escape(uncleanString)),stringMap.get(placeholder)}return Log.debug("Could not find string for pattern ".concat(placeholder)),""};let match;for(;null!==(match=stringPattern.exec(content));){let updatedContent=content.slice(0,match.index);updatedContent+=getUpdatedString(match.groups),updatedContent+=content.slice(match.index+match.groups.placeholder.length),content=updatedContent}return content}treatDatesInContent(content,dates){return dates.forEach(((date,index)=>{content=content.replace(new RegExp("\\[\\[_t_".concat(index,"\\]\\]"),"g"),date)})),content}async doRender(templateSourcePromise,context,themeName){this.currentThemeName=themeName;const iconTemplate=this.iconSystem.getTemplateName(),pendingPromise=new _pending.default("core/templates:doRender"),[templateSource]=await Promise.all([templateSourcePromise,Renderer.getLoader().getTemplate(iconTemplate,themeName)]);this.addHelpers(context,themeName);const renderedContent=await _mustache.default.render(templateSource,context,(partialName=>Renderer.getLoader().partialHelper(partialName,themeName))),{html:html,js:js}=await this.processRenderedContent(renderedContent);return pendingPromise.resolve(),{html:html,js:js}}async processRenderedContent(renderedContent){let html=renderedContent.trim(),js=this.getJS();if(this.requiredStrings.length>0){const stringMap=new Map((await(0,_str.get_strings)(this.requiredStrings)).map(((string,index)=>["[[_s".concat(index,"]]"),string])));this.requiredDates=this.requiredDates.map(function(date){return{timestamp:this.treatStringsInContent(date.timestamp,stringMap),format:this.treatStringsInContent(date.format,stringMap)}}.bind(this)),html=this.treatStringsInContent(html,stringMap),js=this.treatStringsInContent(js,stringMap)}if(this.requiredDates.length>0){const dates=await UserDate.get(this.requiredDates);html=this.treatDatesInContent(html,dates),js=this.treatDatesInContent(js,dates)}return{html:html,js:js}}async render(templateName){let context=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},themeName=arguments.length>2&&void 0!==arguments[2]?arguments[2]:_config.default.theme;this.currentThemeName=themeName,await this.setupIconSystem();const templateSource=Renderer.getLoader().cachePartials(templateName,themeName);return this.doRender(templateSource,context,themeName)}}return _exports.default=Renderer,_defineProperty(Renderer,"uniqInstances",0),_defineProperty(Renderer,"loadTemplateBuffer",[]),_defineProperty(Renderer,"isLoadingTemplates",!1),_defineProperty(Renderer,"disallowedNestedHelpers",["js"]),_defineProperty(Renderer,"templateCache",{}),_defineProperty(Renderer,"templatePromises",{}),_defineProperty(Renderer,"loader",_loader.default),_exports.default})); +class Renderer{constructor(){_defineProperty(this,"requiredStrings",null),_defineProperty(this,"requiredDates",[]),_defineProperty(this,"requiredJS",null),_defineProperty(this,"currentThemeName",""),_defineProperty(this,"iconSystem",null),this.requiredStrings=[],this.requiredJS=[],this.requiredDates=[],this.currentThemeName=""}static setLoader(loader){this.loader=loader}static getLoader(){return this.loader}async renderIcon(key,component,title){component=(0,_utils.getNormalisedComponent)(component),await this.setupIconSystem();const template=await Renderer.getLoader().getTemplate(this.iconSystem.getTemplateName(),this.currentThemeName);return this.iconSystem.renderIcon(key,component,title,template)}async setupIconSystem(){return this.iconSystem||(this.iconSystem=await _icon_system.default.instance()),this.iconSystem}pixHelper(context,sectionText,helper){const parts=sectionText.split(",");let key="",component="",text="";parts.length>0&&(key=helper(parts.shift().trim(),context)),parts.length>0&&(component=helper(parts.shift().trim(),context)),parts.length>0&&(text=helper(parts.join(",").trim(),context));const Loader=Renderer.getLoader(),templateName=this.iconSystem.getTemplateName(),searchKey=Loader.getSearchKey(this.currentThemeName,templateName),template=Loader.getTemplateFromCache(searchKey);return component=(0,_utils.getNormalisedComponent)(component),key=key.replace(///gi,"/"),this.iconSystem.renderIcon(key,component,text,template)}jsHelper(context,sectionText,helper){return this.requiredJS.push(helper(sectionText,context)),""}stringHelper(context,sectionText,helper){let parts=sectionText.split(",");const key=parts.length>0?parts.shift().trim():"",component=parts.length>0?(0,_utils.getNormalisedComponent)(parts.shift().trim()):"";let param=parts.length>0?parts.join(",").trim():"";if(""!==param&&(param=helper(param,context)),param.match(/^{\s*"/gm))try{const parsedParam=JSON.parse(param);parsedParam&&"object"==typeof parsedParam&&(param=parsedParam)}catch(err){window.console.warn(err.message)}const index=this.requiredStrings.length;return this.requiredStrings.push({key:key,component:component,param:param}),"[[_s".concat(index,"]]")}cleanStringHelper(context,sectionText,helper){return this.stringHelper(context,sectionText,helper).replace("s","c")}quoteHelper(context,sectionText,helper){let content=helper(sectionText.trim(),context);return content=JSON.stringify(content),content=content.replace(/([{}]{2,3})/g,"{{=<% %>=}}$1<%={{ }}=%>"),content}shortenTextHelper(context,sectionText,helper){const parts=sectionText.match(/(.*?),(.*)/),length=parts[1].trim(),content=helper(parts[2].trim(),context);return Truncate.truncate(content,{length:length,words:!0,ellipsis:"..."})}userDateHelper(context,sectionText,helper){const parts=sectionText.match(/(.*?),(.*)/),timestamp=helper(parts[1].trim(),context),format=helper(parts[2].trim(),context),index=this.requiredDates.length;return this.requiredDates.push({timestamp:timestamp,format:format}),"[[_t_".concat(index,"]]")}addHelperFunction(helperFunction,context){return function(){return function(sectionText,helper){const originalHelpers=Renderer.disallowedNestedHelpers.reduce(((carry,name)=>(context.hasOwnProperty(name)&&(carry[name]=context[name]),carry)),{});Renderer.disallowedNestedHelpers.forEach((helperName=>{context[helperName]=()=>""}));const result=helperFunction.apply(this,[context,sectionText,helper]);for(const name in originalHelpers)context[name]=originalHelpers[name];return result}.bind(this)}.bind(this)}addHelpers(context,themeName){this.currentThemeName=themeName,this.requiredStrings=[],this.requiredJS=[],context.uniqid=Renderer.uniqInstances++,context.str=this.addHelperFunction(this.stringHelper,context),context.cleanstr=this.addHelperFunction(this.cleanStringHelper,context),context.pix=this.addHelperFunction(this.pixHelper,context),context.js=this.addHelperFunction(this.jsHelper,context),context.quote=this.addHelperFunction(this.quoteHelper,context),context.shortentext=this.addHelperFunction(this.shortenTextHelper,context),context.userdate=this.addHelperFunction(this.userDateHelper,context),context.globals={config:_config.default},context.currentTheme=themeName}getJS(){return this.requiredJS.join(";\n")}treatStringsInContent(content,stringMap){const stringPattern=/(?\[\[_(?[cs])(?\d+)\]\])/g,getUpdatedString=_ref=>{let{placeholder:placeholder,stringType:stringType,stringIndex:stringIndex}=_ref;if(stringMap.has(placeholder))return stringMap.get(placeholder);if("c"===stringType){const uncleanString=stringMap.get("[[_s".concat(stringIndex,"]]"));if(uncleanString)return stringMap.set(placeholder,_mustache.default.escape(uncleanString)),stringMap.get(placeholder)}return Log.debug("Could not find string for pattern ".concat(placeholder)),""};let match;for(;null!==(match=stringPattern.exec(content));){let updatedContent=content.slice(0,match.index);updatedContent+=getUpdatedString(match.groups),updatedContent+=content.slice(match.index+match.groups.placeholder.length),content=updatedContent}return content}treatDatesInContent(content,dates){return dates.forEach(((date,index)=>{content=content.replace(new RegExp("\\[\\[_t_".concat(index,"\\]\\]"),"g"),date)})),content}async doRender(templateSourcePromise,context,themeName){this.currentThemeName=themeName;const iconTemplate=this.iconSystem.getTemplateName(),pendingPromise=new _pending.default("core/templates:doRender"),[templateSource]=await Promise.all([templateSourcePromise,Renderer.getLoader().getTemplate(iconTemplate,themeName)]);this.addHelpers(context,themeName);const renderedContent=await _mustache.default.render(templateSource,context,(partialName=>Renderer.getLoader().partialHelper(partialName,themeName))),{html:html,js:js}=await this.processRenderedContent(renderedContent);return pendingPromise.resolve(),{html:html,js:js}}async processRenderedContent(renderedContent){let html=renderedContent.trim(),js=this.getJS();if(this.requiredStrings.length>0){const stringMap=new Map((await(0,_str.getStrings)(this.requiredStrings)).map(((string,index)=>["[[_s".concat(index,"]]"),string])));this.requiredDates=this.requiredDates.map(function(date){return{timestamp:this.treatStringsInContent(date.timestamp,stringMap),format:this.treatStringsInContent(date.format,stringMap)}}.bind(this)),html=this.treatStringsInContent(html,stringMap),js=this.treatStringsInContent(js,stringMap)}if(this.requiredDates.length>0){const dates=await UserDate.get(this.requiredDates);html=this.treatDatesInContent(html,dates),js=this.treatDatesInContent(js,dates)}return{html:html,js:js}}async render(templateName){let context=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},themeName=arguments.length>2&&void 0!==arguments[2]?arguments[2]:_config.default.theme;this.currentThemeName=themeName,await this.setupIconSystem();const templateSource=Renderer.getLoader().cachePartials(templateName,themeName);return this.doRender(templateSource,context,themeName)}}return _exports.default=Renderer,_defineProperty(Renderer,"uniqInstances",0),_defineProperty(Renderer,"loadTemplateBuffer",[]),_defineProperty(Renderer,"isLoadingTemplates",!1),_defineProperty(Renderer,"disallowedNestedHelpers",["js"]),_defineProperty(Renderer,"templateCache",{}),_defineProperty(Renderer,"templatePromises",{}),_defineProperty(Renderer,"loader",_loader.default),_exports.default})); //# sourceMappingURL=renderer.min.js.map \ No newline at end of file diff --git a/lib/amd/build/local/templates/renderer.min.js.map b/lib/amd/build/local/templates/renderer.min.js.map index 2218d0e5e2a..6fdd59c9ace 100644 --- a/lib/amd/build/local/templates/renderer.min.js.map +++ b/lib/amd/build/local/templates/renderer.min.js.map @@ -1 +1 @@ -{"version":3,"file":"renderer.min.js","sources":["../../../src/local/templates/renderer.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\nimport * as Log from 'core/log';\nimport * as Truncate from 'core/truncate';\nimport * as UserDate from 'core/user_date';\nimport Pending from 'core/pending';\nimport {get_strings as getStrings} from 'core/str';\nimport IconSystem from 'core/icon_system';\nimport config from 'core/config';\nimport mustache from 'core/mustache';\nimport Loader from './loader';\nimport {getNormalisedComponent} from 'core/utils';\n\n/** @var {string} The placeholder character used for standard strings (unclean) */\nconst placeholderString = 's';\n\n/** @var {string} The placeholder character used for cleaned strings */\nconst placeholderCleanedString = 'c';\n\n/**\n * Template Renderer Class.\n *\n * Note: This class is not intended to be instantiated directly. Instead, use the core/templates module.\n *\n * @module core/local/templates/renderer\n * @copyright 2023 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.3\n */\nexport default class Renderer {\n /** @var {string[]} requiredStrings - Collection of strings found during the rendering of one template */\n requiredStrings = null;\n\n /** @var {object[]} requiredDates - Collection of dates found during the rendering of one template */\n requiredDates = [];\n\n /** @var {string[]} requiredJS - Collection of js blocks found during the rendering of one template */\n requiredJS = null;\n\n /** @var {String} themeName for the current render */\n currentThemeName = '';\n\n /** @var {Number} uniqInstances Count of times this constructor has been called. */\n static uniqInstances = 0;\n\n /** @var {Object[]} loadTemplateBuffer - List of templates to be loaded */\n static loadTemplateBuffer = [];\n\n /** @var {Bool} isLoadingTemplates - Whether templates are currently being loaded */\n static isLoadingTemplates = false;\n\n /** @var {Object} iconSystem - Object extending core/iconsystem */\n iconSystem = null;\n\n /** @var {Array} disallowedNestedHelpers - List of helpers that can't be called within other helpers */\n static disallowedNestedHelpers = [\n 'js',\n ];\n\n /** @var {String[]} templateCache - Cache of already loaded template strings */\n static templateCache = {};\n\n /**\n * Cache of already loaded template promises.\n *\n * @type {Promise[]}\n * @static\n * @private\n */\n static templatePromises = {};\n\n /**\n * The loader used to fetch templates.\n * @type {Loader}\n * @static\n * @private\n */\n static loader = Loader;\n\n /**\n * Constructor\n *\n * Each call to templates.render gets it's own instance of this class.\n */\n constructor() {\n this.requiredStrings = [];\n this.requiredJS = [];\n this.requiredDates = [];\n this.currentThemeName = '';\n }\n\n /**\n * Set the template loader to use for all Template renderers.\n *\n * @param {Loader} loader\n */\n static setLoader(loader) {\n this.loader = loader;\n }\n\n /**\n * Get the Loader used to fetch templates.\n *\n * @returns {Loader}\n */\n static getLoader() {\n return this.loader;\n }\n\n /**\n * Render a single image icon.\n *\n * @method renderIcon\n * @private\n * @param {string} key The icon key.\n * @param {string} component The component name.\n * @param {string} title The icon title\n * @returns {Promise}\n */\n async renderIcon(key, component, title) {\n // Preload the module to do the icon rendering based on the theme iconsystem.\n component = getNormalisedComponent(component);\n\n await this.setupIconSystem();\n const template = await Renderer.getLoader().getTemplate(\n this.iconSystem.getTemplateName(),\n this.currentThemeName,\n );\n\n return this.iconSystem.renderIcon(\n key,\n component,\n title,\n template\n );\n }\n\n /**\n * Helper to set up the icon system.\n */\n async setupIconSystem() {\n if (!this.iconSystem) {\n this.iconSystem = await IconSystem.instance();\n }\n\n return this.iconSystem;\n }\n\n /**\n * Render image icons.\n *\n * @method pixHelper\n * @private\n * @param {object} context The mustache context\n * @param {string} sectionText The text to parse arguments from.\n * @param {function} helper Used to render the alt attribute of the text.\n * @returns {string}\n */\n pixHelper(context, sectionText, helper) {\n const parts = sectionText.split(',');\n let key = '';\n let component = '';\n let text = '';\n\n if (parts.length > 0) {\n key = helper(parts.shift().trim(), context);\n }\n if (parts.length > 0) {\n component = helper(parts.shift().trim(), context);\n }\n if (parts.length > 0) {\n text = helper(parts.join(',').trim(), context);\n }\n\n // Note: We cannot use Promises in Mustache helpers.\n // We must fetch straight from the Loader cache.\n // The Loader cache is statically defined on the Loader class and should be used by all children.\n const Loader = Renderer.getLoader();\n const templateName = this.iconSystem.getTemplateName();\n const searchKey = Loader.getSearchKey(this.currentThemeName, templateName);\n const template = Loader.getTemplateFromCache(searchKey);\n\n component = getNormalisedComponent(component);\n\n // The key might have been escaped by the JS Mustache engine which\n // converts forward slashes to HTML entities. Let us undo that here.\n key = key.replace(///gi, '/');\n\n return this.iconSystem.renderIcon(\n key,\n component,\n text,\n template\n );\n }\n\n /**\n * Render blocks of javascript and save them in an array.\n *\n * @method jsHelper\n * @private\n * @param {object} context The current mustache context.\n * @param {string} sectionText The text to save as a js block.\n * @param {function} helper Used to render the block.\n * @returns {string}\n */\n jsHelper(context, sectionText, helper) {\n this.requiredJS.push(helper(sectionText, context));\n return '';\n }\n\n /**\n * String helper used to render {{#str}}abd component { a : 'fish'}{{/str}}\n * into a get_string call.\n *\n * @method stringHelper\n * @private\n * @param {object} context The current mustache context.\n * @param {string} sectionText The text to parse the arguments from.\n * @param {function} helper Used to render subsections of the text.\n * @returns {string}\n */\n stringHelper(context, sectionText, helper) {\n // A string instruction is in the format:\n // key, component, params.\n\n let parts = sectionText.split(',');\n\n const key = parts.length > 0 ? parts.shift().trim() : '';\n const component = parts.length > 0 ? getNormalisedComponent(parts.shift().trim()) : '';\n let param = parts.length > 0 ? parts.join(',').trim() : '';\n\n if (param !== '') {\n // Allow variable expansion in the param part only.\n param = helper(param, context);\n }\n\n if (param.match(/^{\\s*\"/gm)) {\n // If it can't be parsed then the string is not a JSON format.\n try {\n const parsedParam = JSON.parse(param);\n // Handle non-exception-throwing cases, e.g. null, integer, boolean.\n if (parsedParam && typeof parsedParam === \"object\") {\n param = parsedParam;\n }\n } catch (err) {\n // This was probably not JSON.\n // Keep the error message visible but do not promote it because it may not be an error.\n window.console.warn(err.message);\n }\n }\n\n const index = this.requiredStrings.length;\n this.requiredStrings.push({\n key,\n component,\n param,\n });\n\n // The placeholder must not use {{}} as those can be misinterpreted by the engine.\n return `[[_s${index}]]`;\n }\n\n /**\n * String helper to render {{#cleanstr}}abd component { a : 'fish'}{{/cleanstr}}\n * into a get_string following by an HTML escape.\n *\n * @method cleanStringHelper\n * @private\n * @param {object} context The current mustache context.\n * @param {string} sectionText The text to parse the arguments from.\n * @param {function} helper Used to render subsections of the text.\n * @returns {string}\n */\n cleanStringHelper(context, sectionText, helper) {\n // We're going to use [[_cx]] format for clean strings, where x is a number.\n // Hence, replacing 's' with 'c' in the placeholder that stringHelper returns.\n return this\n .stringHelper(context, sectionText, helper)\n .replace(placeholderString, placeholderCleanedString);\n }\n\n /**\n * Quote helper used to wrap content in quotes, and escape all special JSON characters present in the content.\n *\n * @method quoteHelper\n * @private\n * @param {object} context The current mustache context.\n * @param {string} sectionText The text to parse the arguments from.\n * @param {function} helper Used to render subsections of the text.\n * @returns {string}\n */\n quoteHelper(context, sectionText, helper) {\n let content = helper(sectionText.trim(), context);\n\n // Escape the {{ and JSON encode.\n // This involves wrapping {{, and }} in change delimeter tags.\n content = JSON.stringify(content);\n content = content.replace(/([{}]{2,3})/g, '{{=<% %>=}}$1<%={{ }}=%>');\n return content;\n }\n\n /**\n * Shorten text helper to truncate text and append a trailing ellipsis.\n *\n * @method shortenTextHelper\n * @private\n * @param {object} context The current mustache context.\n * @param {string} sectionText The text to parse the arguments from.\n * @param {function} helper Used to render subsections of the text.\n * @returns {string}\n */\n shortenTextHelper(context, sectionText, helper) {\n // Non-greedy split on comma to grab section text into the length and\n // text parts.\n const parts = sectionText.match(/(.*?),(.*)/);\n\n // The length is the part matched in the first set of parethesis.\n const length = parts[1].trim();\n // The length is the part matched in the second set of parethesis.\n const text = parts[2].trim();\n const content = helper(text, context);\n return Truncate.truncate(content, {\n length,\n words: true,\n ellipsis: '...'\n });\n }\n\n /**\n * User date helper to render user dates from timestamps.\n *\n * @method userDateHelper\n * @private\n * @param {object} context The current mustache context.\n * @param {string} sectionText The text to parse the arguments from.\n * @param {function} helper Used to render subsections of the text.\n * @returns {string}\n */\n userDateHelper(context, sectionText, helper) {\n // Non-greedy split on comma to grab the timestamp and format.\n const parts = sectionText.match(/(.*?),(.*)/);\n\n const timestamp = helper(parts[1].trim(), context);\n const format = helper(parts[2].trim(), context);\n const index = this.requiredDates.length;\n\n this.requiredDates.push({\n timestamp: timestamp,\n format: format\n });\n\n return `[[_t_${index}]]`;\n }\n\n /**\n * Return a helper function to be added to the context for rendering the a\n * template.\n *\n * This will parse the provided text before giving it to the helper function\n * in order to remove any disallowed nested helpers to prevent one helper\n * from calling another.\n *\n * In particular to prevent the JS helper from being called from within another\n * helper because it can lead to security issues when the JS portion is user\n * provided.\n *\n * @param {function} helperFunction The helper function to add\n * @param {object} context The template context for the helper function\n * @returns {Function} To be set in the context\n */\n addHelperFunction(helperFunction, context) {\n return function() {\n return function(sectionText, helper) {\n // Override the disallowed helpers in the template context with\n // a function that returns an empty string for use when executing\n // other helpers. This is to prevent these helpers from being\n // executed as part of the rendering of another helper in order to\n // prevent any potential security issues.\n const originalHelpers = Renderer.disallowedNestedHelpers.reduce((carry, name) => {\n if (context.hasOwnProperty(name)) {\n carry[name] = context[name];\n }\n\n return carry;\n }, {});\n\n Renderer.disallowedNestedHelpers.forEach((helperName) => {\n context[helperName] = () => '';\n });\n\n // Execute the helper with the modified context that doesn't include\n // the disallowed nested helpers. This prevents the disallowed\n // helpers from being called from within other helpers.\n const result = helperFunction.apply(this, [context, sectionText, helper]);\n\n // Restore the original helper implementation in the context so that\n // any further rendering has access to them again.\n for (const name in originalHelpers) {\n context[name] = originalHelpers[name];\n }\n\n return result;\n }.bind(this);\n }.bind(this);\n }\n\n /**\n * Add some common helper functions to all context objects passed to templates.\n * These helpers match exactly the helpers available in php.\n *\n * @method addHelpers\n * @private\n * @param {Object} context Simple types used as the context for the template.\n * @param {String} themeName We set this multiple times, because there are async calls.\n */\n addHelpers(context, themeName) {\n this.currentThemeName = themeName;\n this.requiredStrings = [];\n this.requiredJS = [];\n context.uniqid = (Renderer.uniqInstances++);\n\n // Please note that these helpers _must_ not return a Promise.\n context.str = this.addHelperFunction(this.stringHelper, context);\n context.cleanstr = this.addHelperFunction(this.cleanStringHelper, context);\n context.pix = this.addHelperFunction(this.pixHelper, context);\n context.js = this.addHelperFunction(this.jsHelper, context);\n context.quote = this.addHelperFunction(this.quoteHelper, context);\n context.shortentext = this.addHelperFunction(this.shortenTextHelper, context);\n context.userdate = this.addHelperFunction(this.userDateHelper, context);\n context.globals = {config: config};\n context.currentTheme = themeName;\n }\n\n /**\n * Get all the JS blocks from the last rendered template.\n *\n * @method getJS\n * @private\n * @returns {string}\n */\n getJS() {\n return this.requiredJS.join(\";\\n\");\n }\n\n /**\n * Treat strings in content.\n *\n * The purpose of this method is to replace the placeholders found in a string\n * with the their respective translated strings.\n *\n * Previously we were relying on String.replace() but the complexity increased with\n * the numbers of strings to replace. Now we manually walk the string and stop at each\n * placeholder we find, only then we replace it. Most of the time we will\n * replace all the placeholders in a single run, at times we will need a few\n * more runs when placeholders are replaced with strings that contain placeholders\n * themselves.\n *\n * @param {String} content The content in which string placeholders are to be found.\n * @param {Map} stringMap The strings to replace with.\n * @returns {String} The treated content.\n */\n treatStringsInContent(content, stringMap) {\n // Placeholders are in the for [[_sX]] or [[_cX]] where X is the string index.\n const stringPattern = /(?\\[\\[_(?[cs])(?\\d+)\\]\\])/g;\n\n // A helpre to fetch the string for a given placeholder.\n const getUpdatedString = ({placeholder, stringType, stringIndex}) => {\n if (stringMap.has(placeholder)) {\n return stringMap.get(placeholder);\n }\n\n if (stringType === placeholderCleanedString) {\n // Attempt to find the unclean string and clean it. Store it for later use.\n const uncleanString = stringMap.get(`[[_s${stringIndex}]]`);\n if (uncleanString) {\n stringMap.set(placeholder, mustache.escape(uncleanString));\n return stringMap.get(placeholder);\n }\n }\n\n Log.debug(`Could not find string for pattern ${placeholder}`);\n return '';\n };\n\n // Find all placeholders in the content and replace them with their respective strings.\n let match;\n while ((match = stringPattern.exec(content)) !== null) {\n let updatedContent = content.slice(0, match.index);\n updatedContent += getUpdatedString(match.groups);\n updatedContent += content.slice(match.index + match.groups.placeholder.length);\n\n content = updatedContent;\n }\n\n return content;\n }\n\n /**\n * Treat strings in content.\n *\n * The purpose of this method is to replace the date placeholders found in the\n * content with the their respective translated dates.\n *\n * @param {String} content The content in which string placeholders are to be found.\n * @param {Array} dates The dates to replace with.\n * @returns {String} The treated content.\n */\n treatDatesInContent(content, dates) {\n dates.forEach((date, index) => {\n content = content.replace(\n new RegExp(`\\\\[\\\\[_t_${index}\\\\]\\\\]`, 'g'),\n date,\n );\n });\n\n return content;\n }\n\n /**\n * Render a template and then call the callback with the result.\n *\n * @method doRender\n * @private\n * @param {string|Promise} templateSourcePromise The mustache template to render.\n * @param {Object} context Simple types used as the context for the template.\n * @param {String} themeName Name of the current theme.\n * @returns {Promise>} The rendered HTML and JS.\n */\n async doRender(templateSourcePromise, context, themeName) {\n this.currentThemeName = themeName;\n const iconTemplate = this.iconSystem.getTemplateName();\n\n const pendingPromise = new Pending('core/templates:doRender');\n const [templateSource] = await Promise.all([\n templateSourcePromise,\n Renderer.getLoader().getTemplate(iconTemplate, themeName),\n ]);\n\n this.addHelpers(context, themeName);\n\n // Render the template.\n const renderedContent = await mustache.render(\n templateSource,\n context,\n // Note: The third parameter is a function that will be called to process partials.\n (partialName) => Renderer.getLoader().partialHelper(partialName, themeName),\n );\n\n const {html, js} = await this.processRenderedContent(renderedContent);\n\n pendingPromise.resolve();\n return {html, js};\n }\n\n /**\n * Process the rendered content, treating any strings and applying and helper strings, dates, etc.\n * @param {string} renderedContent\n * @returns {Promise>} The rendered HTML and JS.\n */\n async processRenderedContent(renderedContent) {\n let html = renderedContent.trim();\n let js = this.getJS();\n\n if (this.requiredStrings.length > 0) {\n // Fetch the strings into a new Map using the placeholder as an index.\n // Note: We only fetch the unclean version. Cleaning of strings happens lazily in treatStringsInContent.\n const stringMap = new Map(\n (await getStrings(this.requiredStrings)).map((string, index) => (\n [`[[_s${index}]]`, string]\n ))\n );\n\n // Make sure string substitutions are done for the userdate\n // values as well.\n this.requiredDates = this.requiredDates.map(function(date) {\n return {\n timestamp: this.treatStringsInContent(date.timestamp, stringMap),\n format: this.treatStringsInContent(date.format, stringMap)\n };\n }.bind(this));\n\n // Why do we not do another call the render here?\n //\n // Because that would expose DOS holes. E.g.\n // I create an assignment called \"{{fish\" which\n // would get inserted in the template in the first pass\n // and cause the template to die on the second pass (unbalanced).\n html = this.treatStringsInContent(html, stringMap);\n js = this.treatStringsInContent(js, stringMap);\n }\n\n // This has to happen after the strings replacement because you can\n // use the string helper in content for the user date helper.\n if (this.requiredDates.length > 0) {\n const dates = await UserDate.get(this.requiredDates);\n html = this.treatDatesInContent(html, dates);\n js = this.treatDatesInContent(js, dates);\n }\n\n return {html, js};\n }\n\n /**\n * Load a template and call doRender on it.\n *\n * @method render\n * @private\n * @param {string} templateName - should consist of the component and the name of the template like this:\n * core/menu (lib/templates/menu.mustache) or\n * tool_bananas/yellow (admin/tool/bananas/templates/yellow.mustache)\n * @param {Object} [context={}] - Could be array, string or simple value for the context of the template.\n * @param {string} [themeName] - Name of the current theme.\n * @returns {Promise} Native promise object resolved when the template has been rendered.}\n */\n async render(\n templateName,\n context = {},\n themeName = config.theme,\n ) {\n this.currentThemeName = themeName;\n\n // Preload the module to do the icon rendering based on the theme iconsystem.\n await this.setupIconSystem();\n\n const templateSource = Renderer.getLoader().cachePartials(templateName, themeName);\n return this.doRender(templateSource, context, themeName);\n }\n}\n"],"names":["Renderer","constructor","requiredStrings","requiredJS","requiredDates","currentThemeName","loader","this","key","component","title","setupIconSystem","template","getLoader","getTemplate","iconSystem","getTemplateName","renderIcon","IconSystem","instance","pixHelper","context","sectionText","helper","parts","split","text","length","shift","trim","join","Loader","templateName","searchKey","getSearchKey","getTemplateFromCache","replace","jsHelper","push","stringHelper","param","match","parsedParam","JSON","parse","err","window","console","warn","message","index","cleanStringHelper","quoteHelper","content","stringify","shortenTextHelper","Truncate","truncate","words","ellipsis","userDateHelper","timestamp","format","addHelperFunction","helperFunction","originalHelpers","disallowedNestedHelpers","reduce","carry","name","hasOwnProperty","forEach","helperName","result","apply","bind","addHelpers","themeName","uniqid","uniqInstances","str","cleanstr","pix","js","quote","shortentext","userdate","globals","config","currentTheme","getJS","treatStringsInContent","stringMap","stringPattern","getUpdatedString","_ref","placeholder","stringType","stringIndex","has","get","uncleanString","set","mustache","escape","Log","debug","exec","updatedContent","slice","groups","treatDatesInContent","dates","date","RegExp","templateSourcePromise","iconTemplate","pendingPromise","Pending","templateSource","Promise","all","renderedContent","render","partialName","partialHelper","html","processRenderedContent","resolve","Map","map","string","UserDate","theme","cachePartials","doRender"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"renderer.min.js","sources":["../../../src/local/templates/renderer.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\nimport * as Log from 'core/log';\nimport * as Truncate from 'core/truncate';\nimport * as UserDate from 'core/user_date';\nimport Pending from 'core/pending';\nimport {getStrings} from 'core/str';\nimport IconSystem from 'core/icon_system';\nimport config from 'core/config';\nimport mustache from 'core/mustache';\nimport Loader from './loader';\nimport {getNormalisedComponent} from 'core/utils';\n\n/** @var {string} The placeholder character used for standard strings (unclean) */\nconst placeholderString = 's';\n\n/** @var {string} The placeholder character used for cleaned strings */\nconst placeholderCleanedString = 'c';\n\n/**\n * Template Renderer Class.\n *\n * Note: This class is not intended to be instantiated directly. Instead, use the core/templates module.\n *\n * @module core/local/templates/renderer\n * @copyright 2023 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.3\n */\nexport default class Renderer {\n /** @var {string[]} requiredStrings - Collection of strings found during the rendering of one template */\n requiredStrings = null;\n\n /** @var {object[]} requiredDates - Collection of dates found during the rendering of one template */\n requiredDates = [];\n\n /** @var {string[]} requiredJS - Collection of js blocks found during the rendering of one template */\n requiredJS = null;\n\n /** @var {String} themeName for the current render */\n currentThemeName = '';\n\n /** @var {Number} uniqInstances Count of times this constructor has been called. */\n static uniqInstances = 0;\n\n /** @var {Object[]} loadTemplateBuffer - List of templates to be loaded */\n static loadTemplateBuffer = [];\n\n /** @var {Bool} isLoadingTemplates - Whether templates are currently being loaded */\n static isLoadingTemplates = false;\n\n /** @var {Object} iconSystem - Object extending core/iconsystem */\n iconSystem = null;\n\n /** @var {Array} disallowedNestedHelpers - List of helpers that can't be called within other helpers */\n static disallowedNestedHelpers = [\n 'js',\n ];\n\n /** @var {String[]} templateCache - Cache of already loaded template strings */\n static templateCache = {};\n\n /**\n * Cache of already loaded template promises.\n *\n * @type {Promise[]}\n * @static\n * @private\n */\n static templatePromises = {};\n\n /**\n * The loader used to fetch templates.\n * @type {Loader}\n * @static\n * @private\n */\n static loader = Loader;\n\n /**\n * Constructor\n *\n * Each call to templates.render gets it's own instance of this class.\n */\n constructor() {\n this.requiredStrings = [];\n this.requiredJS = [];\n this.requiredDates = [];\n this.currentThemeName = '';\n }\n\n /**\n * Set the template loader to use for all Template renderers.\n *\n * @param {Loader} loader\n */\n static setLoader(loader) {\n this.loader = loader;\n }\n\n /**\n * Get the Loader used to fetch templates.\n *\n * @returns {Loader}\n */\n static getLoader() {\n return this.loader;\n }\n\n /**\n * Render a single image icon.\n *\n * @method renderIcon\n * @private\n * @param {string} key The icon key.\n * @param {string} component The component name.\n * @param {string} title The icon title\n * @returns {Promise}\n */\n async renderIcon(key, component, title) {\n // Preload the module to do the icon rendering based on the theme iconsystem.\n component = getNormalisedComponent(component);\n\n await this.setupIconSystem();\n const template = await Renderer.getLoader().getTemplate(\n this.iconSystem.getTemplateName(),\n this.currentThemeName,\n );\n\n return this.iconSystem.renderIcon(\n key,\n component,\n title,\n template\n );\n }\n\n /**\n * Helper to set up the icon system.\n */\n async setupIconSystem() {\n if (!this.iconSystem) {\n this.iconSystem = await IconSystem.instance();\n }\n\n return this.iconSystem;\n }\n\n /**\n * Render image icons.\n *\n * @method pixHelper\n * @private\n * @param {object} context The mustache context\n * @param {string} sectionText The text to parse arguments from.\n * @param {function} helper Used to render the alt attribute of the text.\n * @returns {string}\n */\n pixHelper(context, sectionText, helper) {\n const parts = sectionText.split(',');\n let key = '';\n let component = '';\n let text = '';\n\n if (parts.length > 0) {\n key = helper(parts.shift().trim(), context);\n }\n if (parts.length > 0) {\n component = helper(parts.shift().trim(), context);\n }\n if (parts.length > 0) {\n text = helper(parts.join(',').trim(), context);\n }\n\n // Note: We cannot use Promises in Mustache helpers.\n // We must fetch straight from the Loader cache.\n // The Loader cache is statically defined on the Loader class and should be used by all children.\n const Loader = Renderer.getLoader();\n const templateName = this.iconSystem.getTemplateName();\n const searchKey = Loader.getSearchKey(this.currentThemeName, templateName);\n const template = Loader.getTemplateFromCache(searchKey);\n\n component = getNormalisedComponent(component);\n\n // The key might have been escaped by the JS Mustache engine which\n // converts forward slashes to HTML entities. Let us undo that here.\n key = key.replace(///gi, '/');\n\n return this.iconSystem.renderIcon(\n key,\n component,\n text,\n template\n );\n }\n\n /**\n * Render blocks of javascript and save them in an array.\n *\n * @method jsHelper\n * @private\n * @param {object} context The current mustache context.\n * @param {string} sectionText The text to save as a js block.\n * @param {function} helper Used to render the block.\n * @returns {string}\n */\n jsHelper(context, sectionText, helper) {\n this.requiredJS.push(helper(sectionText, context));\n return '';\n }\n\n /**\n * String helper used to render {{#str}}abd component { a : 'fish'}{{/str}}\n * into a get_string call.\n *\n * @method stringHelper\n * @private\n * @param {object} context The current mustache context.\n * @param {string} sectionText The text to parse the arguments from.\n * @param {function} helper Used to render subsections of the text.\n * @returns {string}\n */\n stringHelper(context, sectionText, helper) {\n // A string instruction is in the format:\n // key, component, params.\n\n let parts = sectionText.split(',');\n\n const key = parts.length > 0 ? parts.shift().trim() : '';\n const component = parts.length > 0 ? getNormalisedComponent(parts.shift().trim()) : '';\n let param = parts.length > 0 ? parts.join(',').trim() : '';\n\n if (param !== '') {\n // Allow variable expansion in the param part only.\n param = helper(param, context);\n }\n\n if (param.match(/^{\\s*\"/gm)) {\n // If it can't be parsed then the string is not a JSON format.\n try {\n const parsedParam = JSON.parse(param);\n // Handle non-exception-throwing cases, e.g. null, integer, boolean.\n if (parsedParam && typeof parsedParam === \"object\") {\n param = parsedParam;\n }\n } catch (err) {\n // This was probably not JSON.\n // Keep the error message visible but do not promote it because it may not be an error.\n window.console.warn(err.message);\n }\n }\n\n const index = this.requiredStrings.length;\n this.requiredStrings.push({\n key,\n component,\n param,\n });\n\n // The placeholder must not use {{}} as those can be misinterpreted by the engine.\n return `[[_s${index}]]`;\n }\n\n /**\n * String helper to render {{#cleanstr}}abd component { a : 'fish'}{{/cleanstr}}\n * into a get_string following by an HTML escape.\n *\n * @method cleanStringHelper\n * @private\n * @param {object} context The current mustache context.\n * @param {string} sectionText The text to parse the arguments from.\n * @param {function} helper Used to render subsections of the text.\n * @returns {string}\n */\n cleanStringHelper(context, sectionText, helper) {\n // We're going to use [[_cx]] format for clean strings, where x is a number.\n // Hence, replacing 's' with 'c' in the placeholder that stringHelper returns.\n return this\n .stringHelper(context, sectionText, helper)\n .replace(placeholderString, placeholderCleanedString);\n }\n\n /**\n * Quote helper used to wrap content in quotes, and escape all special JSON characters present in the content.\n *\n * @method quoteHelper\n * @private\n * @param {object} context The current mustache context.\n * @param {string} sectionText The text to parse the arguments from.\n * @param {function} helper Used to render subsections of the text.\n * @returns {string}\n */\n quoteHelper(context, sectionText, helper) {\n let content = helper(sectionText.trim(), context);\n\n // Escape the {{ and JSON encode.\n // This involves wrapping {{, and }} in change delimeter tags.\n content = JSON.stringify(content);\n content = content.replace(/([{}]{2,3})/g, '{{=<% %>=}}$1<%={{ }}=%>');\n return content;\n }\n\n /**\n * Shorten text helper to truncate text and append a trailing ellipsis.\n *\n * @method shortenTextHelper\n * @private\n * @param {object} context The current mustache context.\n * @param {string} sectionText The text to parse the arguments from.\n * @param {function} helper Used to render subsections of the text.\n * @returns {string}\n */\n shortenTextHelper(context, sectionText, helper) {\n // Non-greedy split on comma to grab section text into the length and\n // text parts.\n const parts = sectionText.match(/(.*?),(.*)/);\n\n // The length is the part matched in the first set of parethesis.\n const length = parts[1].trim();\n // The length is the part matched in the second set of parethesis.\n const text = parts[2].trim();\n const content = helper(text, context);\n return Truncate.truncate(content, {\n length,\n words: true,\n ellipsis: '...'\n });\n }\n\n /**\n * User date helper to render user dates from timestamps.\n *\n * @method userDateHelper\n * @private\n * @param {object} context The current mustache context.\n * @param {string} sectionText The text to parse the arguments from.\n * @param {function} helper Used to render subsections of the text.\n * @returns {string}\n */\n userDateHelper(context, sectionText, helper) {\n // Non-greedy split on comma to grab the timestamp and format.\n const parts = sectionText.match(/(.*?),(.*)/);\n\n const timestamp = helper(parts[1].trim(), context);\n const format = helper(parts[2].trim(), context);\n const index = this.requiredDates.length;\n\n this.requiredDates.push({\n timestamp: timestamp,\n format: format\n });\n\n return `[[_t_${index}]]`;\n }\n\n /**\n * Return a helper function to be added to the context for rendering the a\n * template.\n *\n * This will parse the provided text before giving it to the helper function\n * in order to remove any disallowed nested helpers to prevent one helper\n * from calling another.\n *\n * In particular to prevent the JS helper from being called from within another\n * helper because it can lead to security issues when the JS portion is user\n * provided.\n *\n * @param {function} helperFunction The helper function to add\n * @param {object} context The template context for the helper function\n * @returns {Function} To be set in the context\n */\n addHelperFunction(helperFunction, context) {\n return function() {\n return function(sectionText, helper) {\n // Override the disallowed helpers in the template context with\n // a function that returns an empty string for use when executing\n // other helpers. This is to prevent these helpers from being\n // executed as part of the rendering of another helper in order to\n // prevent any potential security issues.\n const originalHelpers = Renderer.disallowedNestedHelpers.reduce((carry, name) => {\n if (context.hasOwnProperty(name)) {\n carry[name] = context[name];\n }\n\n return carry;\n }, {});\n\n Renderer.disallowedNestedHelpers.forEach((helperName) => {\n context[helperName] = () => '';\n });\n\n // Execute the helper with the modified context that doesn't include\n // the disallowed nested helpers. This prevents the disallowed\n // helpers from being called from within other helpers.\n const result = helperFunction.apply(this, [context, sectionText, helper]);\n\n // Restore the original helper implementation in the context so that\n // any further rendering has access to them again.\n for (const name in originalHelpers) {\n context[name] = originalHelpers[name];\n }\n\n return result;\n }.bind(this);\n }.bind(this);\n }\n\n /**\n * Add some common helper functions to all context objects passed to templates.\n * These helpers match exactly the helpers available in php.\n *\n * @method addHelpers\n * @private\n * @param {Object} context Simple types used as the context for the template.\n * @param {String} themeName We set this multiple times, because there are async calls.\n */\n addHelpers(context, themeName) {\n this.currentThemeName = themeName;\n this.requiredStrings = [];\n this.requiredJS = [];\n context.uniqid = (Renderer.uniqInstances++);\n\n // Please note that these helpers _must_ not return a Promise.\n context.str = this.addHelperFunction(this.stringHelper, context);\n context.cleanstr = this.addHelperFunction(this.cleanStringHelper, context);\n context.pix = this.addHelperFunction(this.pixHelper, context);\n context.js = this.addHelperFunction(this.jsHelper, context);\n context.quote = this.addHelperFunction(this.quoteHelper, context);\n context.shortentext = this.addHelperFunction(this.shortenTextHelper, context);\n context.userdate = this.addHelperFunction(this.userDateHelper, context);\n context.globals = {config: config};\n context.currentTheme = themeName;\n }\n\n /**\n * Get all the JS blocks from the last rendered template.\n *\n * @method getJS\n * @private\n * @returns {string}\n */\n getJS() {\n return this.requiredJS.join(\";\\n\");\n }\n\n /**\n * Treat strings in content.\n *\n * The purpose of this method is to replace the placeholders found in a string\n * with the their respective translated strings.\n *\n * Previously we were relying on String.replace() but the complexity increased with\n * the numbers of strings to replace. Now we manually walk the string and stop at each\n * placeholder we find, only then we replace it. Most of the time we will\n * replace all the placeholders in a single run, at times we will need a few\n * more runs when placeholders are replaced with strings that contain placeholders\n * themselves.\n *\n * @param {String} content The content in which string placeholders are to be found.\n * @param {Map} stringMap The strings to replace with.\n * @returns {String} The treated content.\n */\n treatStringsInContent(content, stringMap) {\n // Placeholders are in the for [[_sX]] or [[_cX]] where X is the string index.\n const stringPattern = /(?\\[\\[_(?[cs])(?\\d+)\\]\\])/g;\n\n // A helpre to fetch the string for a given placeholder.\n const getUpdatedString = ({placeholder, stringType, stringIndex}) => {\n if (stringMap.has(placeholder)) {\n return stringMap.get(placeholder);\n }\n\n if (stringType === placeholderCleanedString) {\n // Attempt to find the unclean string and clean it. Store it for later use.\n const uncleanString = stringMap.get(`[[_s${stringIndex}]]`);\n if (uncleanString) {\n stringMap.set(placeholder, mustache.escape(uncleanString));\n return stringMap.get(placeholder);\n }\n }\n\n Log.debug(`Could not find string for pattern ${placeholder}`);\n return '';\n };\n\n // Find all placeholders in the content and replace them with their respective strings.\n let match;\n while ((match = stringPattern.exec(content)) !== null) {\n let updatedContent = content.slice(0, match.index);\n updatedContent += getUpdatedString(match.groups);\n updatedContent += content.slice(match.index + match.groups.placeholder.length);\n\n content = updatedContent;\n }\n\n return content;\n }\n\n /**\n * Treat strings in content.\n *\n * The purpose of this method is to replace the date placeholders found in the\n * content with the their respective translated dates.\n *\n * @param {String} content The content in which string placeholders are to be found.\n * @param {Array} dates The dates to replace with.\n * @returns {String} The treated content.\n */\n treatDatesInContent(content, dates) {\n dates.forEach((date, index) => {\n content = content.replace(\n new RegExp(`\\\\[\\\\[_t_${index}\\\\]\\\\]`, 'g'),\n date,\n );\n });\n\n return content;\n }\n\n /**\n * Render a template and then call the callback with the result.\n *\n * @method doRender\n * @private\n * @param {string|Promise} templateSourcePromise The mustache template to render.\n * @param {Object} context Simple types used as the context for the template.\n * @param {String} themeName Name of the current theme.\n * @returns {Promise>} The rendered HTML and JS.\n */\n async doRender(templateSourcePromise, context, themeName) {\n this.currentThemeName = themeName;\n const iconTemplate = this.iconSystem.getTemplateName();\n\n const pendingPromise = new Pending('core/templates:doRender');\n const [templateSource] = await Promise.all([\n templateSourcePromise,\n Renderer.getLoader().getTemplate(iconTemplate, themeName),\n ]);\n\n this.addHelpers(context, themeName);\n\n // Render the template.\n const renderedContent = await mustache.render(\n templateSource,\n context,\n // Note: The third parameter is a function that will be called to process partials.\n (partialName) => Renderer.getLoader().partialHelper(partialName, themeName),\n );\n\n const {html, js} = await this.processRenderedContent(renderedContent);\n\n pendingPromise.resolve();\n return {html, js};\n }\n\n /**\n * Process the rendered content, treating any strings and applying and helper strings, dates, etc.\n * @param {string} renderedContent\n * @returns {Promise>} The rendered HTML and JS.\n */\n async processRenderedContent(renderedContent) {\n let html = renderedContent.trim();\n let js = this.getJS();\n\n if (this.requiredStrings.length > 0) {\n // Fetch the strings into a new Map using the placeholder as an index.\n // Note: We only fetch the unclean version. Cleaning of strings happens lazily in treatStringsInContent.\n const stringMap = new Map(\n (await getStrings(this.requiredStrings)).map((string, index) => (\n [`[[_s${index}]]`, string]\n ))\n );\n\n // Make sure string substitutions are done for the userdate\n // values as well.\n this.requiredDates = this.requiredDates.map(function(date) {\n return {\n timestamp: this.treatStringsInContent(date.timestamp, stringMap),\n format: this.treatStringsInContent(date.format, stringMap)\n };\n }.bind(this));\n\n // Why do we not do another call the render here?\n //\n // Because that would expose DOS holes. E.g.\n // I create an assignment called \"{{fish\" which\n // would get inserted in the template in the first pass\n // and cause the template to die on the second pass (unbalanced).\n html = this.treatStringsInContent(html, stringMap);\n js = this.treatStringsInContent(js, stringMap);\n }\n\n // This has to happen after the strings replacement because you can\n // use the string helper in content for the user date helper.\n if (this.requiredDates.length > 0) {\n const dates = await UserDate.get(this.requiredDates);\n html = this.treatDatesInContent(html, dates);\n js = this.treatDatesInContent(js, dates);\n }\n\n return {html, js};\n }\n\n /**\n * Load a template and call doRender on it.\n *\n * @method render\n * @private\n * @param {string} templateName - should consist of the component and the name of the template like this:\n * core/menu (lib/templates/menu.mustache) or\n * tool_bananas/yellow (admin/tool/bananas/templates/yellow.mustache)\n * @param {Object} [context={}] - Could be array, string or simple value for the context of the template.\n * @param {string} [themeName] - Name of the current theme.\n * @returns {Promise} Native promise object resolved when the template has been rendered.}\n */\n async render(\n templateName,\n context = {},\n themeName = config.theme,\n ) {\n this.currentThemeName = themeName;\n\n // Preload the module to do the icon rendering based on the theme iconsystem.\n await this.setupIconSystem();\n\n const templateSource = Renderer.getLoader().cachePartials(templateName, themeName);\n return this.doRender(templateSource, context, themeName);\n }\n}\n"],"names":["Renderer","constructor","requiredStrings","requiredJS","requiredDates","currentThemeName","loader","this","key","component","title","setupIconSystem","template","getLoader","getTemplate","iconSystem","getTemplateName","renderIcon","IconSystem","instance","pixHelper","context","sectionText","helper","parts","split","text","length","shift","trim","join","Loader","templateName","searchKey","getSearchKey","getTemplateFromCache","replace","jsHelper","push","stringHelper","param","match","parsedParam","JSON","parse","err","window","console","warn","message","index","cleanStringHelper","quoteHelper","content","stringify","shortenTextHelper","Truncate","truncate","words","ellipsis","userDateHelper","timestamp","format","addHelperFunction","helperFunction","originalHelpers","disallowedNestedHelpers","reduce","carry","name","hasOwnProperty","forEach","helperName","result","apply","bind","addHelpers","themeName","uniqid","uniqInstances","str","cleanstr","pix","js","quote","shortentext","userdate","globals","config","currentTheme","getJS","treatStringsInContent","stringMap","stringPattern","getUpdatedString","_ref","placeholder","stringType","stringIndex","has","get","uncleanString","set","mustache","escape","Log","debug","exec","updatedContent","slice","groups","treatDatesInContent","dates","date","RegExp","templateSourcePromise","iconTemplate","pendingPromise","Pending","templateSource","Promise","all","renderedContent","render","partialName","partialHelper","html","processRenderedContent","resolve","Map","map","string","UserDate","theme","cachePartials","doRender"],"mappings":"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"} \ No newline at end of file diff --git a/lib/amd/build/moodlenet/oauth2callback.min.js b/lib/amd/build/moodlenet/oauth2callback.min.js index d7792185dfe..12c3eb6ce11 100644 --- a/lib/amd/build/moodlenet/oauth2callback.min.js +++ b/lib/amd/build/moodlenet/oauth2callback.min.js @@ -6,6 +6,6 @@ define("core/moodlenet/oauth2callback",["exports","core/prefetch","core/notifica * @copyright 2023 Huong Nguyen * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 4.2 - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_prefetch=(obj=_prefetch)&&obj.__esModule?obj:{default:obj};_exports.init=(error,errorDescription)=>{_prefetch.default.prefetchStrings("moodle",["moodlenet:sharefailtitle","error"]),((error,errorDescription)=>{window.opener?(window.opener.moodleNetAuthorize(error,errorDescription),setTimeout((()=>{window.close()}),300)):(0,_notification.alert)((0,_str.get_string)("error","moodle"),(0,_str.get_string)("moodlenet:sharefailtitle","moodle"))})(error,errorDescription)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_prefetch=(obj=_prefetch)&&obj.__esModule?obj:{default:obj};_exports.init=(error,errorDescription)=>{_prefetch.default.prefetchStrings("moodle",["moodlenet:sharefailtitle","error"]),((error,errorDescription)=>{window.opener?(window.opener.moodleNetAuthorize(error,errorDescription),setTimeout((()=>{window.close()}),300)):(0,_notification.alert)((0,_str.getString)("error","moodle"),(0,_str.getString)("moodlenet:sharefailtitle","moodle"))})(error,errorDescription)}})); //# sourceMappingURL=oauth2callback.min.js.map \ No newline at end of file diff --git a/lib/amd/build/moodlenet/oauth2callback.min.js.map b/lib/amd/build/moodlenet/oauth2callback.min.js.map index 56172ac9cb6..9c09da0bbd2 100644 --- a/lib/amd/build/moodlenet/oauth2callback.min.js.map +++ b/lib/amd/build/moodlenet/oauth2callback.min.js.map @@ -1 +1 @@ -{"version":3,"file":"oauth2callback.min.js","sources":["../../src/moodlenet/oauth2callback.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 * A module to handle the OAuth2 callback for MoodleNet.\n *\n * @module core/moodlenet/oauth2callback\n * @copyright 2023 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.2\n */\n\nimport Prefetch from \"core/prefetch\";\nimport {alert} from 'core/notification';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Handle the OAuth2 callback for MoodleNet.\n *\n * @param {String} error Error\n * @param {String} errorDescription Error description\n */\nconst handleCallback = (error, errorDescription) => {\n if (window.opener) {\n // Call the MoodleNet Authorization again in the opener window.\n window.opener.moodleNetAuthorize(error, errorDescription);\n // Close the authorization popup.\n // We need to use setTimeout here because the Behat 'I press \"x\" and switch to main window' step expects the popup to still\n // be visible after clicking the button. Otherwise, it will throw a webdriver error.\n setTimeout(() => {\n // Close the authorization popup.\n window.close();\n }, 300);\n } else {\n alert(getString('error', 'moodle'), getString('moodlenet:sharefailtitle', 'moodle'));\n }\n};\n\n/**\n * Initialize.\n *\n * @param {String} error Error\n * @param {String} errorDescription Error description\n */\nexport const init = (error, errorDescription) => {\n Prefetch.prefetchStrings('moodle', ['moodlenet:sharefailtitle', 'error']);\n handleCallback(error, errorDescription);\n};\n"],"names":["error","errorDescription","prefetchStrings","window","opener","moodleNetAuthorize","setTimeout","close","handleCallback"],"mappings":";;;;;;;;4JAwDoB,CAACA,MAAOC,sCACfC,gBAAgB,SAAU,CAAC,2BAA4B,UAvB7C,EAACF,MAAOC,oBACvBE,OAAOC,QAEPD,OAAOC,OAAOC,mBAAmBL,MAAOC,kBAIxCK,YAAW,KAEPH,OAAOI,UACR,+BAEG,mBAAU,QAAS,WAAW,mBAAU,2BAA4B,YAY9EC,CAAeR,MAAOC"} \ No newline at end of file +{"version":3,"file":"oauth2callback.min.js","sources":["../../src/moodlenet/oauth2callback.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 * A module to handle the OAuth2 callback for MoodleNet.\n *\n * @module core/moodlenet/oauth2callback\n * @copyright 2023 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.2\n */\n\nimport Prefetch from \"core/prefetch\";\nimport {alert} from 'core/notification';\nimport {getString} from 'core/str';\n\n/**\n * Handle the OAuth2 callback for MoodleNet.\n *\n * @param {String} error Error\n * @param {String} errorDescription Error description\n */\nconst handleCallback = (error, errorDescription) => {\n if (window.opener) {\n // Call the MoodleNet Authorization again in the opener window.\n window.opener.moodleNetAuthorize(error, errorDescription);\n // Close the authorization popup.\n // We need to use setTimeout here because the Behat 'I press \"x\" and switch to main window' step expects the popup to still\n // be visible after clicking the button. Otherwise, it will throw a webdriver error.\n setTimeout(() => {\n // Close the authorization popup.\n window.close();\n }, 300);\n } else {\n alert(getString('error', 'moodle'), getString('moodlenet:sharefailtitle', 'moodle'));\n }\n};\n\n/**\n * Initialize.\n *\n * @param {String} error Error\n * @param {String} errorDescription Error description\n */\nexport const init = (error, errorDescription) => {\n Prefetch.prefetchStrings('moodle', ['moodlenet:sharefailtitle', 'error']);\n handleCallback(error, errorDescription);\n};\n"],"names":["error","errorDescription","prefetchStrings","window","opener","moodleNetAuthorize","setTimeout","close","handleCallback"],"mappings":";;;;;;;;4JAwDoB,CAACA,MAAOC,sCACfC,gBAAgB,SAAU,CAAC,2BAA4B,UAvB7C,EAACF,MAAOC,oBACvBE,OAAOC,QAEPD,OAAOC,OAAOC,mBAAmBL,MAAOC,kBAIxCK,YAAW,KAEPH,OAAOI,UACR,+BAEG,kBAAU,QAAS,WAAW,kBAAU,2BAA4B,YAY9EC,CAAeR,MAAOC"} \ No newline at end of file diff --git a/lib/amd/build/moodlenet/send_resource.min.js b/lib/amd/build/moodlenet/send_resource.min.js index d4279abd4fd..6b3438b724a 100644 --- a/lib/amd/build/moodlenet/send_resource.min.js +++ b/lib/amd/build/moodlenet/send_resource.min.js @@ -6,6 +6,6 @@ define("core/moodlenet/send_resource",["exports","core/config","core/modal_facto * @copyright 2023 Huong Nguyen * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 4.2 - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_config=_interopRequireDefault(_config),_modal_factory=_interopRequireDefault(_modal_factory),_prefetch=_interopRequireDefault(_prefetch),Templates=_interopRequireWildcard(Templates),MoodleNetService=_interopRequireWildcard(MoodleNetService),_send_activity_modal=_interopRequireDefault(_send_activity_modal);let currentModal,siteSupportUrl,issuerId,courseId,resourceId,shareFormat,type,listenersRegistered=!1;const responseFromMoodleNet=function(status){let resourceUrl=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";const $modal=currentModal.getModal(),modal=$modal[0];modal.querySelector(".modal-header").classList.add("no-border"),currentModal.setBody(Templates.render("core/moodlenet/send_activity_modal_done",{success:status,sitesupporturl:siteSupportUrl})),status&&(currentModal.setFooter(Templates.render("core/moodlenet/send_activity_modal_footer_view",{resourseurl:resourceUrl})),currentModal.showFooter())},handleAuthorization=issuerId=>MoodleNetService.authorizationCheck(issuerId,courseId).then((async data=>data.status?((issuerId,resourceId,shareFormat)=>{const modal=currentModal.getModal()[0];let infoPromise;modal.querySelector(".modal-header").classList.remove("no-border"),modal.querySelector(".modal-header").classList.add("no-header-text"),currentModal.setBody(Templates.render("core/moodlenet/send_activity_modal_packaging",{})),currentModal.hideFooter(),"activity"===type?infoPromise=MoodleNetService.sendActivity(issuerId,resourceId,shareFormat):"course"===type&&(infoPromise=MoodleNetService.sendCourse(issuerId,resourceId,shareFormat)),infoPromise.then((async data=>{const status=data.status,resourceUrl=data.resourceurl;return responseFromMoodleNet(status,resourceUrl)})).catch(_notification.exception)})(issuerId,resourceId,shareFormat):(window.moodleNetAuthorize=(error,errorDescription)=>{""===error?handleAuthorization(issuerId):"access_denied"!==error&&(0,_notification.alert)("Authorization error","Error: "+error+"

Error description: "+errorDescription,"Cancel")},window.open(data.loginurl,"moodlenet_auth","location=0,status=0,width=".concat(550,",height=").concat(550,",scrollbars=yes"))))).catch(_notification.exception),registerEventListeners=()=>{document.addEventListener("click",(e=>{const shareAction=e.target.closest('[data-action="sendtomoodlenet"]'),sendAction=e.target.closest('.moodlenet-action-buttons [data-action="share"]');if(shareAction){e.preventDefault(),type=shareAction.getAttribute("data-type");const shareType=shareAction.getAttribute("data-sharetype"),resourceId=_config.default.contextInstanceId;let infoPromise;"activity"===type?infoPromise=MoodleNetService.getActivityInformation(resourceId):"course"===type&&(infoPromise=MoodleNetService.getCourseInformation(resourceId)),infoPromise.then((async data=>{(async(data,shareType)=>{if(data.status)return siteSupportUrl=data.supportpageurl,issuerId=data.issuerid,_modal_factory.default.create({type:_send_activity_modal.default.TYPE,large:!0,templateContext:{activitytype:data.type,activityname:data.name,sharetype:await(0,_str.get_string)("moodlenet:sharetype"+shareType,"moodle"),server:data.server}}).then((modal=>(currentModal=modal,modal.show(),modal))).catch(_notification.exception);return(0,_notification.addNotification)({message:data.warnings[0].message,type:"error"})})(data,shareType).catch(_notification.exception)})).catch(_notification.exception)}sendAction&&(e.preventDefault(),courseId=_config.default.courseId,resourceId=_config.default.contextInstanceId,shareFormat=0,handleAuthorization(issuerId))}))};_exports.init=()=>{listenersRegistered||(_prefetch.default.prefetchTemplates(["core/moodlenet/send_activity_modal_base","core/moodlenet/send_activity_modal_packaging","core/moodlenet/send_activity_modal_done","core/moodlenet/send_activity_modal_footer_view"]),registerEventListeners(),listenersRegistered=!0)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_config=_interopRequireDefault(_config),_modal_factory=_interopRequireDefault(_modal_factory),_prefetch=_interopRequireDefault(_prefetch),Templates=_interopRequireWildcard(Templates),MoodleNetService=_interopRequireWildcard(MoodleNetService),_send_activity_modal=_interopRequireDefault(_send_activity_modal);let currentModal,siteSupportUrl,issuerId,courseId,resourceId,shareFormat,type,listenersRegistered=!1;const responseFromMoodleNet=function(status){let resourceUrl=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";const $modal=currentModal.getModal(),modal=$modal[0];modal.querySelector(".modal-header").classList.add("no-border"),currentModal.setBody(Templates.render("core/moodlenet/send_activity_modal_done",{success:status,sitesupporturl:siteSupportUrl})),status&&(currentModal.setFooter(Templates.render("core/moodlenet/send_activity_modal_footer_view",{resourseurl:resourceUrl})),currentModal.showFooter())},handleAuthorization=issuerId=>MoodleNetService.authorizationCheck(issuerId,courseId).then((async data=>data.status?((issuerId,resourceId,shareFormat)=>{const modal=currentModal.getModal()[0];let infoPromise;modal.querySelector(".modal-header").classList.remove("no-border"),modal.querySelector(".modal-header").classList.add("no-header-text"),currentModal.setBody(Templates.render("core/moodlenet/send_activity_modal_packaging",{})),currentModal.hideFooter(),"activity"===type?infoPromise=MoodleNetService.sendActivity(issuerId,resourceId,shareFormat):"course"===type&&(infoPromise=MoodleNetService.sendCourse(issuerId,resourceId,shareFormat)),infoPromise.then((async data=>{const status=data.status,resourceUrl=data.resourceurl;return responseFromMoodleNet(status,resourceUrl)})).catch(_notification.exception)})(issuerId,resourceId,shareFormat):(window.moodleNetAuthorize=(error,errorDescription)=>{""===error?handleAuthorization(issuerId):"access_denied"!==error&&(0,_notification.alert)("Authorization error","Error: "+error+"

Error description: "+errorDescription,"Cancel")},window.open(data.loginurl,"moodlenet_auth","location=0,status=0,width=".concat(550,",height=").concat(550,",scrollbars=yes"))))).catch(_notification.exception),registerEventListeners=()=>{document.addEventListener("click",(e=>{const shareAction=e.target.closest('[data-action="sendtomoodlenet"]'),sendAction=e.target.closest('.moodlenet-action-buttons [data-action="share"]');if(shareAction){e.preventDefault(),type=shareAction.getAttribute("data-type");const shareType=shareAction.getAttribute("data-sharetype"),resourceId=_config.default.contextInstanceId;let infoPromise;"activity"===type?infoPromise=MoodleNetService.getActivityInformation(resourceId):"course"===type&&(infoPromise=MoodleNetService.getCourseInformation(resourceId)),infoPromise.then((async data=>{(async(data,shareType)=>{if(data.status)return siteSupportUrl=data.supportpageurl,issuerId=data.issuerid,_modal_factory.default.create({type:_send_activity_modal.default.TYPE,large:!0,templateContext:{activitytype:data.type,activityname:data.name,sharetype:await(0,_str.getString)("moodlenet:sharetype"+shareType,"moodle"),server:data.server}}).then((modal=>(currentModal=modal,modal.show(),modal))).catch(_notification.exception);return(0,_notification.addNotification)({message:data.warnings[0].message,type:"error"})})(data,shareType).catch(_notification.exception)})).catch(_notification.exception)}sendAction&&(e.preventDefault(),courseId=_config.default.courseId,resourceId=_config.default.contextInstanceId,shareFormat=0,handleAuthorization(issuerId))}))};_exports.init=()=>{listenersRegistered||(_prefetch.default.prefetchTemplates(["core/moodlenet/send_activity_modal_base","core/moodlenet/send_activity_modal_packaging","core/moodlenet/send_activity_modal_done","core/moodlenet/send_activity_modal_footer_view"]),registerEventListeners(),listenersRegistered=!0)}})); //# sourceMappingURL=send_resource.min.js.map \ No newline at end of file diff --git a/lib/amd/build/moodlenet/send_resource.min.js.map b/lib/amd/build/moodlenet/send_resource.min.js.map index c3725009faf..f121df4885b 100644 --- a/lib/amd/build/moodlenet/send_resource.min.js.map +++ b/lib/amd/build/moodlenet/send_resource.min.js.map @@ -1 +1 @@ -{"version":3,"file":"send_resource.min.js","sources":["../../src/moodlenet/send_resource.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 * A module to handle Share operations of the MoodleNet.\n *\n * @module core/moodlenet/send_resource\n * @copyright 2023 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.2\n */\n\nimport Config from 'core/config';\nimport ModalFactory from 'core/modal_factory';\nimport {alert as displayAlert, addNotification, exception as displayException} from 'core/notification';\nimport {get_string as getString} from 'core/str';\nimport Prefetch from \"core/prefetch\";\nimport * as Templates from 'core/templates';\nimport * as MoodleNetService from 'core/moodlenet/service';\nimport SendActivityModal from 'core/moodlenet/send_activity_modal';\n\nconst TYPE_ACTIVITY = \"activity\";\nconst TYPE_COURSE = \"course\";\n\nlet listenersRegistered = false;\nlet currentModal;\nlet siteSupportUrl;\nlet issuerId;\nlet courseId;\nlet resourceId;\nlet shareFormat;\nlet type;\n\n/**\n * Handle send to MoodleNet.\n *\n * @param {int} issuerId The OAuth 2 issuer ID.\n * @param {int} resourceId The resource ID, it can be a course or an activity.\n * @param {int} shareFormat The share format.\n */\nconst sendToMoodleNet = (issuerId, resourceId, shareFormat) => {\n const $modal = currentModal.getModal();\n const modal = $modal[0];\n modal.querySelector('.modal-header').classList.remove('no-border');\n modal.querySelector('.modal-header').classList.add('no-header-text');\n\n currentModal.setBody(Templates.render('core/moodlenet/send_activity_modal_packaging', {}));\n currentModal.hideFooter();\n\n let infoPromise;\n if (type === TYPE_ACTIVITY) {\n infoPromise = MoodleNetService.sendActivity(issuerId, resourceId, shareFormat);\n } else if (type === TYPE_COURSE) {\n infoPromise = MoodleNetService.sendCourse(issuerId, resourceId, shareFormat);\n }\n infoPromise.then(async(data) => {\n const status = data.status;\n const resourceUrl = data.resourceurl;\n return responseFromMoodleNet(status, resourceUrl);\n }).catch(displayException);\n};\n\n/**\n * Handle response from MoodleNet.\n *\n * @param {boolean} status Response status. True if successful.\n * @param {String} resourceUrl Resource URL.\n */\nconst responseFromMoodleNet = (status, resourceUrl = '') => {\n const $modal = currentModal.getModal();\n const modal = $modal[0];\n modal.querySelector('.modal-header').classList.add('no-border');\n currentModal.setBody(Templates.render('core/moodlenet/send_activity_modal_done', {\n success: status,\n sitesupporturl: siteSupportUrl,\n }));\n\n if (status) {\n currentModal.setFooter(Templates.render('core/moodlenet/send_activity_modal_footer_view', {\n resourseurl: resourceUrl,\n }));\n currentModal.showFooter();\n }\n};\n\n/**\n * Handle authorization with MoodleNet server.\n *\n * @param {int} issuerId The OAuth 2 issuer ID.\n * @return {promise}\n */\nconst handleAuthorization = (issuerId) => {\n const windowsizewidth = 550;\n const windowsizeheight = 550;\n\n // Check if the user is authorized with MoodleNet or not.\n return MoodleNetService.authorizationCheck(issuerId, courseId).then(async(data) => {\n if (!data.status) {\n // Not yet authorized.\n // Declare moodleNetAuthorize variable, so we can call it later in the callback.\n window.moodleNetAuthorize = (error, errorDescription) => {\n // This will be called by the callback after the authorization is successful.\n if (error === '') {\n handleAuthorization(issuerId);\n } else if (error !== 'access_denied') {\n displayAlert(\n 'Authorization error',\n 'Error: ' + error + '

Error description: ' + errorDescription,\n 'Cancel'\n );\n }\n };\n // Open the login url of the OAuth 2 issuer for user to login into MoodleNet and authorize.\n return window.open(data.loginurl, 'moodlenet_auth',\n `location=0,status=0,width=${windowsizewidth},height=${windowsizeheight},scrollbars=yes`);\n } else {\n // Already authorized.\n return sendToMoodleNet(issuerId, resourceId, shareFormat);\n }\n }).catch(displayException);\n};\n\n/**\n * Render the modal to send resource to MoodleNet.\n *\n * @param {obj} data The data of the resource to be shared.\n * @param {string} shareType The type of the resource to be shared.\n */\nconst renderModal = async(data, shareType) => {\n if (data.status) {\n siteSupportUrl = data.supportpageurl;\n issuerId = data.issuerid;\n const modalPromise = ModalFactory.create({\n type: SendActivityModal.TYPE,\n large: true,\n templateContext: {\n 'activitytype': data.type,\n 'activityname': data.name,\n 'sharetype': await getString('moodlenet:sharetype' + shareType, 'moodle'),\n 'server': data.server,\n }\n });\n return modalPromise.then(modal => {\n currentModal = modal;\n modal.show();\n return modal;\n }).catch(displayException);\n } else {\n return addNotification({\n message: data.warnings[0].message,\n type: 'error'\n });\n }\n};\n\n/**\n * Register events.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', e => {\n const shareAction = e.target.closest('[data-action=\"sendtomoodlenet\"]');\n const sendAction = e.target.closest('.moodlenet-action-buttons [data-action=\"share\"]');\n if (shareAction) {\n e.preventDefault();\n type = shareAction.getAttribute('data-type');\n const shareType = shareAction.getAttribute('data-sharetype');\n const resourceId = Config.contextInstanceId;\n\n let infoPromise;\n if (type === TYPE_ACTIVITY) {\n infoPromise = MoodleNetService.getActivityInformation(resourceId);\n } else if (type === TYPE_COURSE) {\n infoPromise = MoodleNetService.getCourseInformation(resourceId);\n }\n infoPromise.then(async(data) => {\n renderModal(data, shareType).catch(displayException);\n }).catch(displayException);\n }\n\n if (sendAction) {\n e.preventDefault();\n courseId = Config.courseId;\n resourceId = Config.contextInstanceId;\n shareFormat = 0;\n handleAuthorization(issuerId);\n }\n });\n};\n\n/**\n * Initialize.\n */\nexport const init = () => {\n if (!listenersRegistered) {\n Prefetch.prefetchTemplates([\n 'core/moodlenet/send_activity_modal_base',\n 'core/moodlenet/send_activity_modal_packaging',\n 'core/moodlenet/send_activity_modal_done',\n 'core/moodlenet/send_activity_modal_footer_view',\n ]);\n registerEventListeners();\n listenersRegistered = true;\n }\n};\n"],"names":["currentModal","siteSupportUrl","issuerId","courseId","resourceId","shareFormat","type","listenersRegistered","responseFromMoodleNet","status","resourceUrl","$modal","getModal","modal","querySelector","classList","add","setBody","Templates","render","success","sitesupporturl","setFooter","resourseurl","showFooter","handleAuthorization","MoodleNetService","authorizationCheck","then","async","data","infoPromise","remove","hideFooter","sendActivity","sendCourse","resourceurl","catch","displayException","sendToMoodleNet","window","moodleNetAuthorize","error","errorDescription","open","loginurl","registerEventListeners","document","addEventListener","e","shareAction","target","closest","sendAction","preventDefault","getAttribute","shareType","Config","contextInstanceId","getActivityInformation","getCourseInformation","supportpageurl","issuerid","ModalFactory","create","SendActivityModal","TYPE","large","templateContext","name","server","show","message","warnings","renderModal","prefetchTemplates"],"mappings":";;;;;;;;0YAqCIA,aACAC,eACAC,SACAC,SACAC,WACAC,YACAC,KAPAC,qBAAsB,QA4CpBC,sBAAwB,SAACC,YAAQC,mEAAc,SAC3CC,OAASX,aAAaY,WACtBC,MAAQF,OAAO,GACrBE,MAAMC,cAAc,iBAAiBC,UAAUC,IAAI,aACnDhB,aAAaiB,QAAQC,UAAUC,OAAO,0CAA2C,CAC7EC,QAASX,OACTY,eAAgBpB,kBAGhBQ,SACAT,aAAasB,UAAUJ,UAAUC,OAAO,iDAAkD,CACtFI,YAAab,eAEjBV,aAAawB,eAUfC,oBAAuBvB,UAKlBwB,iBAAiBC,mBAAmBzB,SAAUC,UAAUyB,MAAKC,MAAAA,MAC3DC,KAAKrB,OAzDM,EAACP,SAAUE,WAAYC,qBAErCQ,MADSb,aAAaY,WACP,OAOjBmB,YANJlB,MAAMC,cAAc,iBAAiBC,UAAUiB,OAAO,aACtDnB,MAAMC,cAAc,iBAAiBC,UAAUC,IAAI,kBAEnDhB,aAAaiB,QAAQC,UAAUC,OAAO,+CAAgD,KACtFnB,aAAaiC,aA1BK,aA6Bd3B,KACAyB,YAAcL,iBAAiBQ,aAAahC,SAAUE,WAAYC,aA7BtD,WA8BLC,OACPyB,YAAcL,iBAAiBS,WAAWjC,SAAUE,WAAYC,cAEpE0B,YAAYH,MAAKC,MAAAA,aACPpB,OAASqB,KAAKrB,OACdC,YAAcoB,KAAKM,mBAClB5B,sBAAsBC,OAAQC,gBACtC2B,MAAMC,0BA0DMC,CAAgBrC,SAAUE,WAAYC,cAjB7CmC,OAAOC,mBAAqB,CAACC,MAAOC,oBAElB,KAAVD,MACAjB,oBAAoBvB,UACH,kBAAVwC,+BAEH,sBACA,UAAYA,MAAQ,8BAAgCC,iBACpD,WAKLH,OAAOI,KAAKd,KAAKe,SAAU,qDArBlB,uBACC,2BA0BtBR,MAAMC,yBAuCPQ,uBAAyB,KAC3BC,SAASC,iBAAiB,SAASC,UACzBC,YAAcD,EAAEE,OAAOC,QAAQ,mCAC/BC,WAAaJ,EAAEE,OAAOC,QAAQ,sDAChCF,YAAa,CACbD,EAAEK,iBACFhD,KAAO4C,YAAYK,aAAa,mBAC1BC,UAAYN,YAAYK,aAAa,kBACrCnD,WAAaqD,gBAAOC,sBAEtB3B,YAnJM,aAoJNzB,KACAyB,YAAcL,iBAAiBiC,uBAAuBvD,YApJlD,WAqJGE,OACPyB,YAAcL,iBAAiBkC,qBAAqBxD,aAExD2B,YAAYH,MAAKC,MAAAA,OA9CTA,OAAMC,KAAM0B,gBACxB1B,KAAKrB,cACLR,eAAiB6B,KAAK+B,eACtB3D,SAAW4B,KAAKgC,SACKC,uBAAaC,OAAO,CACrC1D,KAAM2D,6BAAkBC,KACxBC,OAAO,EACPC,gBAAiB,cACGtC,KAAKxB,kBACLwB,KAAKuC,qBACF,mBAAU,sBAAwBb,UAAW,iBACtD1B,KAAKwC,UAGH1C,MAAKf,QACrBb,aAAea,MACfA,MAAM0D,OACC1D,SACRwB,MAAMC,gCAEF,iCAAgB,CACnBkC,QAAS1C,KAAK2C,SAAS,GAAGD,QAC1BlE,KAAM,WAyBFoE,CAAY5C,KAAM0B,WAAWnB,MAAMC,4BACpCD,MAAMC,yBAGTe,aACAJ,EAAEK,iBACFnD,SAAWsD,gBAAOtD,SAClBC,WAAaqD,gBAAOC,kBACpBrD,YAAc,EACdoB,oBAAoBvB,6BAQZ,KACXK,wCACQoE,kBAAkB,CACvB,0CACA,+CACA,0CACA,mDAEJ7B,yBACAvC,qBAAsB"} \ No newline at end of file +{"version":3,"file":"send_resource.min.js","sources":["../../src/moodlenet/send_resource.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 * A module to handle Share operations of the MoodleNet.\n *\n * @module core/moodlenet/send_resource\n * @copyright 2023 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.2\n */\n\nimport Config from 'core/config';\nimport ModalFactory from 'core/modal_factory';\nimport {alert as displayAlert, addNotification, exception as displayException} from 'core/notification';\nimport {getString} from 'core/str';\nimport Prefetch from \"core/prefetch\";\nimport * as Templates from 'core/templates';\nimport * as MoodleNetService from 'core/moodlenet/service';\nimport SendActivityModal from 'core/moodlenet/send_activity_modal';\n\nconst TYPE_ACTIVITY = \"activity\";\nconst TYPE_COURSE = \"course\";\n\nlet listenersRegistered = false;\nlet currentModal;\nlet siteSupportUrl;\nlet issuerId;\nlet courseId;\nlet resourceId;\nlet shareFormat;\nlet type;\n\n/**\n * Handle send to MoodleNet.\n *\n * @param {int} issuerId The OAuth 2 issuer ID.\n * @param {int} resourceId The resource ID, it can be a course or an activity.\n * @param {int} shareFormat The share format.\n */\nconst sendToMoodleNet = (issuerId, resourceId, shareFormat) => {\n const $modal = currentModal.getModal();\n const modal = $modal[0];\n modal.querySelector('.modal-header').classList.remove('no-border');\n modal.querySelector('.modal-header').classList.add('no-header-text');\n\n currentModal.setBody(Templates.render('core/moodlenet/send_activity_modal_packaging', {}));\n currentModal.hideFooter();\n\n let infoPromise;\n if (type === TYPE_ACTIVITY) {\n infoPromise = MoodleNetService.sendActivity(issuerId, resourceId, shareFormat);\n } else if (type === TYPE_COURSE) {\n infoPromise = MoodleNetService.sendCourse(issuerId, resourceId, shareFormat);\n }\n infoPromise.then(async(data) => {\n const status = data.status;\n const resourceUrl = data.resourceurl;\n return responseFromMoodleNet(status, resourceUrl);\n }).catch(displayException);\n};\n\n/**\n * Handle response from MoodleNet.\n *\n * @param {boolean} status Response status. True if successful.\n * @param {String} resourceUrl Resource URL.\n */\nconst responseFromMoodleNet = (status, resourceUrl = '') => {\n const $modal = currentModal.getModal();\n const modal = $modal[0];\n modal.querySelector('.modal-header').classList.add('no-border');\n currentModal.setBody(Templates.render('core/moodlenet/send_activity_modal_done', {\n success: status,\n sitesupporturl: siteSupportUrl,\n }));\n\n if (status) {\n currentModal.setFooter(Templates.render('core/moodlenet/send_activity_modal_footer_view', {\n resourseurl: resourceUrl,\n }));\n currentModal.showFooter();\n }\n};\n\n/**\n * Handle authorization with MoodleNet server.\n *\n * @param {int} issuerId The OAuth 2 issuer ID.\n * @return {promise}\n */\nconst handleAuthorization = (issuerId) => {\n const windowsizewidth = 550;\n const windowsizeheight = 550;\n\n // Check if the user is authorized with MoodleNet or not.\n return MoodleNetService.authorizationCheck(issuerId, courseId).then(async(data) => {\n if (!data.status) {\n // Not yet authorized.\n // Declare moodleNetAuthorize variable, so we can call it later in the callback.\n window.moodleNetAuthorize = (error, errorDescription) => {\n // This will be called by the callback after the authorization is successful.\n if (error === '') {\n handleAuthorization(issuerId);\n } else if (error !== 'access_denied') {\n displayAlert(\n 'Authorization error',\n 'Error: ' + error + '

Error description: ' + errorDescription,\n 'Cancel'\n );\n }\n };\n // Open the login url of the OAuth 2 issuer for user to login into MoodleNet and authorize.\n return window.open(data.loginurl, 'moodlenet_auth',\n `location=0,status=0,width=${windowsizewidth},height=${windowsizeheight},scrollbars=yes`);\n } else {\n // Already authorized.\n return sendToMoodleNet(issuerId, resourceId, shareFormat);\n }\n }).catch(displayException);\n};\n\n/**\n * Render the modal to send resource to MoodleNet.\n *\n * @param {obj} data The data of the resource to be shared.\n * @param {string} shareType The type of the resource to be shared.\n */\nconst renderModal = async(data, shareType) => {\n if (data.status) {\n siteSupportUrl = data.supportpageurl;\n issuerId = data.issuerid;\n const modalPromise = ModalFactory.create({\n type: SendActivityModal.TYPE,\n large: true,\n templateContext: {\n 'activitytype': data.type,\n 'activityname': data.name,\n 'sharetype': await getString('moodlenet:sharetype' + shareType, 'moodle'),\n 'server': data.server,\n }\n });\n return modalPromise.then(modal => {\n currentModal = modal;\n modal.show();\n return modal;\n }).catch(displayException);\n } else {\n return addNotification({\n message: data.warnings[0].message,\n type: 'error'\n });\n }\n};\n\n/**\n * Register events.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', e => {\n const shareAction = e.target.closest('[data-action=\"sendtomoodlenet\"]');\n const sendAction = e.target.closest('.moodlenet-action-buttons [data-action=\"share\"]');\n if (shareAction) {\n e.preventDefault();\n type = shareAction.getAttribute('data-type');\n const shareType = shareAction.getAttribute('data-sharetype');\n const resourceId = Config.contextInstanceId;\n\n let infoPromise;\n if (type === TYPE_ACTIVITY) {\n infoPromise = MoodleNetService.getActivityInformation(resourceId);\n } else if (type === TYPE_COURSE) {\n infoPromise = MoodleNetService.getCourseInformation(resourceId);\n }\n infoPromise.then(async(data) => {\n renderModal(data, shareType).catch(displayException);\n }).catch(displayException);\n }\n\n if (sendAction) {\n e.preventDefault();\n courseId = Config.courseId;\n resourceId = Config.contextInstanceId;\n shareFormat = 0;\n handleAuthorization(issuerId);\n }\n });\n};\n\n/**\n * Initialize.\n */\nexport const init = () => {\n if (!listenersRegistered) {\n Prefetch.prefetchTemplates([\n 'core/moodlenet/send_activity_modal_base',\n 'core/moodlenet/send_activity_modal_packaging',\n 'core/moodlenet/send_activity_modal_done',\n 'core/moodlenet/send_activity_modal_footer_view',\n ]);\n registerEventListeners();\n listenersRegistered = true;\n }\n};\n"],"names":["currentModal","siteSupportUrl","issuerId","courseId","resourceId","shareFormat","type","listenersRegistered","responseFromMoodleNet","status","resourceUrl","$modal","getModal","modal","querySelector","classList","add","setBody","Templates","render","success","sitesupporturl","setFooter","resourseurl","showFooter","handleAuthorization","MoodleNetService","authorizationCheck","then","async","data","infoPromise","remove","hideFooter","sendActivity","sendCourse","resourceurl","catch","displayException","sendToMoodleNet","window","moodleNetAuthorize","error","errorDescription","open","loginurl","registerEventListeners","document","addEventListener","e","shareAction","target","closest","sendAction","preventDefault","getAttribute","shareType","Config","contextInstanceId","getActivityInformation","getCourseInformation","supportpageurl","issuerid","ModalFactory","create","SendActivityModal","TYPE","large","templateContext","name","server","show","message","warnings","renderModal","prefetchTemplates"],"mappings":";;;;;;;;0YAqCIA,aACAC,eACAC,SACAC,SACAC,WACAC,YACAC,KAPAC,qBAAsB,QA4CpBC,sBAAwB,SAACC,YAAQC,mEAAc,SAC3CC,OAASX,aAAaY,WACtBC,MAAQF,OAAO,GACrBE,MAAMC,cAAc,iBAAiBC,UAAUC,IAAI,aACnDhB,aAAaiB,QAAQC,UAAUC,OAAO,0CAA2C,CAC7EC,QAASX,OACTY,eAAgBpB,kBAGhBQ,SACAT,aAAasB,UAAUJ,UAAUC,OAAO,iDAAkD,CACtFI,YAAab,eAEjBV,aAAawB,eAUfC,oBAAuBvB,UAKlBwB,iBAAiBC,mBAAmBzB,SAAUC,UAAUyB,MAAKC,MAAAA,MAC3DC,KAAKrB,OAzDM,EAACP,SAAUE,WAAYC,qBAErCQ,MADSb,aAAaY,WACP,OAOjBmB,YANJlB,MAAMC,cAAc,iBAAiBC,UAAUiB,OAAO,aACtDnB,MAAMC,cAAc,iBAAiBC,UAAUC,IAAI,kBAEnDhB,aAAaiB,QAAQC,UAAUC,OAAO,+CAAgD,KACtFnB,aAAaiC,aA1BK,aA6Bd3B,KACAyB,YAAcL,iBAAiBQ,aAAahC,SAAUE,WAAYC,aA7BtD,WA8BLC,OACPyB,YAAcL,iBAAiBS,WAAWjC,SAAUE,WAAYC,cAEpE0B,YAAYH,MAAKC,MAAAA,aACPpB,OAASqB,KAAKrB,OACdC,YAAcoB,KAAKM,mBAClB5B,sBAAsBC,OAAQC,gBACtC2B,MAAMC,0BA0DMC,CAAgBrC,SAAUE,WAAYC,cAjB7CmC,OAAOC,mBAAqB,CAACC,MAAOC,oBAElB,KAAVD,MACAjB,oBAAoBvB,UACH,kBAAVwC,+BAEH,sBACA,UAAYA,MAAQ,8BAAgCC,iBACpD,WAKLH,OAAOI,KAAKd,KAAKe,SAAU,qDArBlB,uBACC,2BA0BtBR,MAAMC,yBAuCPQ,uBAAyB,KAC3BC,SAASC,iBAAiB,SAASC,UACzBC,YAAcD,EAAEE,OAAOC,QAAQ,mCAC/BC,WAAaJ,EAAEE,OAAOC,QAAQ,sDAChCF,YAAa,CACbD,EAAEK,iBACFhD,KAAO4C,YAAYK,aAAa,mBAC1BC,UAAYN,YAAYK,aAAa,kBACrCnD,WAAaqD,gBAAOC,sBAEtB3B,YAnJM,aAoJNzB,KACAyB,YAAcL,iBAAiBiC,uBAAuBvD,YApJlD,WAqJGE,OACPyB,YAAcL,iBAAiBkC,qBAAqBxD,aAExD2B,YAAYH,MAAKC,MAAAA,OA9CTA,OAAMC,KAAM0B,gBACxB1B,KAAKrB,cACLR,eAAiB6B,KAAK+B,eACtB3D,SAAW4B,KAAKgC,SACKC,uBAAaC,OAAO,CACrC1D,KAAM2D,6BAAkBC,KACxBC,OAAO,EACPC,gBAAiB,cACGtC,KAAKxB,kBACLwB,KAAKuC,qBACF,kBAAU,sBAAwBb,UAAW,iBACtD1B,KAAKwC,UAGH1C,MAAKf,QACrBb,aAAea,MACfA,MAAM0D,OACC1D,SACRwB,MAAMC,gCAEF,iCAAgB,CACnBkC,QAAS1C,KAAK2C,SAAS,GAAGD,QAC1BlE,KAAM,WAyBFoE,CAAY5C,KAAM0B,WAAWnB,MAAMC,4BACpCD,MAAMC,yBAGTe,aACAJ,EAAEK,iBACFnD,SAAWsD,gBAAOtD,SAClBC,WAAaqD,gBAAOC,kBACpBrD,YAAc,EACdoB,oBAAoBvB,6BAQZ,KACXK,wCACQoE,kBAAkB,CACvB,0CACA,+CACA,0CACA,mDAEJ7B,yBACAvC,qBAAsB"} \ No newline at end of file diff --git a/lib/amd/build/pending.min.js.map b/lib/amd/build/pending.min.js.map index c73b363a405..f829d68084d 100644 --- a/lib/amd/build/pending.min.js.map +++ b/lib/amd/build/pending.min.js.map @@ -1 +1 @@ -{"version":3,"file":"pending.min.js","sources":["../src/pending.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 * A helper used to inform Behat that an operation is in progress and that Behat must wait for it to complete.\n *\n * @module core/pending\n * @copyright 2018 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.6\n */\n\n/**\n * A helper used to register any long-running operations that are in-progress and that Behat must wait for it to complete.\n *\n * This is useful in cases where the user interface may be updated and take some time to change - for example where\n * applying a transition.\n *\n * This data is used by Behat, but may also be consumed by other location too.\n *\n * By informing Behat that an action is about to happen, and then that it is complete, allows\n * Behat to wait for that completion and avoid random failures in automated testing.\n *\n * Note: It is recommended that a descriptive key be used to aid in debugging where possible, but this is optional.\n */\nexport default class {\n\n /**\n * Create a new Pending Promise statically.\n *\n * @param {String} pendingKey An identifier to help in debugging\n * @return {Promise} A Native Promise\n * @deprecated since Moodle 4.2\n */\n static request(pendingKey) {\n window.console.error(\n `The core/pending::request method has been deprecated. ` +\n `Please use one of the alternative calls to core/pending, for example \"new Pending('${pendingKey}')\". ` +\n `Called with ${pendingKey}`\n );\n return new this(pendingKey);\n }\n\n /**\n * Request a new pendingPromise for later resolution.\n *\n * When the action you are performing is complete, simply call resolve on the returned Promise.\n *\n * @param {String} [pendingKey='pendingPromise'] An identifier to help in debugging\n * @return {Promise} A Native Promise\n * @example\n * import Pending from 'core/pending';\n * import {get_string as getString} from 'core/str';\n *\n * const stringPromise = new Pending('mod_myexample/setup');\n * const myString = getString('ok')\n * .then(okay => {\n * window.console.log(okay);\n * })\n * .then(okay => stringPromise.resolve(okay));\n */\n constructor(pendingKey = 'pendingPromise') {\n let resolver;\n let rejector;\n const pendingPromise = this.constructor.Promise((resolve, reject) => {\n resolver = resolve;\n rejector = reject;\n }, pendingKey);\n pendingPromise.resolve = resolver;\n pendingPromise.reject = rejector;\n\n return pendingPromise;\n }\n\n /**\n * Create a new Pending Promise with the same interface as a native Promise.\n *\n * @param {Callable} fn A callable which takes the resolve and reject arguments as in a Native Promise constructor.\n * @param {String} [pendingKey='pendingPromise'] An identifier to help in debugging\n * @returns {Promise}\n * @since Moodle 4.2\n * @example\n * // Use the Pending class in the same way that you would a Native Promise.\n * import Pending from 'core/pending';\n * import {get_string as getString} from 'core/str';\n *\n * export const init => {\n * Pending.Promise((resolve, reject) => {\n * getString('ok')\n * .then(okay => {\n * window.console.log(okay);\n * return okay;\n * })\n * .then(resolve)\n * .catch(reject);\n * }, 'mod_myexample/setup:init');\n * };\n */\n static Promise(fn, pendingKey = 'pendingPromise') {\n const resolver = new Promise((resolve, reject) => {\n M.util.js_pending(pendingKey);\n\n fn(resolve, reject);\n });\n\n resolver.then(() => {\n M.util.js_complete(pendingKey);\n return;\n }).catch();\n\n return resolver;\n }\n}\n"],"names":["pendingKey","window","console","error","this","constructor","resolver","rejector","pendingPromise","Promise","resolve","reject","fn","M","util","js_pending","then","js_complete","catch"],"mappings":";;;;;;;;;qBA8CmBA,mBACXC,OAAOC,QAAQC,MACX,uJACsFH,2CACvEA,aAEZ,IAAII,KAAKJ,YAqBpBK,kBACQC,SACAC,SAFIP,kEAAa,uBAGfQ,eAAiBJ,KAAKC,YAAYI,SAAQ,CAACC,QAASC,UACtDL,SAAWI,QACXH,SAAWI,SACZX,mBACHQ,eAAeE,QAAUJ,SACzBE,eAAeG,OAASJ,SAEjBC,8BA2BII,QAAIZ,kEAAa,uBACtBM,SAAW,IAAIG,SAAQ,CAACC,QAASC,UACnCE,EAAEC,KAAKC,WAAWf,YAElBY,GAAGF,QAASC,kBAGhBL,SAASU,MAAK,KACVH,EAAEC,KAAKG,YAAYjB,eAEpBkB,QAEIZ"} \ No newline at end of file +{"version":3,"file":"pending.min.js","sources":["../src/pending.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 * A helper used to inform Behat that an operation is in progress and that Behat must wait for it to complete.\n *\n * @module core/pending\n * @copyright 2018 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.6\n */\n\n/**\n * A helper used to register any long-running operations that are in-progress and that Behat must wait for it to complete.\n *\n * This is useful in cases where the user interface may be updated and take some time to change - for example where\n * applying a transition.\n *\n * This data is used by Behat, but may also be consumed by other location too.\n *\n * By informing Behat that an action is about to happen, and then that it is complete, allows\n * Behat to wait for that completion and avoid random failures in automated testing.\n *\n * Note: It is recommended that a descriptive key be used to aid in debugging where possible, but this is optional.\n */\nexport default class {\n\n /**\n * Create a new Pending Promise statically.\n *\n * @param {String} pendingKey An identifier to help in debugging\n * @return {Promise} A Native Promise\n * @deprecated since Moodle 4.2\n */\n static request(pendingKey) {\n window.console.error(\n `The core/pending::request method has been deprecated. ` +\n `Please use one of the alternative calls to core/pending, for example \"new Pending('${pendingKey}')\". ` +\n `Called with ${pendingKey}`\n );\n return new this(pendingKey);\n }\n\n /**\n * Request a new pendingPromise for later resolution.\n *\n * When the action you are performing is complete, simply call resolve on the returned Promise.\n *\n * @param {String} [pendingKey='pendingPromise'] An identifier to help in debugging\n * @return {Promise} A Native Promise\n * @example\n * import Pending from 'core/pending';\n * import {getString} from 'core/str';\n *\n * const stringPromise = new Pending('mod_myexample/setup');\n * const myString = getString('ok')\n * .then(okay => {\n * window.console.log(okay);\n * })\n * .then(okay => stringPromise.resolve(okay));\n */\n constructor(pendingKey = 'pendingPromise') {\n let resolver;\n let rejector;\n const pendingPromise = this.constructor.Promise((resolve, reject) => {\n resolver = resolve;\n rejector = reject;\n }, pendingKey);\n pendingPromise.resolve = resolver;\n pendingPromise.reject = rejector;\n\n return pendingPromise;\n }\n\n /**\n * Create a new Pending Promise with the same interface as a native Promise.\n *\n * @param {Callable} fn A callable which takes the resolve and reject arguments as in a Native Promise constructor.\n * @param {String} [pendingKey='pendingPromise'] An identifier to help in debugging\n * @returns {Promise}\n * @since Moodle 4.2\n * @example\n * // Use the Pending class in the same way that you would a Native Promise.\n * import Pending from 'core/pending';\n * import {getString} from 'core/str';\n *\n * export const init => {\n * Pending.Promise((resolve, reject) => {\n * getString('ok')\n * .then(okay => {\n * window.console.log(okay);\n * return okay;\n * })\n * .then(resolve)\n * .catch(reject);\n * }, 'mod_myexample/setup:init');\n * };\n */\n static Promise(fn, pendingKey = 'pendingPromise') {\n const resolver = new Promise((resolve, reject) => {\n M.util.js_pending(pendingKey);\n\n fn(resolve, reject);\n });\n\n resolver.then(() => {\n M.util.js_complete(pendingKey);\n return;\n }).catch();\n\n return resolver;\n }\n}\n"],"names":["pendingKey","window","console","error","this","constructor","resolver","rejector","pendingPromise","Promise","resolve","reject","fn","M","util","js_pending","then","js_complete","catch"],"mappings":";;;;;;;;;qBA8CmBA,mBACXC,OAAOC,QAAQC,MACX,uJACsFH,2CACvEA,aAEZ,IAAII,KAAKJ,YAqBpBK,kBACQC,SACAC,SAFIP,kEAAa,uBAGfQ,eAAiBJ,KAAKC,YAAYI,SAAQ,CAACC,QAASC,UACtDL,SAAWI,QACXH,SAAWI,SACZX,mBACHQ,eAAeE,QAAUJ,SACzBE,eAAeG,OAASJ,SAEjBC,8BA2BII,QAAIZ,kEAAa,uBACtBM,SAAW,IAAIG,SAAQ,CAACC,QAASC,UACnCE,EAAEC,KAAKC,WAAWf,YAElBY,GAAGF,QAASC,kBAGhBL,SAASU,MAAK,KACVH,EAAEC,KAAKG,YAAYjB,eAEpBkB,QAEIZ"} \ No newline at end of file diff --git a/lib/amd/build/prefetch.min.js.map b/lib/amd/build/prefetch.min.js.map index 61d06993231..4a5614b1d90 100644 --- a/lib/amd/build/prefetch.min.js.map +++ b/lib/amd/build/prefetch.min.js.map @@ -1 +1 @@ -{"version":3,"file":"prefetch.min.js","sources":["../src/prefetch.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 * Prefetch module to help lazily load content for use on the current page.\n *\n * @module core/prefetch\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n *\n * @example Pre-fetching a set of strings to use later\n *\n * import prefetch from 'core/prefetch';\n *\n * // A single string prefetch.\n * prefetch.prefetchString('error', 'cannotfindteacher');\n *\n * // Prefetch multiple strings in the same component.\n * prefetch.prefetchStrings('core', [\n * 'yes',\n * 'no',\n * ]);\n *\n * // Use the strings.\n * import {get_string as getString, get_strings as getStrings} from 'core/str';\n * getString('cannotfindteacher', 'error')\n * .then(str => {\n * window.console.log(str); // Cannot find teacher\n * })\n * .catch();\n * getStrings([\n * {\n * key: 'cannotfindteacher',\n * component: 'error',\n * },\n * {\n * key: 'yes',\n * component: 'core',\n * },\n * {\n * key: 'no',\n * component: 'core',\n * },\n * ])\n * .then((cannotFindTeacher, yes, no) => {\n * window.console.log(cannotFindTeacher); // Cannot find teacher\n * window.console.log(yes); // Yes\n * window.console.log(no); // No\n * })\n * .catch();\n */\nimport Config from 'core/config';\n\n// Keep track of whether the initial prefetch has occurred.\nlet initialPrefetchComplete = false;\n\n// Prefetch templates.\nlet templateList = [];\n\n// Prefetch strings.\nlet stringList = {};\n\nlet prefetchTimer;\n\n/**\n * Fetch all queued items in the queue.\n *\n * Should only be called via processQueue.\n * @private\n */\nconst fetchQueue = () => {\n // Prefetch templates.\n if (templateList) {\n const templatesToLoad = templateList.slice();\n templateList = [];\n import('core/templates')\n .then(Templates => Templates.prefetchTemplates(templatesToLoad))\n .catch();\n }\n\n // Prefetch strings.\n const mappedStringsToFetch = stringList;\n stringList = {};\n\n const stringsToFetch = [];\n Object.keys(mappedStringsToFetch).forEach(component => {\n stringsToFetch.push(...mappedStringsToFetch[component].map(key => {\n return {component, key};\n }));\n });\n\n if (stringsToFetch) {\n import('core/str')\n .then(Str => Str.get_strings(stringsToFetch))\n .catch();\n }\n};\n\n/**\n * Process the prefetch queues as required.\n *\n * The initial call will queue the first fetch after a delay.\n * Subsequent fetches are immediate.\n *\n * @private\n */\nconst processQueue = () => {\n if (prefetchTimer) {\n // There is a live prefetch timer. The initial prefetch has been scheduled but is not complete.\n return;\n }\n\n // The initial prefetch has compelted. Just queue as normal.\n if (initialPrefetchComplete) {\n fetchQueue();\n\n return;\n }\n\n // Queue the initial prefetch in a short while.\n prefetchTimer = setTimeout(() => {\n initialPrefetchComplete = true;\n prefetchTimer = null;\n\n // Ensure that the icon system is loaded.\n // This can be quite slow and delay UI interactions if it is loaded on demand.\n import(Config.iconsystemmodule)\n .then(IconSystem => {\n const iconSystem = new IconSystem();\n prefetchTemplate(iconSystem.getTemplateName());\n\n return iconSystem;\n })\n .then(iconSystem => {\n fetchQueue();\n iconSystem.init();\n\n return;\n })\n .catch();\n }, 500);\n};\n\n/**\n * Add a set of templates to the prefetch queue.\n *\n * @param {Array} templatesNames A list of the template names to fetch\n * @static\n */\nconst prefetchTemplates = templatesNames => {\n templateList = templateList.concat(templatesNames);\n\n processQueue();\n};\n\n/**\n * Add a single template to the prefetch queue.\n *\n * @param {String} templateName The template names to fetch\n * @static\n */\nconst prefetchTemplate = templateName => {\n prefetchTemplates([templateName]);\n};\n\n/**\n * Add a set of strings from the same component to the prefetch queue.\n *\n * @param {String} component The component that all of the strings belongs to\n * @param {String[]} keys An array of string identifiers.\n * @static\n */\nconst prefetchStrings = (component, keys) => {\n if (!stringList[component]) {\n stringList[component] = [];\n }\n\n stringList[component] = stringList[component].concat(keys);\n\n processQueue();\n};\n\n/**\n * Add a single string to the prefetch queue.\n *\n * @param {String} component The component that the string belongs to\n * @param {String} key The string identifier\n * @static\n */\nconst prefetchString = (component, key) => {\n if (!stringList[component]) {\n stringList[component] = [];\n }\n\n stringList[component].push(key);\n\n processQueue();\n};\n\n// Prefetch some commonly-used templates.\nprefetchTemplates([].concat(\n ['core/loading'],\n ['core/modal'],\n ['core/modal_backdrop'],\n));\n\n// And some commonly used strings.\nprefetchStrings('core', [\n 'cancel',\n 'closebuttontitle',\n 'loading',\n 'savechanges',\n]);\nprefetchStrings('core_form', [\n 'showless',\n 'showmore',\n]);\n\nexport default {\n prefetchTemplate,\n prefetchTemplates,\n prefetchString,\n prefetchStrings,\n};\n"],"names":["prefetchTimer","initialPrefetchComplete","templateList","stringList","fetchQueue","templatesToLoad","slice","then","Templates","prefetchTemplates","catch","mappedStringsToFetch","stringsToFetch","Object","keys","forEach","component","push","map","key","Str","get_strings","processQueue","setTimeout","Config","iconsystemmodule","IconSystem","iconSystem","prefetchTemplate","getTemplateName","init","templatesNames","concat","templateName","prefetchStrings","prefetchString"],"mappings":"mXA0EIA,cARAC,yBAA0B,EAG1BC,aAAe,GAGfC,WAAa,SAUXC,WAAa,QAEXF,aAAc,OACRG,gBAAkBH,aAAaI,QACrCJ,aAAe,gmBAEdK,MAAKC,WAAaA,UAAUC,kBAAkBJ,mBAC9CK,cAICC,qBAAuBR,WAC7BA,WAAa,SAEPS,eAAiB,GACvBC,OAAOC,KAAKH,sBAAsBI,SAAQC,YACtCJ,eAAeK,QAAQN,qBAAqBK,WAAWE,KAAIC,MAChD,CAACH,UAAAA,UAAWG,IAAAA,YAIvBP,2lBAECL,MAAKa,KAAOA,IAAIC,YAAYT,kBAC5BF,SAYHY,aAAe,KACbtB,gBAMAC,wBACAG,aAMJJ,cAAgBuB,YAAW,KACvBtB,yBAA0B,EAC1BD,cAAgB,sNAITwB,gBAAOC,4SAAPD,gBAAP,4EAAOA,gBAAOC,oBACblB,MAAKmB,mBACIC,WAAa,IAAID,kBACvBE,iBAAiBD,WAAWE,mBAErBF,cAEVpB,MAAKoB,aACFvB,aACAuB,WAAWG,UAIdpB,UACF,OASDD,kBAAoBsB,iBACtB7B,aAAeA,aAAa8B,OAAOD,gBAEnCT,gBASEM,iBAAmBK,eACrBxB,kBAAkB,CAACwB,gBAUjBC,gBAAkB,CAAClB,UAAWF,QAC3BX,WAAWa,aACZb,WAAWa,WAAa,IAG5Bb,WAAWa,WAAab,WAAWa,WAAWgB,OAAOlB,MAErDQ,gBAqBJb,kBAAkB,GAAGuB,OACjB,CAAC,gBACD,CAAC,cACD,CAAC,yBAILE,gBAAgB,OAAQ,CACpB,SACA,mBACA,UACA,gBAEJA,gBAAgB,YAAa,CACzB,WACA,0BAGW,CACXN,iBAAAA,iBACAnB,kBAAAA,kBACA0B,eAhCmB,CAACnB,UAAWG,OAC1BhB,WAAWa,aACZb,WAAWa,WAAa,IAG5Bb,WAAWa,WAAWC,KAAKE,KAE3BG,gBA0BAY,gBAAAA"} \ No newline at end of file +{"version":3,"file":"prefetch.min.js","sources":["../src/prefetch.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 * Prefetch module to help lazily load content for use on the current page.\n *\n * @module core/prefetch\n * @copyright 2020 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n *\n * @example Pre-fetching a set of strings to use later\n *\n * import prefetch from 'core/prefetch';\n *\n * // A single string prefetch.\n * prefetch.prefetchString('error', 'cannotfindteacher');\n *\n * // Prefetch multiple strings in the same component.\n * prefetch.prefetchStrings('core', [\n * 'yes',\n * 'no',\n * ]);\n *\n * // Use the strings.\n * import {getString, getStrings} from 'core/str';\n * getString('cannotfindteacher', 'error')\n * .then(str => {\n * window.console.log(str); // Cannot find teacher\n * })\n * .catch();\n * getStrings([\n * {\n * key: 'cannotfindteacher',\n * component: 'error',\n * },\n * {\n * key: 'yes',\n * component: 'core',\n * },\n * {\n * key: 'no',\n * component: 'core',\n * },\n * ])\n * .then((cannotFindTeacher, yes, no) => {\n * window.console.log(cannotFindTeacher); // Cannot find teacher\n * window.console.log(yes); // Yes\n * window.console.log(no); // No\n * })\n * .catch();\n */\nimport Config from 'core/config';\n\n// Keep track of whether the initial prefetch has occurred.\nlet initialPrefetchComplete = false;\n\n// Prefetch templates.\nlet templateList = [];\n\n// Prefetch strings.\nlet stringList = {};\n\nlet prefetchTimer;\n\n/**\n * Fetch all queued items in the queue.\n *\n * Should only be called via processQueue.\n * @private\n */\nconst fetchQueue = () => {\n // Prefetch templates.\n if (templateList) {\n const templatesToLoad = templateList.slice();\n templateList = [];\n import('core/templates')\n .then(Templates => Templates.prefetchTemplates(templatesToLoad))\n .catch();\n }\n\n // Prefetch strings.\n const mappedStringsToFetch = stringList;\n stringList = {};\n\n const stringsToFetch = [];\n Object.keys(mappedStringsToFetch).forEach(component => {\n stringsToFetch.push(...mappedStringsToFetch[component].map(key => {\n return {component, key};\n }));\n });\n\n if (stringsToFetch) {\n import('core/str')\n .then(Str => Str.get_strings(stringsToFetch))\n .catch();\n }\n};\n\n/**\n * Process the prefetch queues as required.\n *\n * The initial call will queue the first fetch after a delay.\n * Subsequent fetches are immediate.\n *\n * @private\n */\nconst processQueue = () => {\n if (prefetchTimer) {\n // There is a live prefetch timer. The initial prefetch has been scheduled but is not complete.\n return;\n }\n\n // The initial prefetch has compelted. Just queue as normal.\n if (initialPrefetchComplete) {\n fetchQueue();\n\n return;\n }\n\n // Queue the initial prefetch in a short while.\n prefetchTimer = setTimeout(() => {\n initialPrefetchComplete = true;\n prefetchTimer = null;\n\n // Ensure that the icon system is loaded.\n // This can be quite slow and delay UI interactions if it is loaded on demand.\n import(Config.iconsystemmodule)\n .then(IconSystem => {\n const iconSystem = new IconSystem();\n prefetchTemplate(iconSystem.getTemplateName());\n\n return iconSystem;\n })\n .then(iconSystem => {\n fetchQueue();\n iconSystem.init();\n\n return;\n })\n .catch();\n }, 500);\n};\n\n/**\n * Add a set of templates to the prefetch queue.\n *\n * @param {Array} templatesNames A list of the template names to fetch\n * @static\n */\nconst prefetchTemplates = templatesNames => {\n templateList = templateList.concat(templatesNames);\n\n processQueue();\n};\n\n/**\n * Add a single template to the prefetch queue.\n *\n * @param {String} templateName The template names to fetch\n * @static\n */\nconst prefetchTemplate = templateName => {\n prefetchTemplates([templateName]);\n};\n\n/**\n * Add a set of strings from the same component to the prefetch queue.\n *\n * @param {String} component The component that all of the strings belongs to\n * @param {String[]} keys An array of string identifiers.\n * @static\n */\nconst prefetchStrings = (component, keys) => {\n if (!stringList[component]) {\n stringList[component] = [];\n }\n\n stringList[component] = stringList[component].concat(keys);\n\n processQueue();\n};\n\n/**\n * Add a single string to the prefetch queue.\n *\n * @param {String} component The component that the string belongs to\n * @param {String} key The string identifier\n * @static\n */\nconst prefetchString = (component, key) => {\n if (!stringList[component]) {\n stringList[component] = [];\n }\n\n stringList[component].push(key);\n\n processQueue();\n};\n\n// Prefetch some commonly-used templates.\nprefetchTemplates([].concat(\n ['core/loading'],\n ['core/modal'],\n ['core/modal_backdrop'],\n));\n\n// And some commonly used strings.\nprefetchStrings('core', [\n 'cancel',\n 'closebuttontitle',\n 'loading',\n 'savechanges',\n]);\nprefetchStrings('core_form', [\n 'showless',\n 'showmore',\n]);\n\nexport default {\n prefetchTemplate,\n prefetchTemplates,\n prefetchString,\n prefetchStrings,\n};\n"],"names":["prefetchTimer","initialPrefetchComplete","templateList","stringList","fetchQueue","templatesToLoad","slice","then","Templates","prefetchTemplates","catch","mappedStringsToFetch","stringsToFetch","Object","keys","forEach","component","push","map","key","Str","get_strings","processQueue","setTimeout","Config","iconsystemmodule","IconSystem","iconSystem","prefetchTemplate","getTemplateName","init","templatesNames","concat","templateName","prefetchStrings","prefetchString"],"mappings":"mXA0EIA,cARAC,yBAA0B,EAG1BC,aAAe,GAGfC,WAAa,SAUXC,WAAa,QAEXF,aAAc,OACRG,gBAAkBH,aAAaI,QACrCJ,aAAe,gmBAEdK,MAAKC,WAAaA,UAAUC,kBAAkBJ,mBAC9CK,cAICC,qBAAuBR,WAC7BA,WAAa,SAEPS,eAAiB,GACvBC,OAAOC,KAAKH,sBAAsBI,SAAQC,YACtCJ,eAAeK,QAAQN,qBAAqBK,WAAWE,KAAIC,MAChD,CAACH,UAAAA,UAAWG,IAAAA,YAIvBP,2lBAECL,MAAKa,KAAOA,IAAIC,YAAYT,kBAC5BF,SAYHY,aAAe,KACbtB,gBAMAC,wBACAG,aAMJJ,cAAgBuB,YAAW,KACvBtB,yBAA0B,EAC1BD,cAAgB,sNAITwB,gBAAOC,4SAAPD,gBAAP,4EAAOA,gBAAOC,oBACblB,MAAKmB,mBACIC,WAAa,IAAID,kBACvBE,iBAAiBD,WAAWE,mBAErBF,cAEVpB,MAAKoB,aACFvB,aACAuB,WAAWG,UAIdpB,UACF,OASDD,kBAAoBsB,iBACtB7B,aAAeA,aAAa8B,OAAOD,gBAEnCT,gBASEM,iBAAmBK,eACrBxB,kBAAkB,CAACwB,gBAUjBC,gBAAkB,CAAClB,UAAWF,QAC3BX,WAAWa,aACZb,WAAWa,WAAa,IAG5Bb,WAAWa,WAAab,WAAWa,WAAWgB,OAAOlB,MAErDQ,gBAqBJb,kBAAkB,GAAGuB,OACjB,CAAC,gBACD,CAAC,cACD,CAAC,yBAILE,gBAAgB,OAAQ,CACpB,SACA,mBACA,UACA,gBAEJA,gBAAgB,YAAa,CACzB,WACA,0BAGW,CACXN,iBAAAA,iBACAnB,kBAAAA,kBACA0B,eAhCmB,CAACnB,UAAWG,OAC1BhB,WAAWa,aACZb,WAAWa,WAAa,IAG5Bb,WAAWa,WAAWC,KAAKE,KAE3BG,gBA0BAY,gBAAAA"} \ No newline at end of file diff --git a/lib/amd/build/str.min.js.map b/lib/amd/build/str.min.js.map index 2053dbe15e4..6a8ddbc3028 100644 --- a/lib/amd/build/str.min.js.map +++ b/lib/amd/build/str.min.js.map @@ -1 +1 @@ -{"version":3,"file":"str.min.js","sources":["../src/str.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 * Fetch and return language strings.\n *\n * @module core/str\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n *\n */\nimport $ from 'jquery';\nimport Ajax from 'core/ajax';\nimport LocalStorage from 'core/localstorage';\n\n/**\n * @typedef StringRequest\n * @type {object}\n * @param {string} requests.key The string identifer to fetch\n * @param {string} [requests.component='core'] The componet to fetch from\n * @param {string} [requests.lang] The language to fetch a string for. Defaults to current page language.\n * @param {object|string} [requests.param] The param for variable expansion in the string.\n */\n\n// Module cache for the promises so that we don't make multiple\n// unnecessary requests.\nlet promiseCache = [];\n\n/* eslint-disable no-restricted-properties */\n\n/**\n * Return a Promise that resolves to a string.\n *\n * If the string has previously been cached, then the Promise will be resolved immediately, otherwise it will be fetched\n * from the server and resolved when available.\n *\n * @param {string} key The language string key\n * @param {string} component The language string component\n * @param {string} param The param for variable expansion in the string.\n * @param {string} lang The users language - if not passed it is deduced.\n * @return {jQuery.Promise} A jQuery Promise containing the translated string\n *\n * @example Fetching a string\n *\n * import {get_string as getString} from 'core/str';\n * get_string('cannotfindteacher', 'error')\n * .then((str) => window.console.log(str)); // Cannot find teacher\n */\n// eslint-disable-next-line camelcase\nexport const get_string = (key, component, param, lang) => {\n return get_strings([{key, component, param, lang}])\n .then(results => results[0]);\n};\n\n/**\n * Return a Promise that resolves to a string.\n *\n * If the string has previously been cached, then the Promise will be resolved immediately, otherwise it will be fetched\n * from the server and resolved when available.\n *\n * @param {string} key The language string key\n * @param {string} component The language string component\n * @param {string} param The param for variable expansion in the string.\n * @param {string} lang The users language - if not passed it is deduced.\n * @return {Promise} A native Promise containing the translated string\n *\n * @example Fetching a string\n *\n * import {getString} from 'core/str';\n *\n * getString('cannotfindteacher', 'error')\n * .then((str) => window.console.log(str)); // Cannot find teacher\n */\nexport const getString = (key, component, param, lang) => getRequestedStrings([{key, component, param, lang}])[0];\n\n// eslint-disable-next-line camelcase\n/**\n * Make a batch request to load a set of strings.\n *\n * Any missing string will be fetched from the server.\n * The Promise will only be resolved once all strings are available, or an attempt has been made to fetch them.\n *\n * @param {Array.} requests List of strings to fetch\n * @return {Promise[]} An array of native promises containing the translated strings\n *\n * @example Fetching a set of strings\n *\n * import {getStrings} from 'core/str';\n * getStrings([\n * {\n * key: 'cannotfindteacher',\n * component: 'error',\n * },\n * {\n * key: 'yes',\n * component: 'core',\n * },\n * {\n * key: 'no',\n * component: 'core',\n * },\n * ])\n * .then((cannotFindTeacher, yes, no) => {\n * window.console.log(cannotFindTeacher); // Cannot find teacher\n * window.console.log(yes); // Yes\n * window.console.log(no); // No\n * });\n */\nexport const getStrings = (requests) => Promise.all(getRequestedStrings(requests));\n\n/**\n * Internal function to perform the string requests.\n *\n * @param {Array.} requests List of strings to fetch\n * @returns {Promise}\n */\nconst getRequestedStrings = (requests) => {\n let requestData = [];\n const pageLang = $('html').attr('lang').replace(/-/g, '_');\n\n // Helper function to construct the cache key.\n const getCacheKey = ({key, component, lang = pageLang}) => `core_str/${key}/${component}/${lang}`;\n\n const stringPromises = requests.map((request) => {\n let {component, key, param, lang = pageLang} = request;\n if (!component) {\n component = 'core';\n }\n\n const cacheKey = getCacheKey({key, component, lang});\n\n // Helper function to add the promise to cache.\n const buildReturn = (promise) => {\n // Make sure the promise cache contains our promise.\n promiseCache[cacheKey] = promise;\n return promise;\n };\n\n // Check if we can serve the string straight from M.str.\n if (component in M.str && key in M.str[component]) {\n return buildReturn(new Promise((resolve) => {\n resolve(M.util.get_string(key, component, param, lang));\n }));\n }\n\n // Check if the string is in the browser's local storage.\n const cached = LocalStorage.get(cacheKey);\n if (cached) {\n M.str[component] = {...M.str[component], [key]: cached};\n return buildReturn(new Promise((resolve) => {\n resolve(M.util.get_string(key, component, param, lang));\n }));\n }\n\n // Check if we've already loaded this string from the server.\n if (cacheKey in promiseCache) {\n return buildReturn(promiseCache[cacheKey]).then(() => {\n return M.util.get_string(key, component, param, lang);\n });\n } else {\n // We're going to have to ask the server for the string so\n // add this string to the list of requests to be sent.\n return buildReturn(new Promise((resolve, reject) => {\n requestData.push({\n methodname: 'core_get_string',\n args: {\n stringid: key,\n stringparams: [],\n component,\n lang,\n },\n done: (str) => {\n // When we get the response from the server\n // we should update M.str and the browser's\n // local storage before resolving this promise.\n M.str[component] = {...M.str[component], [key]: str};\n LocalStorage.set(cacheKey, str);\n resolve(M.util.get_string(key, component, param, lang));\n },\n fail: reject\n });\n }));\n }\n });\n\n if (requestData.length) {\n // If we need to load any strings from the server then send\n // off the request.\n Ajax.call(requestData, true, false, false, 0, M.cfg.langrev);\n }\n\n return stringPromises;\n};\n\n/**\n * Make a batch request to load a set of strings.\n *\n * Any missing string will be fetched from the server.\n * The Promise will only be resolved once all strings are available, or an attempt has been made to fetch them.\n *\n * @param {Array.} requests List of strings to fetch\n * @return {Promise[]} An array of native promises containing the translated strings\n *\n * @example Fetching a set of strings\n *\n * import {getStrings} from 'core/str';\n * get_strings([\n * {\n * key: 'cannotfindteacher',\n * component: 'error',\n * },\n * {\n * key: 'yes',\n * component: 'core',\n * },\n * {\n * key: 'no',\n * component: 'core',\n * },\n * ])\n * .then((cannotFindTeacher, yes, no) => {\n * window.console.log(cannotFindTeacher); // Cannot find teacher\n * window.console.log(yes); // Yes\n * window.console.log(no); // No\n * });\n */\n// eslint-disable-next-line camelcase\nexport const get_strings = (requests) => {\n // We need to use jQuery here because some calling code uses the\n // .done handler instead of the .then handler.\n return $.when.apply($, getRequestedStrings(requests))\n .then((...strings) => strings);\n};\n\n/**\n * Add a list of strings to the caches.\n *\n * This function should typically only be called from core APIs to pre-cache values.\n *\n * @method cache_strings\n * @protected\n * @param {Object[]} strings List of strings to fetch\n * @param {string} strings.key The string identifer to fetch\n * @param {string} strings.value The string value\n * @param {string} [strings.component='core'] The componet to fetch from\n * @param {string} [strings.lang] The language to fetch a string for. Defaults to current page language.\n */\n// eslint-disable-next-line camelcase\nexport const cache_strings = (strings) => {\n const defaultLang = $('html').attr('lang').replace(/-/g, '_');\n\n strings.forEach(({key, component, value, lang = defaultLang}) => {\n const cacheKey = ['core_str', key, component, lang].join('/');\n\n // Check M.str caching.\n if (!(component in M.str) || !(key in M.str[component])) {\n if (!(component in M.str)) {\n M.str[component] = {};\n }\n\n M.str[component][key] = value;\n }\n\n // Check local storage.\n if (!LocalStorage.get(cacheKey)) {\n LocalStorage.set(cacheKey, value);\n }\n\n // Check the promises cache.\n if (!(cacheKey in promiseCache)) {\n promiseCache[cacheKey] = $.Deferred().resolve(value).promise();\n }\n });\n};\n/* eslint-enable no-restricted-properties */\n"],"names":["promiseCache","key","component","param","lang","get_strings","then","results","getRequestedStrings","requests","Promise","all","requestData","pageLang","attr","replace","stringPromises","map","request","cacheKey","_ref","getCacheKey","buildReturn","promise","M","str","resolve","util","get_string","cached","LocalStorage","get","reject","push","methodname","args","stringid","stringparams","done","set","fail","length","call","cfg","langrev","$","when","apply","strings","defaultLang","forEach","_ref2","value","join","Deferred"],"mappings":";;;;;;;;;+SAuCIA,aAAe,uBAuBO,CAACC,IAAKC,UAAWC,MAAOC,OACvCC,YAAY,CAAC,CAACJ,IAAAA,IAAKC,UAAAA,UAAWC,MAAAA,MAAOC,KAAAA,QACvCE,MAAKC,SAAWA,QAAQ,wBAsBR,CAACN,IAAKC,UAAWC,MAAOC,OAASI,oBAAoB,CAAC,CAACP,IAAAA,IAAKC,UAAAA,UAAWC,MAAAA,MAAOC,KAAAA,QAAQ,uBAmCpFK,UAAaC,QAAQC,IAAIH,oBAAoBC,iBAQlED,oBAAuBC,eACrBG,YAAc,SACZC,UAAW,mBAAE,QAAQC,KAAK,QAAQC,QAAQ,KAAM,KAKhDC,eAAiBP,SAASQ,KAAKC,cAC7BhB,UAACA,UAADD,IAAYA,IAAZE,MAAiBA,MAAjBC,KAAwBA,KAAOS,UAAYK,QAC1ChB,YACDA,UAAY,cAGViB,SARUC,CAAAA,WAACnB,IAACA,IAADC,UAAMA,UAANE,KAAiBA,KAAOS,wCAA0BZ,gBAAOC,sBAAaE,OAQtEiB,CAAY,CAACpB,IAAAA,IAAKC,UAAAA,UAAWE,KAAAA,OAGxCkB,YAAeC,UAEjBvB,aAAamB,UAAYI,QAClBA,YAIPrB,aAAasB,EAAEC,KAAOxB,OAAOuB,EAAEC,IAAIvB,kBAC5BoB,YAAY,IAAIZ,SAASgB,UAC5BA,QAAQF,EAAEG,KAAKC,WAAW3B,IAAKC,UAAWC,MAAOC,iBAKnDyB,OAASC,sBAAaC,IAAIZ,iBAC5BU,QACAL,EAAEC,IAAIvB,WAAa,IAAIsB,EAAEC,IAAIvB,YAAaD,KAAM4B,QACzCP,YAAY,IAAIZ,SAASgB,UAC5BA,QAAQF,EAAEG,KAAKC,WAAW3B,IAAKC,UAAWC,MAAOC,YAKrDe,YAAYnB,aACLsB,YAAYtB,aAAamB,WAAWb,MAAK,IACrCkB,EAAEG,KAAKC,WAAW3B,IAAKC,UAAWC,MAAOC,QAK7CkB,YAAY,IAAIZ,SAAQ,CAACgB,QAASM,UACrCpB,YAAYqB,KAAK,CACbC,WAAY,kBACZC,KAAM,CACFC,SAAUnC,IACVoC,aAAc,GACdnC,UAAAA,UACAE,KAAAA,MAEJkC,KAAOb,MAIHD,EAAEC,IAAIvB,WAAa,IAAIsB,EAAEC,IAAIvB,YAAaD,KAAMwB,2BACnCc,IAAIpB,SAAUM,KAC3BC,QAAQF,EAAEG,KAAKC,WAAW3B,IAAKC,UAAWC,MAAOC,QAErDoC,KAAMR,uBAMlBpB,YAAY6B,sBAGPC,KAAK9B,aAAa,GAAM,GAAO,EAAO,EAAGY,EAAEmB,IAAIC,SAGjD5B,gBAoCEX,YAAeI,UAGjBoC,gBAAEC,KAAKC,MAAMF,gBAAGrC,oBAAoBC,WACtCH,MAAK,yCAAI0C,gDAAAA,qCAAYA,mEAiBAA,gBACpBC,aAAc,mBAAE,QAAQnC,KAAK,QAAQC,QAAQ,KAAM,KAEzDiC,QAAQE,SAAQC,YAAClD,IAACA,IAADC,UAAMA,UAANkD,MAAiBA,MAAjBhD,KAAwBA,KAAO6C,yBACtC9B,SAAW,CAAC,WAAYlB,IAAKC,UAAWE,MAAMiD,KAAK,KAGnDnD,aAAasB,EAAEC,KAAUxB,OAAOuB,EAAEC,IAAIvB,aAClCA,aAAasB,EAAEC,MACjBD,EAAEC,IAAIvB,WAAa,IAGvBsB,EAAEC,IAAIvB,WAAWD,KAAOmD,OAIvBtB,sBAAaC,IAAIZ,iCACLoB,IAAIpB,SAAUiC,OAIzBjC,YAAYnB,eACdA,aAAamB,UAAY0B,gBAAES,WAAW5B,QAAQ0B,OAAO7B"} \ No newline at end of file +{"version":3,"file":"str.min.js","sources":["../src/str.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 * Fetch and return language strings.\n *\n * @module core/str\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n *\n */\nimport $ from 'jquery';\nimport Ajax from 'core/ajax';\nimport LocalStorage from 'core/localstorage';\n\n/**\n * @typedef StringRequest\n * @type {object}\n * @param {string} requests.key The string identifer to fetch\n * @param {string} [requests.component='core'] The componet to fetch from\n * @param {string} [requests.lang] The language to fetch a string for. Defaults to current page language.\n * @param {object|string} [requests.param] The param for variable expansion in the string.\n */\n\n// Module cache for the promises so that we don't make multiple\n// unnecessary requests.\nlet promiseCache = [];\n\n/* eslint-disable no-restricted-properties */\n\n/**\n * Return a Promise that resolves to a string.\n *\n * If the string has previously been cached, then the Promise will be resolved immediately, otherwise it will be fetched\n * from the server and resolved when available.\n *\n * @param {string} key The language string key\n * @param {string} component The language string component\n * @param {string} param The param for variable expansion in the string.\n * @param {string} lang The users language - if not passed it is deduced.\n * @return {jQuery.Promise} A jQuery Promise containing the translated string\n *\n * @example Fetching a string\n *\n * import {getString} from 'core/str';\n * get_string('cannotfindteacher', 'error')\n * .then((str) => window.console.log(str)); // Cannot find teacher\n */\n// eslint-disable-next-line camelcase\nexport const get_string = (key, component, param, lang) => {\n return get_strings([{key, component, param, lang}])\n .then(results => results[0]);\n};\n\n/**\n * Return a Promise that resolves to a string.\n *\n * If the string has previously been cached, then the Promise will be resolved immediately, otherwise it will be fetched\n * from the server and resolved when available.\n *\n * @param {string} key The language string key\n * @param {string} component The language string component\n * @param {string} param The param for variable expansion in the string.\n * @param {string} lang The users language - if not passed it is deduced.\n * @return {Promise} A native Promise containing the translated string\n *\n * @example Fetching a string\n *\n * import {getString} from 'core/str';\n *\n * getString('cannotfindteacher', 'error')\n * .then((str) => window.console.log(str)); // Cannot find teacher\n */\nexport const getString = (key, component, param, lang) => getRequestedStrings([{key, component, param, lang}])[0];\n\n// eslint-disable-next-line camelcase\n/**\n * Make a batch request to load a set of strings.\n *\n * Any missing string will be fetched from the server.\n * The Promise will only be resolved once all strings are available, or an attempt has been made to fetch them.\n *\n * @param {Array.} requests List of strings to fetch\n * @return {Promise[]} An array of native promises containing the translated strings\n *\n * @example Fetching a set of strings\n *\n * import {getStrings} from 'core/str';\n * getStrings([\n * {\n * key: 'cannotfindteacher',\n * component: 'error',\n * },\n * {\n * key: 'yes',\n * component: 'core',\n * },\n * {\n * key: 'no',\n * component: 'core',\n * },\n * ])\n * .then((cannotFindTeacher, yes, no) => {\n * window.console.log(cannotFindTeacher); // Cannot find teacher\n * window.console.log(yes); // Yes\n * window.console.log(no); // No\n * });\n */\nexport const getStrings = (requests) => Promise.all(getRequestedStrings(requests));\n\n/**\n * Internal function to perform the string requests.\n *\n * @param {Array.} requests List of strings to fetch\n * @returns {Promise}\n */\nconst getRequestedStrings = (requests) => {\n let requestData = [];\n const pageLang = $('html').attr('lang').replace(/-/g, '_');\n\n // Helper function to construct the cache key.\n const getCacheKey = ({key, component, lang = pageLang}) => `core_str/${key}/${component}/${lang}`;\n\n const stringPromises = requests.map((request) => {\n let {component, key, param, lang = pageLang} = request;\n if (!component) {\n component = 'core';\n }\n\n const cacheKey = getCacheKey({key, component, lang});\n\n // Helper function to add the promise to cache.\n const buildReturn = (promise) => {\n // Make sure the promise cache contains our promise.\n promiseCache[cacheKey] = promise;\n return promise;\n };\n\n // Check if we can serve the string straight from M.str.\n if (component in M.str && key in M.str[component]) {\n return buildReturn(new Promise((resolve) => {\n resolve(M.util.get_string(key, component, param, lang));\n }));\n }\n\n // Check if the string is in the browser's local storage.\n const cached = LocalStorage.get(cacheKey);\n if (cached) {\n M.str[component] = {...M.str[component], [key]: cached};\n return buildReturn(new Promise((resolve) => {\n resolve(M.util.get_string(key, component, param, lang));\n }));\n }\n\n // Check if we've already loaded this string from the server.\n if (cacheKey in promiseCache) {\n return buildReturn(promiseCache[cacheKey]).then(() => {\n return M.util.get_string(key, component, param, lang);\n });\n } else {\n // We're going to have to ask the server for the string so\n // add this string to the list of requests to be sent.\n return buildReturn(new Promise((resolve, reject) => {\n requestData.push({\n methodname: 'core_get_string',\n args: {\n stringid: key,\n stringparams: [],\n component,\n lang,\n },\n done: (str) => {\n // When we get the response from the server\n // we should update M.str and the browser's\n // local storage before resolving this promise.\n M.str[component] = {...M.str[component], [key]: str};\n LocalStorage.set(cacheKey, str);\n resolve(M.util.get_string(key, component, param, lang));\n },\n fail: reject\n });\n }));\n }\n });\n\n if (requestData.length) {\n // If we need to load any strings from the server then send\n // off the request.\n Ajax.call(requestData, true, false, false, 0, M.cfg.langrev);\n }\n\n return stringPromises;\n};\n\n/**\n * Make a batch request to load a set of strings.\n *\n * Any missing string will be fetched from the server.\n * The Promise will only be resolved once all strings are available, or an attempt has been made to fetch them.\n *\n * @param {Array.} requests List of strings to fetch\n * @return {Promise[]} An array of native promises containing the translated strings\n *\n * @example Fetching a set of strings\n *\n * import {getStrings} from 'core/str';\n * get_strings([\n * {\n * key: 'cannotfindteacher',\n * component: 'error',\n * },\n * {\n * key: 'yes',\n * component: 'core',\n * },\n * {\n * key: 'no',\n * component: 'core',\n * },\n * ])\n * .then((cannotFindTeacher, yes, no) => {\n * window.console.log(cannotFindTeacher); // Cannot find teacher\n * window.console.log(yes); // Yes\n * window.console.log(no); // No\n * });\n */\n// eslint-disable-next-line camelcase\nexport const get_strings = (requests) => {\n // We need to use jQuery here because some calling code uses the\n // .done handler instead of the .then handler.\n return $.when.apply($, getRequestedStrings(requests))\n .then((...strings) => strings);\n};\n\n/**\n * Add a list of strings to the caches.\n *\n * This function should typically only be called from core APIs to pre-cache values.\n *\n * @method cache_strings\n * @protected\n * @param {Object[]} strings List of strings to fetch\n * @param {string} strings.key The string identifer to fetch\n * @param {string} strings.value The string value\n * @param {string} [strings.component='core'] The componet to fetch from\n * @param {string} [strings.lang] The language to fetch a string for. Defaults to current page language.\n */\n// eslint-disable-next-line camelcase\nexport const cache_strings = (strings) => {\n const defaultLang = $('html').attr('lang').replace(/-/g, '_');\n\n strings.forEach(({key, component, value, lang = defaultLang}) => {\n const cacheKey = ['core_str', key, component, lang].join('/');\n\n // Check M.str caching.\n if (!(component in M.str) || !(key in M.str[component])) {\n if (!(component in M.str)) {\n M.str[component] = {};\n }\n\n M.str[component][key] = value;\n }\n\n // Check local storage.\n if (!LocalStorage.get(cacheKey)) {\n LocalStorage.set(cacheKey, value);\n }\n\n // Check the promises cache.\n if (!(cacheKey in promiseCache)) {\n promiseCache[cacheKey] = $.Deferred().resolve(value).promise();\n }\n });\n};\n/* eslint-enable no-restricted-properties */\n"],"names":["promiseCache","key","component","param","lang","get_strings","then","results","getRequestedStrings","requests","Promise","all","requestData","pageLang","attr","replace","stringPromises","map","request","cacheKey","_ref","getCacheKey","buildReturn","promise","M","str","resolve","util","get_string","cached","LocalStorage","get","reject","push","methodname","args","stringid","stringparams","done","set","fail","length","call","cfg","langrev","$","when","apply","strings","defaultLang","forEach","_ref2","value","join","Deferred"],"mappings":";;;;;;;;;+SAuCIA,aAAe,uBAuBO,CAACC,IAAKC,UAAWC,MAAOC,OACvCC,YAAY,CAAC,CAACJ,IAAAA,IAAKC,UAAAA,UAAWC,MAAAA,MAAOC,KAAAA,QACvCE,MAAKC,SAAWA,QAAQ,wBAsBR,CAACN,IAAKC,UAAWC,MAAOC,OAASI,oBAAoB,CAAC,CAACP,IAAAA,IAAKC,UAAAA,UAAWC,MAAAA,MAAOC,KAAAA,QAAQ,uBAmCpFK,UAAaC,QAAQC,IAAIH,oBAAoBC,iBAQlED,oBAAuBC,eACrBG,YAAc,SACZC,UAAW,mBAAE,QAAQC,KAAK,QAAQC,QAAQ,KAAM,KAKhDC,eAAiBP,SAASQ,KAAKC,cAC7BhB,UAACA,UAADD,IAAYA,IAAZE,MAAiBA,MAAjBC,KAAwBA,KAAOS,UAAYK,QAC1ChB,YACDA,UAAY,cAGViB,SARUC,CAAAA,WAACnB,IAACA,IAADC,UAAMA,UAANE,KAAiBA,KAAOS,wCAA0BZ,gBAAOC,sBAAaE,OAQtEiB,CAAY,CAACpB,IAAAA,IAAKC,UAAAA,UAAWE,KAAAA,OAGxCkB,YAAeC,UAEjBvB,aAAamB,UAAYI,QAClBA,YAIPrB,aAAasB,EAAEC,KAAOxB,OAAOuB,EAAEC,IAAIvB,kBAC5BoB,YAAY,IAAIZ,SAASgB,UAC5BA,QAAQF,EAAEG,KAAKC,WAAW3B,IAAKC,UAAWC,MAAOC,iBAKnDyB,OAASC,sBAAaC,IAAIZ,iBAC5BU,QACAL,EAAEC,IAAIvB,WAAa,IAAIsB,EAAEC,IAAIvB,YAAaD,KAAM4B,QACzCP,YAAY,IAAIZ,SAASgB,UAC5BA,QAAQF,EAAEG,KAAKC,WAAW3B,IAAKC,UAAWC,MAAOC,YAKrDe,YAAYnB,aACLsB,YAAYtB,aAAamB,WAAWb,MAAK,IACrCkB,EAAEG,KAAKC,WAAW3B,IAAKC,UAAWC,MAAOC,QAK7CkB,YAAY,IAAIZ,SAAQ,CAACgB,QAASM,UACrCpB,YAAYqB,KAAK,CACbC,WAAY,kBACZC,KAAM,CACFC,SAAUnC,IACVoC,aAAc,GACdnC,UAAAA,UACAE,KAAAA,MAEJkC,KAAOb,MAIHD,EAAEC,IAAIvB,WAAa,IAAIsB,EAAEC,IAAIvB,YAAaD,KAAMwB,2BACnCc,IAAIpB,SAAUM,KAC3BC,QAAQF,EAAEG,KAAKC,WAAW3B,IAAKC,UAAWC,MAAOC,QAErDoC,KAAMR,uBAMlBpB,YAAY6B,sBAGPC,KAAK9B,aAAa,GAAM,GAAO,EAAO,EAAGY,EAAEmB,IAAIC,SAGjD5B,gBAoCEX,YAAeI,UAGjBoC,gBAAEC,KAAKC,MAAMF,gBAAGrC,oBAAoBC,WACtCH,MAAK,yCAAI0C,gDAAAA,qCAAYA,mEAiBAA,gBACpBC,aAAc,mBAAE,QAAQnC,KAAK,QAAQC,QAAQ,KAAM,KAEzDiC,QAAQE,SAAQC,YAAClD,IAACA,IAADC,UAAMA,UAANkD,MAAiBA,MAAjBhD,KAAwBA,KAAO6C,yBACtC9B,SAAW,CAAC,WAAYlB,IAAKC,UAAWE,MAAMiD,KAAK,KAGnDnD,aAAasB,EAAEC,KAAUxB,OAAOuB,EAAEC,IAAIvB,aAClCA,aAAasB,EAAEC,MACjBD,EAAEC,IAAIvB,WAAa,IAGvBsB,EAAEC,IAAIvB,WAAWD,KAAOmD,OAIvBtB,sBAAaC,IAAIZ,iCACLoB,IAAIpB,SAAUiC,OAIzBjC,YAAYnB,eACdA,aAAamB,UAAY0B,gBAAES,WAAW5B,QAAQ0B,OAAO7B"} \ No newline at end of file diff --git a/lib/amd/build/toast.min.js.map b/lib/amd/build/toast.min.js.map index 9f08963a2cf..3aaa566a32b 100644 --- a/lib/amd/build/toast.min.js.map +++ b/lib/amd/build/toast.min.js.map @@ -1 +1 @@ -{"version":3,"file":"toast.min.js","sources":["../src/toast.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 * A system for displaying small snackbar notifications to users which disappear shortly after they are shown.\n *\n * @module core/toast\n * @copyright 2019 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\n\n/**\n * Add a new region to place toasts in, taking in a parent element.\n *\n * @method\n * @param {HTMLElement} parent\n */\nexport const addToastRegion = async(parent) => {\n const pendingPromise = new Pending('addToastRegion');\n\n try {\n const {html, js} = await Templates.renderForPromise('core/local/toast/wrapper', {});\n Templates.prependNodeContents(parent, html, js);\n } catch (e) {\n Notification.exception(e);\n }\n\n pendingPromise.resolve();\n};\n\n/**\n * Add a new toast or snackbar notification to the page.\n *\n * @method\n * @param {String|Promise} message\n * @param {Object} configuration\n * @param {String} [configuration.title]\n * @param {String} [configuration.subtitle]\n * @param {String} [configuration.type=info] Optional type of the toast notification ('success', 'info', 'warning' or 'danger')\n * @param {Boolean} [configuration.autohide=true]\n * @param {Boolean} [configuration.closeButton=false]\n * @param {Number} [configuration.delay=4000]\n *\n * @example\n * import {add as addToast} from 'core/toast';\n * import {get_string as getString} from 'core/str';\n *\n * addToast('Example string', {\n * type: 'warning',\n * autohide: false,\n * closeButton: true,\n * });\n *\n * addToast(getString('example', 'mod_myexample'), {\n * type: 'warning',\n * autohide: false,\n * closeButton: true,\n * });\n */\nexport const add = async(message, configuration) => {\n const pendingPromise = new Pending('addToastRegion');\n configuration = {\n type: 'info',\n closeButton: false,\n autohide: true,\n delay: 4000,\n ...configuration,\n };\n\n const templateName = `core/local/toast/message`;\n try {\n const targetNode = await getTargetNode();\n const {html, js} = await Templates.renderForPromise(templateName, {\n message: await message,\n ...configuration\n });\n Templates.prependNodeContents(targetNode, html, js);\n } catch (e) {\n Notification.exception(e);\n }\n\n pendingPromise.resolve();\n};\n\nconst getTargetNode = async() => {\n const regions = document.querySelectorAll('.toast-wrapper');\n\n if (regions.length) {\n return regions[regions.length - 1];\n }\n\n await addToastRegion(document.body, 'fixed-bottom');\n return getTargetNode();\n};\n"],"names":["addToastRegion","async","pendingPromise","Pending","html","js","Templates","renderForPromise","prependNodeContents","parent","e","exception","resolve","message","configuration","type","closeButton","autohide","delay","targetNode","getTargetNode","regions","document","querySelectorAll","length","body"],"mappings":";;;;;;;2PAgCaA,eAAiBC,MAAAA,eACpBC,eAAiB,IAAIC,iBAAQ,4BAGzBC,KAACA,KAADC,GAAOA,UAAYC,mBAAUC,iBAAiB,2BAA4B,uBACtEC,oBAAoBC,OAAQL,KAAMC,IAC9C,MAAOK,yBACQC,UAAUD,GAG3BR,eAAeU,+DAgCAX,MAAMY,QAASC,uBACxBZ,eAAiB,IAAIC,iBAAQ,kBACnCW,cAAgB,CACZC,KAAM,OACNC,aAAa,EACbC,UAAU,EACVC,MAAO,OACJJ,yBAKGK,iBAAmBC,iBACnBhB,KAACA,KAADC,GAAOA,UAAYC,mBAAUC,4CAA+B,CAC9DM,cAAeA,WACZC,mCAEGN,oBAAoBW,WAAYf,KAAMC,IAClD,MAAOK,yBACQC,UAAUD,GAG3BR,eAAeU,iBAGbQ,cAAgBnB,gBACZoB,QAAUC,SAASC,iBAAiB,yBAEtCF,QAAQG,OACDH,QAAQA,QAAQG,OAAS,UAG9BxB,eAAesB,SAASG,MACvBL"} \ No newline at end of file +{"version":3,"file":"toast.min.js","sources":["../src/toast.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 * A system for displaying small snackbar notifications to users which disappear shortly after they are shown.\n *\n * @module core/toast\n * @copyright 2019 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\n\n/**\n * Add a new region to place toasts in, taking in a parent element.\n *\n * @method\n * @param {HTMLElement} parent\n */\nexport const addToastRegion = async(parent) => {\n const pendingPromise = new Pending('addToastRegion');\n\n try {\n const {html, js} = await Templates.renderForPromise('core/local/toast/wrapper', {});\n Templates.prependNodeContents(parent, html, js);\n } catch (e) {\n Notification.exception(e);\n }\n\n pendingPromise.resolve();\n};\n\n/**\n * Add a new toast or snackbar notification to the page.\n *\n * @method\n * @param {String|Promise} message\n * @param {Object} configuration\n * @param {String} [configuration.title]\n * @param {String} [configuration.subtitle]\n * @param {String} [configuration.type=info] Optional type of the toast notification ('success', 'info', 'warning' or 'danger')\n * @param {Boolean} [configuration.autohide=true]\n * @param {Boolean} [configuration.closeButton=false]\n * @param {Number} [configuration.delay=4000]\n *\n * @example\n * import {add as addToast} from 'core/toast';\n * import {getString} from 'core/str';\n *\n * addToast('Example string', {\n * type: 'warning',\n * autohide: false,\n * closeButton: true,\n * });\n *\n * addToast(getString('example', 'mod_myexample'), {\n * type: 'warning',\n * autohide: false,\n * closeButton: true,\n * });\n */\nexport const add = async(message, configuration) => {\n const pendingPromise = new Pending('addToastRegion');\n configuration = {\n type: 'info',\n closeButton: false,\n autohide: true,\n delay: 4000,\n ...configuration,\n };\n\n const templateName = `core/local/toast/message`;\n try {\n const targetNode = await getTargetNode();\n const {html, js} = await Templates.renderForPromise(templateName, {\n message: await message,\n ...configuration\n });\n Templates.prependNodeContents(targetNode, html, js);\n } catch (e) {\n Notification.exception(e);\n }\n\n pendingPromise.resolve();\n};\n\nconst getTargetNode = async() => {\n const regions = document.querySelectorAll('.toast-wrapper');\n\n if (regions.length) {\n return regions[regions.length - 1];\n }\n\n await addToastRegion(document.body, 'fixed-bottom');\n return getTargetNode();\n};\n"],"names":["addToastRegion","async","pendingPromise","Pending","html","js","Templates","renderForPromise","prependNodeContents","parent","e","exception","resolve","message","configuration","type","closeButton","autohide","delay","targetNode","getTargetNode","regions","document","querySelectorAll","length","body"],"mappings":";;;;;;;2PAgCaA,eAAiBC,MAAAA,eACpBC,eAAiB,IAAIC,iBAAQ,4BAGzBC,KAACA,KAADC,GAAOA,UAAYC,mBAAUC,iBAAiB,2BAA4B,uBACtEC,oBAAoBC,OAAQL,KAAMC,IAC9C,MAAOK,yBACQC,UAAUD,GAG3BR,eAAeU,+DAgCAX,MAAMY,QAASC,uBACxBZ,eAAiB,IAAIC,iBAAQ,kBACnCW,cAAgB,CACZC,KAAM,OACNC,aAAa,EACbC,UAAU,EACVC,MAAO,OACJJ,yBAKGK,iBAAmBC,iBACnBhB,KAACA,KAADC,GAAOA,UAAYC,mBAAUC,4CAA+B,CAC9DM,cAAeA,WACZC,mCAEGN,oBAAoBW,WAAYf,KAAMC,IAClD,MAAOK,yBACQC,UAAUD,GAG3BR,eAAeU,iBAGbQ,cAAgBnB,gBACZoB,QAAUC,SAASC,iBAAiB,yBAEtCF,QAAQG,OACDH,QAAQA,QAAQG,OAAS,UAG9BxB,eAAesB,SAASG,MACvBL"} \ No newline at end of file diff --git a/lib/amd/src/addblockmodal.js b/lib/amd/src/addblockmodal.js index 829c7c39d33..985f003d8d1 100644 --- a/lib/amd/src/addblockmodal.js +++ b/lib/amd/src/addblockmodal.js @@ -24,7 +24,7 @@ import ModalFactory from 'core/modal_factory'; import Templates from 'core/templates'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Ajax from 'core/ajax'; const SELECTORS = { diff --git a/lib/amd/src/copy_to_clipboard.js b/lib/amd/src/copy_to_clipboard.js index 10c44946ad9..c042ef79792 100644 --- a/lib/amd/src/copy_to_clipboard.js +++ b/lib/amd/src/copy_to_clipboard.js @@ -40,7 +40,7 @@ * Copy to clipboard * */ -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {add as addToast} from 'core/toast'; import {prefetchStrings} from 'core/prefetch'; diff --git a/lib/amd/src/datafilter.js b/lib/amd/src/datafilter.js index 0cf3a2eb043..addb1282917 100644 --- a/lib/amd/src/datafilter.js +++ b/lib/amd/src/datafilter.js @@ -23,7 +23,7 @@ import CourseFilter from 'core/datafilter/filtertypes/courseid'; import GenericFilter from 'core/datafilter/filtertype'; -import {get_strings as getStrings} from 'core/str'; +import {getStrings} from 'core/str'; import Notification from 'core/notification'; import Pending from 'core/pending'; import Selectors from 'core/datafilter/selectors'; diff --git a/lib/amd/src/datafilter/filtertype.js b/lib/amd/src/datafilter/filtertype.js index c424518e9cd..9fb50f0477d 100644 --- a/lib/amd/src/datafilter/filtertype.js +++ b/lib/amd/src/datafilter/filtertype.js @@ -22,7 +22,7 @@ */ import Autocomplete from 'core/form-autocomplete'; import Selectors from 'core/datafilter/selectors'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; /** * Fetch all checked options in the select. diff --git a/lib/amd/src/datafilter/filtertypes/keyword.js b/lib/amd/src/datafilter/filtertypes/keyword.js index 729a9b2a1d7..dfb78c5e813 100644 --- a/lib/amd/src/datafilter/filtertypes/keyword.js +++ b/lib/amd/src/datafilter/filtertypes/keyword.js @@ -21,7 +21,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ import Filter from 'core/datafilter/filtertype'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; export default class extends Filter { /** diff --git a/lib/amd/src/dynamic_tabs.js b/lib/amd/src/dynamic_tabs.js index 73d2cdfa0c3..7f3a2108e91 100644 --- a/lib/amd/src/dynamic_tabs.js +++ b/lib/amd/src/dynamic_tabs.js @@ -26,7 +26,7 @@ import Templates from 'core/templates'; import {addIconToContainer} from 'core/loadingicon'; import Notification from 'core/notification'; import Pending from 'core/pending'; -import {get_strings as getStrings} from 'core/str'; +import {getStrings} from 'core/str'; import {getContent} from 'core/local/repository/dynamic_tabs'; import {isAnyWatchedFormDirty, resetAllFormDirtyStates} from 'core_form/changechecker'; diff --git a/lib/amd/src/emoji/picker.js b/lib/amd/src/emoji/picker.js index 2b489a136a5..355191eac65 100644 --- a/lib/amd/src/emoji/picker.js +++ b/lib/amd/src/emoji/picker.js @@ -23,7 +23,7 @@ import LocalStorage from 'core/localstorage'; import * as EmojiData from 'core/emoji/data'; import {throttle, debounce} from 'core/utils'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {render as renderTemplate} from 'core/templates'; const VISIBLE_ROW_COUNT = 10; diff --git a/lib/amd/src/local/templates/renderer.js b/lib/amd/src/local/templates/renderer.js index d67e98f1715..2e05b124604 100644 --- a/lib/amd/src/local/templates/renderer.js +++ b/lib/amd/src/local/templates/renderer.js @@ -17,7 +17,7 @@ import * as Log from 'core/log'; import * as Truncate from 'core/truncate'; import * as UserDate from 'core/user_date'; import Pending from 'core/pending'; -import {get_strings as getStrings} from 'core/str'; +import {getStrings} from 'core/str'; import IconSystem from 'core/icon_system'; import config from 'core/config'; import mustache from 'core/mustache'; diff --git a/lib/amd/src/moodlenet/oauth2callback.js b/lib/amd/src/moodlenet/oauth2callback.js index 76eb09fcf05..552fb33d22f 100644 --- a/lib/amd/src/moodlenet/oauth2callback.js +++ b/lib/amd/src/moodlenet/oauth2callback.js @@ -24,7 +24,7 @@ import Prefetch from "core/prefetch"; import {alert} from 'core/notification'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; /** * Handle the OAuth2 callback for MoodleNet. diff --git a/lib/amd/src/moodlenet/send_resource.js b/lib/amd/src/moodlenet/send_resource.js index f1e56e7ef2b..49a21f962a2 100644 --- a/lib/amd/src/moodlenet/send_resource.js +++ b/lib/amd/src/moodlenet/send_resource.js @@ -25,7 +25,7 @@ import Config from 'core/config'; import ModalFactory from 'core/modal_factory'; import {alert as displayAlert, addNotification, exception as displayException} from 'core/notification'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Prefetch from "core/prefetch"; import * as Templates from 'core/templates'; import * as MoodleNetService from 'core/moodlenet/service'; diff --git a/lib/amd/src/pending.js b/lib/amd/src/pending.js index 059256a1a4e..5ee7ee796fe 100644 --- a/lib/amd/src/pending.js +++ b/lib/amd/src/pending.js @@ -62,7 +62,7 @@ export default class { * @return {Promise} A Native Promise * @example * import Pending from 'core/pending'; - * import {get_string as getString} from 'core/str'; + * import {getString} from 'core/str'; * * const stringPromise = new Pending('mod_myexample/setup'); * const myString = getString('ok') @@ -94,7 +94,7 @@ export default class { * @example * // Use the Pending class in the same way that you would a Native Promise. * import Pending from 'core/pending'; - * import {get_string as getString} from 'core/str'; + * import {getString} from 'core/str'; * * export const init => { * Pending.Promise((resolve, reject) => { diff --git a/lib/amd/src/prefetch.js b/lib/amd/src/prefetch.js index 84b5050b75a..2223b29133a 100644 --- a/lib/amd/src/prefetch.js +++ b/lib/amd/src/prefetch.js @@ -34,7 +34,7 @@ * ]); * * // Use the strings. - * import {get_string as getString, get_strings as getStrings} from 'core/str'; + * import {getString, getStrings} from 'core/str'; * getString('cannotfindteacher', 'error') * .then(str => { * window.console.log(str); // Cannot find teacher diff --git a/lib/amd/src/str.js b/lib/amd/src/str.js index fdcd4309615..fef1dca942c 100644 --- a/lib/amd/src/str.js +++ b/lib/amd/src/str.js @@ -55,7 +55,7 @@ let promiseCache = []; * * @example Fetching a string * - * import {get_string as getString} from 'core/str'; + * import {getString} from 'core/str'; * get_string('cannotfindteacher', 'error') * .then((str) => window.console.log(str)); // Cannot find teacher */ diff --git a/lib/amd/src/toast.js b/lib/amd/src/toast.js index ecb27b1dd68..cd9c4792b67 100644 --- a/lib/amd/src/toast.js +++ b/lib/amd/src/toast.js @@ -58,7 +58,7 @@ export const addToastRegion = async(parent) => { * * @example * import {add as addToast} from 'core/toast'; - * import {get_string as getString} from 'core/str'; + * import {getString} from 'core/str'; * * addToast('Example string', { * type: 'warning', diff --git a/lib/editor/tiny/amd/build/utils.min.js b/lib/editor/tiny/amd/build/utils.min.js index a672847db29..8957bd8ad56 100644 --- a/lib/editor/tiny/amd/build/utils.min.js +++ b/lib/editor/tiny/amd/build/utils.min.js @@ -1,3 +1,3 @@ -define("editor_tiny/utils",["exports","core/templates","./options","core/str"],(function(_exports,_templates,_options,_str){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.updateEditorState=_exports.removeToolbarButtons=_exports.removeToolbarButton=_exports.removeSubmenuItem=_exports.removeMenubarItems=_exports.removeMenubarItem=_exports.getPluginMetadata=_exports.getImagePath=_exports.getDocumentationLink=_exports.getButtonImage=_exports.ensureEditorIsValid=_exports.displayFilepicker=_exports.addToolbarSection=_exports.addToolbarButtons=_exports.addToolbarButton=_exports.addQuickbarsToolbarItem=_exports.addMenubarItem=_exports.addContextmenuItem=void 0;const getImagePath=function(identifier){let component=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"editor_tiny";return Promise.resolve(M.util.image_url(identifier,component))};_exports.getImagePath=getImagePath;_exports.getButtonImage=async function(identifier){let component=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"editor_tiny";return(0,_templates.renderForPromise)("editor_tiny/toolbar_button",{image:await getImagePath(identifier,component)})};_exports.displayFilepicker=(editor,filetype)=>new Promise(((resolve,reject)=>{const configuration=(0,_options.getFilePicker)(editor,filetype);if(configuration){const options={...configuration,formcallback:resolve};M.core_filepicker.show(Y,options)}else reject("Unknown filetype ".concat(filetype))}));_exports.addToolbarButton=(toolbar,section,button)=>{if(!toolbar)return[{name:section,items:[button]}];return JSON.parse(JSON.stringify(toolbar)).map((item=>(item.name===section&&item.items.push(button),item)))};_exports.addToolbarButtons=(toolbar,section,buttons)=>{if(!toolbar)return[{name:section,items:buttons}];return JSON.parse(JSON.stringify(toolbar)).map((item=>(item.name===section&&buttons.forEach((button=>item.items.push(button))),item)))};_exports.addToolbarSection=function(toolbar,name,relativeTo){let append=!(arguments.length>3&&void 0!==arguments[3])||arguments[3];const newSection={name:name,items:[]},sectionInserted=toolbar.some(((section,index)=>section.name===relativeTo&&(append?toolbar.splice(index+1,0,newSection):toolbar.splice(index,0,newSection),!0)));return sectionInserted||(append?toolbar.push(newSection):toolbar.unshift(newSection)),toolbar};_exports.addMenubarItem=(menubar,section,menuitem)=>{if(!menubar){({})[section]={title:section,items:menuitem}}const mutatedMenubar=JSON.parse(JSON.stringify(menubar));return Array.from(Object.entries(mutatedMenubar)).forEach((_ref=>{let[name,menu]=_ref;name===section&&(menu.items="".concat(menu.items," ").concat(menuitem))})),mutatedMenubar};_exports.addContextmenuItem=function(contextmenu){const contextmenuItems=(null!=contextmenu?contextmenu:"").split(" ");for(var _len=arguments.length,menuitems=new Array(_len>1?_len-1:0),_key=1;_key<_len;_key++)menuitems[_key-1]=arguments[_key];return contextmenuItems.concat(menuitems).filter((item=>""!==item)).join(" ")};_exports.addQuickbarsToolbarItem=function(toolbar){return toolbar};const getDocumentationLink=pluginName=>"https://docs.moodle.org/en/editor_tiny/".concat(pluginName);_exports.getDocumentationLink=getDocumentationLink;_exports.getPluginMetadata=async function(component,pluginName){let url=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const name=await(0,_str.get_string)("helplinktext",component);return{getMetadata:()=>({name:name,url:null!=url?url:getDocumentationLink(pluginName)})}};_exports.ensureEditorIsValid=editor=>editor.getElement().isConnected?editor:null;_exports.removeToolbarButton=(toolbar,section,button)=>{if(!toolbar)return[{name:section,items:[button]}];return JSON.parse(JSON.stringify(toolbar)).map((item=>(item.name===section&&item.items.splice(item.items.indexOf(button),1),item)))};_exports.removeToolbarButtons=(toolbar,section,buttons)=>{if(!toolbar)return[{name:section,items:buttons}];return JSON.parse(JSON.stringify(toolbar)).map((item=>(item.name===section&&buttons.forEach((button=>item.items.splice(item.items.indexOf(button),1))),item)))};_exports.removeSubmenuItem=async(editor,section,submenuitem)=>{const menuItems=editor.ui.registry.getAll().menuItems[section],submenuitemtitle=await(0,_str.get_string)(submenuitem,"editor_tiny");menuItems&&editor.ui.registry.addNestedMenuItem(section,{text:menuItems.text,getSubmenuItems:()=>{let newSubmenu=[];return menuItems.getSubmenuItems().forEach((item=>{item.text.trim()!=submenuitemtitle&&newSubmenu.push(item)})),newSubmenu}})};_exports.removeMenubarItem=(menubar,section,menuitem)=>(menubar[section].items=menubar[section].items.replace(menuitem,""),menubar);_exports.removeMenubarItems=(menubar,section,menuitems)=>{const regexPattern=new RegExp(menuitems.join("|"),"ig");return menubar[section].items=menubar[section].items.replace(regexPattern,""),menubar};_exports.updateEditorState=(editor,target)=>{target.hasAttribute("readonly")?editor.mode.set("readonly"):editor.mode.set("design")}})); +define("editor_tiny/utils",["exports","core/templates","./options","core/str"],(function(_exports,_templates,_options,_str){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.updateEditorState=_exports.removeToolbarButtons=_exports.removeToolbarButton=_exports.removeSubmenuItem=_exports.removeMenubarItems=_exports.removeMenubarItem=_exports.getPluginMetadata=_exports.getImagePath=_exports.getDocumentationLink=_exports.getButtonImage=_exports.ensureEditorIsValid=_exports.displayFilepicker=_exports.addToolbarSection=_exports.addToolbarButtons=_exports.addToolbarButton=_exports.addQuickbarsToolbarItem=_exports.addMenubarItem=_exports.addContextmenuItem=void 0;const getImagePath=function(identifier){let component=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"editor_tiny";return Promise.resolve(M.util.image_url(identifier,component))};_exports.getImagePath=getImagePath;_exports.getButtonImage=async function(identifier){let component=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"editor_tiny";return(0,_templates.renderForPromise)("editor_tiny/toolbar_button",{image:await getImagePath(identifier,component)})};_exports.displayFilepicker=(editor,filetype)=>new Promise(((resolve,reject)=>{const configuration=(0,_options.getFilePicker)(editor,filetype);if(configuration){const options={...configuration,formcallback:resolve};M.core_filepicker.show(Y,options)}else reject("Unknown filetype ".concat(filetype))}));_exports.addToolbarButton=(toolbar,section,button)=>{if(!toolbar)return[{name:section,items:[button]}];return JSON.parse(JSON.stringify(toolbar)).map((item=>(item.name===section&&item.items.push(button),item)))};_exports.addToolbarButtons=(toolbar,section,buttons)=>{if(!toolbar)return[{name:section,items:buttons}];return JSON.parse(JSON.stringify(toolbar)).map((item=>(item.name===section&&buttons.forEach((button=>item.items.push(button))),item)))};_exports.addToolbarSection=function(toolbar,name,relativeTo){let append=!(arguments.length>3&&void 0!==arguments[3])||arguments[3];const newSection={name:name,items:[]},sectionInserted=toolbar.some(((section,index)=>section.name===relativeTo&&(append?toolbar.splice(index+1,0,newSection):toolbar.splice(index,0,newSection),!0)));return sectionInserted||(append?toolbar.push(newSection):toolbar.unshift(newSection)),toolbar};_exports.addMenubarItem=(menubar,section,menuitem)=>{if(!menubar){({})[section]={title:section,items:menuitem}}const mutatedMenubar=JSON.parse(JSON.stringify(menubar));return Array.from(Object.entries(mutatedMenubar)).forEach((_ref=>{let[name,menu]=_ref;name===section&&(menu.items="".concat(menu.items," ").concat(menuitem))})),mutatedMenubar};_exports.addContextmenuItem=function(contextmenu){const contextmenuItems=(null!=contextmenu?contextmenu:"").split(" ");for(var _len=arguments.length,menuitems=new Array(_len>1?_len-1:0),_key=1;_key<_len;_key++)menuitems[_key-1]=arguments[_key];return contextmenuItems.concat(menuitems).filter((item=>""!==item)).join(" ")};_exports.addQuickbarsToolbarItem=function(toolbar){return toolbar};const getDocumentationLink=pluginName=>"https://docs.moodle.org/en/editor_tiny/".concat(pluginName);_exports.getDocumentationLink=getDocumentationLink;_exports.getPluginMetadata=async function(component,pluginName){let url=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const name=await(0,_str.getString)("helplinktext",component);return{getMetadata:()=>({name:name,url:null!=url?url:getDocumentationLink(pluginName)})}};_exports.ensureEditorIsValid=editor=>editor.getElement().isConnected?editor:null;_exports.removeToolbarButton=(toolbar,section,button)=>{if(!toolbar)return[{name:section,items:[button]}];return JSON.parse(JSON.stringify(toolbar)).map((item=>(item.name===section&&item.items.splice(item.items.indexOf(button),1),item)))};_exports.removeToolbarButtons=(toolbar,section,buttons)=>{if(!toolbar)return[{name:section,items:buttons}];return JSON.parse(JSON.stringify(toolbar)).map((item=>(item.name===section&&buttons.forEach((button=>item.items.splice(item.items.indexOf(button),1))),item)))};_exports.removeSubmenuItem=async(editor,section,submenuitem)=>{const menuItems=editor.ui.registry.getAll().menuItems[section],submenuitemtitle=await(0,_str.getString)(submenuitem,"editor_tiny");menuItems&&editor.ui.registry.addNestedMenuItem(section,{text:menuItems.text,getSubmenuItems:()=>{let newSubmenu=[];return menuItems.getSubmenuItems().forEach((item=>{item.text.trim()!=submenuitemtitle&&newSubmenu.push(item)})),newSubmenu}})};_exports.removeMenubarItem=(menubar,section,menuitem)=>(menubar[section].items=menubar[section].items.replace(menuitem,""),menubar);_exports.removeMenubarItems=(menubar,section,menuitems)=>{const regexPattern=new RegExp(menuitems.join("|"),"ig");return menubar[section].items=menubar[section].items.replace(regexPattern,""),menubar};_exports.updateEditorState=(editor,target)=>{target.hasAttribute("readonly")?editor.mode.set("readonly"):editor.mode.set("design")}})); //# sourceMappingURL=utils.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/amd/build/utils.min.js.map b/lib/editor/tiny/amd/build/utils.min.js.map index d40d9b35255..9821fe2f756 100644 --- a/lib/editor/tiny/amd/build/utils.min.js.map +++ b/lib/editor/tiny/amd/build/utils.min.js.map @@ -1 +1 @@ -{"version":3,"file":"utils.min.js","sources":["../src/utils.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\nimport {renderForPromise} from 'core/templates';\nimport {getFilePicker} from './options';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Get the image path for the specified image.\n *\n * @param {string} identifier The name of the image\n * @param {string} component The component name\n * @return {string} The image URL path\n */\nexport const getImagePath = (identifier, component = 'editor_tiny') => Promise.resolve(M.util.image_url(identifier, component));\n\nexport const getButtonImage = async(identifier, component = 'editor_tiny') => renderForPromise('editor_tiny/toolbar_button', {\n image: await getImagePath(identifier, component),\n});\n\n/**\n * Helper to display a filepicker and return a Promise.\n *\n * The Promise will resolve when a file is selected, or reject if the file type is not found.\n *\n * @param {TinyMCE} editor\n * @param {string} filetype\n * @returns {Promise} The file object returned by the filepicker\n */\nexport const displayFilepicker = (editor, filetype) => new Promise((resolve, reject) => {\n const configuration = getFilePicker(editor, filetype);\n if (configuration) {\n const options = {\n ...configuration,\n formcallback: resolve,\n };\n M.core_filepicker.show(Y, options);\n return;\n }\n reject(`Unknown filetype ${filetype}`);\n});\n\n/**\n * Given a TinyMCE Toolbar configuration, add the specified button to the named section.\n *\n * @param {object} toolbar\n * @param {string} section\n * @param {string} button\n * @returns {object} The toolbar configuration\n */\nexport const addToolbarButton = (toolbar, section, button) => {\n if (!toolbar) {\n return [{\n name: section,\n items: [button],\n }];\n }\n\n const mutatedToolbar = JSON.parse(JSON.stringify(toolbar));\n return mutatedToolbar.map((item) => {\n if (item.name === section) {\n item.items.push(button);\n }\n\n return item;\n });\n};\n\n/**\n * Given a TinyMCE Toolbar configuration, add the specified buttons to the named section.\n *\n * @param {object} toolbar\n * @param {string} section\n * @param {Array} buttons\n * @returns {object} The toolbar configuration\n */\nexport const addToolbarButtons = (toolbar, section, buttons) => {\n if (!toolbar) {\n return [{\n name: section,\n items: buttons,\n }];\n }\n\n const mutatedToolbar = JSON.parse(JSON.stringify(toolbar));\n return mutatedToolbar.map((item) => {\n if (item.name === section) {\n buttons.forEach(button => item.items.push(button));\n }\n\n return item;\n });\n};\n\n/**\n * Insert a new section into the toolbar.\n *\n * @param {array} toolbar The TinyMCE.editor.settings.toolbar configuration\n * @param {string} name The new section name to add\n * @param {string} relativeTo Insert relative to this section name\n * @param {boolean} append Append or Prepend\n * @returns {array}\n */\nexport const addToolbarSection = (toolbar, name, relativeTo, append = true) => {\n const newSection = {\n name,\n items: [],\n };\n const sectionInserted = toolbar.some((section, index) => {\n if (section.name === relativeTo) {\n if (append) {\n toolbar.splice(index + 1, 0, newSection);\n } else {\n toolbar.splice(index, 0, newSection);\n }\n return true;\n }\n return false;\n });\n\n if (!sectionInserted) {\n // Relative section not found.\n if (append) {\n toolbar.push(newSection);\n } else {\n toolbar.unshift(newSection);\n }\n }\n\n return toolbar;\n};\n\n/**\n * Given a TinyMCE Menubar configuration, add the specified button to the named section.\n *\n * @param {object} menubar\n * @param {string} section\n * @param {string} menuitem\n * @returns {object}\n */\nexport const addMenubarItem = (menubar, section, menuitem) => {\n if (!menubar) {\n const emptyMenubar = {};\n emptyMenubar[section] = {\n title: section,\n items: menuitem,\n };\n }\n\n const mutatedMenubar = JSON.parse(JSON.stringify(menubar));\n Array.from(Object.entries(mutatedMenubar)).forEach(([name, menu]) => {\n if (name === section) {\n menu.items = `${menu.items} ${menuitem}`;\n }\n });\n\n return mutatedMenubar;\n};\n\n/**\n * Given a TinyMCE contextmenu configuration, add the specified button to the end.\n *\n * @param {string} contextmenu\n * @param {string[]} menuitems\n * @returns {string}\n */\nexport const addContextmenuItem = (contextmenu, ...menuitems) => {\n const contextmenuItems = (contextmenu ?? '').split(' ');\n\n return contextmenuItems\n .concat(menuitems)\n .filter((item) => item !== '')\n .join(' ');\n};\n\n/**\n * Given a TinyMCE quickbars configuration, add items to the meun.\n *\n * @param {string} toolbar\n * @param {string[]} menuitems\n * @returns {string}\n */\n// eslint-disable-next-line no-unused-vars\nexport const addQuickbarsToolbarItem = (toolbar, ...menuitems) => {\n // For the moment we have disabled use of this menu.\n // The configuration is left in place to allow plugins to declare that they would like to support it in the future.\n return toolbar;\n};\n\n/**\n * Get the link to the user documentation for the named plugin.\n *\n * @param {string} pluginName\n * @returns {string}\n */\nexport const getDocumentationLink = (pluginName) => `https://docs.moodle.org/en/editor_tiny/${pluginName}`;\n\n/**\n * Get the default plugin metadata for the named plugin.\n * If no URL is provided, then a URL is generated pointing to the standard Moodle Documentation.\n *\n * @param {string} component The component name\n * @param {string} pluginName The plugin name\n * @param {string|null} [url=null] An optional URL to the plugin documentation\n * @returns {object}\n */\nexport const getPluginMetadata = async(component, pluginName, url = null) => {\n const name = await getString('helplinktext', component);\n return {\n getMetadata: () => ({\n name,\n url: url ?? getDocumentationLink(pluginName),\n }),\n };\n};\n\n/**\n * Ensure that the editor is still in the DOM, removing it if it is not.\n *\n * @param {TinyMCE} editor\n * @returns {TinyMCE|null}\n */\nexport const ensureEditorIsValid = (editor) => {\n // TinyMCE uses the element ID as a map key internally, even if the target has changed.\n // In cases such as where an editor is in a modal form which has been detached from the DOM, but the editor not removed,\n // we need to manually destroy the editor.\n // We could theoretically do this with a Mutation Observer, but in some cases the Node may be moved,\n // or added back elsewhere in the DOM.\n if (!editor.getElement().isConnected) {\n return null;\n }\n\n return editor;\n};\n\n/**\n * Given a TinyMCE Toolbar configuration, remove the specified button from the named section.\n *\n * @param {object} toolbar\n * @param {string} section\n * @param {string} button\n * @returns {object} The toolbar configuration\n */\n export const removeToolbarButton = (toolbar, section, button) => {\n if (!toolbar) {\n return [{\n name: section,\n items: [button],\n }];\n }\n\n const mutatedToolbar = JSON.parse(JSON.stringify(toolbar));\n return mutatedToolbar.map((item) => {\n if (item.name === section) {\n item.items.splice(item.items.indexOf(button), 1);\n }\n\n return item;\n });\n};\n\n/**\n * Given a TinyMCE Toolbar configuration, remove the specified buttons from the named section.\n *\n * @param {object} toolbar\n * @param {string} section\n * @param {Array} buttons\n * @returns {object} The toolbar configuration\n */\n export const removeToolbarButtons = (toolbar, section, buttons) => {\n if (!toolbar) {\n return [{\n name: section,\n items: buttons,\n }];\n }\n\n const mutatedToolbar = JSON.parse(JSON.stringify(toolbar));\n return mutatedToolbar.map((item) => {\n if (item.name === section) {\n buttons.forEach(button => item.items.splice(item.items.indexOf(button), 1));\n }\n\n return item;\n });\n};\n\n/**\n * Remove the specified sub-menu item from the named section.\n * Recreate a menu with the same sub-menu items but remove the specified item.\n *\n * @param {TinyMCE} editor\n * @param {string} section\n * @param {string} submenuitem The text of sub-menu that we want to removed\n */\nexport const removeSubmenuItem = async(editor, section, submenuitem) => {\n // Get menu items.\n const menuItems = editor.ui.registry.getAll().menuItems[section];\n\n // Because we will match between title strings,\n // we make sure no problems arise while applying multi-language.\n const submenuitemtitle = await getString(submenuitem, 'editor_tiny');\n\n // Overriding the menu items,\n // by recreating them but excluding the specified sub-menu.\n if (menuItems) {\n editor.ui.registry.addNestedMenuItem(\n section,\n {\n text: menuItems.text,\n getSubmenuItems: () => {\n let newSubmenu = [];\n menuItems.getSubmenuItems().forEach((item) => {\n // Need to trim the text because some of the sub-menus use space to replace an icon.\n if (item.text.trim() != submenuitemtitle) {\n newSubmenu.push(item);\n }\n });\n return newSubmenu;\n }\n }\n );\n }\n};\n\n/**\n * Given a TinyMCE Menubar configuration, remove the specified menu from the named section.\n *\n * @param {string} menubar\n * @param {string} section\n * @param {string} menuitem\n * @returns {object}\n */\nexport const removeMenubarItem = (menubar, section, menuitem) => {\n menubar[section].items = menubar[section].items\n .replace(menuitem, '');\n\n return menubar;\n};\n\n/**\n * Given a TinyMCE Menubar configuration, remove the specified menu from the named section.\n *\n * @param {string} menubar\n * @param {string} section\n * @param {Array} menuitems\n * @returns {object}\n */\nexport const removeMenubarItems = (menubar, section, menuitems) => {\n // Create RegExp pattern.\n const regexPattern = new RegExp(menuitems.join('|'), \"ig\");\n\n // Remove menuitems.\n menubar[section].items = menubar[section].items.replace(regexPattern, '');\n\n return menubar;\n};\n\n/**\n * Updates the state of the editor.\n *\n * @param {TinyMCE} editor\n * @param {HTMLElement} target\n */\nexport const updateEditorState = (editor, target) => {\n if (target.hasAttribute('readonly')) {\n editor.mode.set(\"readonly\");\n } else {\n editor.mode.set(\"design\");\n }\n};\n"],"names":["getImagePath","identifier","component","Promise","resolve","M","util","image_url","async","image","editor","filetype","reject","configuration","options","formcallback","core_filepicker","show","Y","toolbar","section","button","name","items","JSON","parse","stringify","map","item","push","buttons","forEach","relativeTo","append","newSection","sectionInserted","some","index","splice","unshift","menubar","menuitem","title","mutatedMenubar","Array","from","Object","entries","_ref","menu","contextmenu","contextmenuItems","split","menuitems","concat","filter","join","getDocumentationLink","pluginName","url","getMetadata","getElement","isConnected","indexOf","submenuitem","menuItems","ui","registry","getAll","submenuitemtitle","addNestedMenuItem","text","getSubmenuItems","newSubmenu","trim","replace","regexPattern","RegExp","target","hasAttribute","mode","set"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"utils.min.js","sources":["../src/utils.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\nimport {renderForPromise} from 'core/templates';\nimport {getFilePicker} from './options';\nimport {getString} from 'core/str';\n\n/**\n * Get the image path for the specified image.\n *\n * @param {string} identifier The name of the image\n * @param {string} component The component name\n * @return {string} The image URL path\n */\nexport const getImagePath = (identifier, component = 'editor_tiny') => Promise.resolve(M.util.image_url(identifier, component));\n\nexport const getButtonImage = async(identifier, component = 'editor_tiny') => renderForPromise('editor_tiny/toolbar_button', {\n image: await getImagePath(identifier, component),\n});\n\n/**\n * Helper to display a filepicker and return a Promise.\n *\n * The Promise will resolve when a file is selected, or reject if the file type is not found.\n *\n * @param {TinyMCE} editor\n * @param {string} filetype\n * @returns {Promise} The file object returned by the filepicker\n */\nexport const displayFilepicker = (editor, filetype) => new Promise((resolve, reject) => {\n const configuration = getFilePicker(editor, filetype);\n if (configuration) {\n const options = {\n ...configuration,\n formcallback: resolve,\n };\n M.core_filepicker.show(Y, options);\n return;\n }\n reject(`Unknown filetype ${filetype}`);\n});\n\n/**\n * Given a TinyMCE Toolbar configuration, add the specified button to the named section.\n *\n * @param {object} toolbar\n * @param {string} section\n * @param {string} button\n * @returns {object} The toolbar configuration\n */\nexport const addToolbarButton = (toolbar, section, button) => {\n if (!toolbar) {\n return [{\n name: section,\n items: [button],\n }];\n }\n\n const mutatedToolbar = JSON.parse(JSON.stringify(toolbar));\n return mutatedToolbar.map((item) => {\n if (item.name === section) {\n item.items.push(button);\n }\n\n return item;\n });\n};\n\n/**\n * Given a TinyMCE Toolbar configuration, add the specified buttons to the named section.\n *\n * @param {object} toolbar\n * @param {string} section\n * @param {Array} buttons\n * @returns {object} The toolbar configuration\n */\nexport const addToolbarButtons = (toolbar, section, buttons) => {\n if (!toolbar) {\n return [{\n name: section,\n items: buttons,\n }];\n }\n\n const mutatedToolbar = JSON.parse(JSON.stringify(toolbar));\n return mutatedToolbar.map((item) => {\n if (item.name === section) {\n buttons.forEach(button => item.items.push(button));\n }\n\n return item;\n });\n};\n\n/**\n * Insert a new section into the toolbar.\n *\n * @param {array} toolbar The TinyMCE.editor.settings.toolbar configuration\n * @param {string} name The new section name to add\n * @param {string} relativeTo Insert relative to this section name\n * @param {boolean} append Append or Prepend\n * @returns {array}\n */\nexport const addToolbarSection = (toolbar, name, relativeTo, append = true) => {\n const newSection = {\n name,\n items: [],\n };\n const sectionInserted = toolbar.some((section, index) => {\n if (section.name === relativeTo) {\n if (append) {\n toolbar.splice(index + 1, 0, newSection);\n } else {\n toolbar.splice(index, 0, newSection);\n }\n return true;\n }\n return false;\n });\n\n if (!sectionInserted) {\n // Relative section not found.\n if (append) {\n toolbar.push(newSection);\n } else {\n toolbar.unshift(newSection);\n }\n }\n\n return toolbar;\n};\n\n/**\n * Given a TinyMCE Menubar configuration, add the specified button to the named section.\n *\n * @param {object} menubar\n * @param {string} section\n * @param {string} menuitem\n * @returns {object}\n */\nexport const addMenubarItem = (menubar, section, menuitem) => {\n if (!menubar) {\n const emptyMenubar = {};\n emptyMenubar[section] = {\n title: section,\n items: menuitem,\n };\n }\n\n const mutatedMenubar = JSON.parse(JSON.stringify(menubar));\n Array.from(Object.entries(mutatedMenubar)).forEach(([name, menu]) => {\n if (name === section) {\n menu.items = `${menu.items} ${menuitem}`;\n }\n });\n\n return mutatedMenubar;\n};\n\n/**\n * Given a TinyMCE contextmenu configuration, add the specified button to the end.\n *\n * @param {string} contextmenu\n * @param {string[]} menuitems\n * @returns {string}\n */\nexport const addContextmenuItem = (contextmenu, ...menuitems) => {\n const contextmenuItems = (contextmenu ?? '').split(' ');\n\n return contextmenuItems\n .concat(menuitems)\n .filter((item) => item !== '')\n .join(' ');\n};\n\n/**\n * Given a TinyMCE quickbars configuration, add items to the meun.\n *\n * @param {string} toolbar\n * @param {string[]} menuitems\n * @returns {string}\n */\n// eslint-disable-next-line no-unused-vars\nexport const addQuickbarsToolbarItem = (toolbar, ...menuitems) => {\n // For the moment we have disabled use of this menu.\n // The configuration is left in place to allow plugins to declare that they would like to support it in the future.\n return toolbar;\n};\n\n/**\n * Get the link to the user documentation for the named plugin.\n *\n * @param {string} pluginName\n * @returns {string}\n */\nexport const getDocumentationLink = (pluginName) => `https://docs.moodle.org/en/editor_tiny/${pluginName}`;\n\n/**\n * Get the default plugin metadata for the named plugin.\n * If no URL is provided, then a URL is generated pointing to the standard Moodle Documentation.\n *\n * @param {string} component The component name\n * @param {string} pluginName The plugin name\n * @param {string|null} [url=null] An optional URL to the plugin documentation\n * @returns {object}\n */\nexport const getPluginMetadata = async(component, pluginName, url = null) => {\n const name = await getString('helplinktext', component);\n return {\n getMetadata: () => ({\n name,\n url: url ?? getDocumentationLink(pluginName),\n }),\n };\n};\n\n/**\n * Ensure that the editor is still in the DOM, removing it if it is not.\n *\n * @param {TinyMCE} editor\n * @returns {TinyMCE|null}\n */\nexport const ensureEditorIsValid = (editor) => {\n // TinyMCE uses the element ID as a map key internally, even if the target has changed.\n // In cases such as where an editor is in a modal form which has been detached from the DOM, but the editor not removed,\n // we need to manually destroy the editor.\n // We could theoretically do this with a Mutation Observer, but in some cases the Node may be moved,\n // or added back elsewhere in the DOM.\n if (!editor.getElement().isConnected) {\n return null;\n }\n\n return editor;\n};\n\n/**\n * Given a TinyMCE Toolbar configuration, remove the specified button from the named section.\n *\n * @param {object} toolbar\n * @param {string} section\n * @param {string} button\n * @returns {object} The toolbar configuration\n */\n export const removeToolbarButton = (toolbar, section, button) => {\n if (!toolbar) {\n return [{\n name: section,\n items: [button],\n }];\n }\n\n const mutatedToolbar = JSON.parse(JSON.stringify(toolbar));\n return mutatedToolbar.map((item) => {\n if (item.name === section) {\n item.items.splice(item.items.indexOf(button), 1);\n }\n\n return item;\n });\n};\n\n/**\n * Given a TinyMCE Toolbar configuration, remove the specified buttons from the named section.\n *\n * @param {object} toolbar\n * @param {string} section\n * @param {Array} buttons\n * @returns {object} The toolbar configuration\n */\n export const removeToolbarButtons = (toolbar, section, buttons) => {\n if (!toolbar) {\n return [{\n name: section,\n items: buttons,\n }];\n }\n\n const mutatedToolbar = JSON.parse(JSON.stringify(toolbar));\n return mutatedToolbar.map((item) => {\n if (item.name === section) {\n buttons.forEach(button => item.items.splice(item.items.indexOf(button), 1));\n }\n\n return item;\n });\n};\n\n/**\n * Remove the specified sub-menu item from the named section.\n * Recreate a menu with the same sub-menu items but remove the specified item.\n *\n * @param {TinyMCE} editor\n * @param {string} section\n * @param {string} submenuitem The text of sub-menu that we want to removed\n */\nexport const removeSubmenuItem = async(editor, section, submenuitem) => {\n // Get menu items.\n const menuItems = editor.ui.registry.getAll().menuItems[section];\n\n // Because we will match between title strings,\n // we make sure no problems arise while applying multi-language.\n const submenuitemtitle = await getString(submenuitem, 'editor_tiny');\n\n // Overriding the menu items,\n // by recreating them but excluding the specified sub-menu.\n if (menuItems) {\n editor.ui.registry.addNestedMenuItem(\n section,\n {\n text: menuItems.text,\n getSubmenuItems: () => {\n let newSubmenu = [];\n menuItems.getSubmenuItems().forEach((item) => {\n // Need to trim the text because some of the sub-menus use space to replace an icon.\n if (item.text.trim() != submenuitemtitle) {\n newSubmenu.push(item);\n }\n });\n return newSubmenu;\n }\n }\n );\n }\n};\n\n/**\n * Given a TinyMCE Menubar configuration, remove the specified menu from the named section.\n *\n * @param {string} menubar\n * @param {string} section\n * @param {string} menuitem\n * @returns {object}\n */\nexport const removeMenubarItem = (menubar, section, menuitem) => {\n menubar[section].items = menubar[section].items\n .replace(menuitem, '');\n\n return menubar;\n};\n\n/**\n * Given a TinyMCE Menubar configuration, remove the specified menu from the named section.\n *\n * @param {string} menubar\n * @param {string} section\n * @param {Array} menuitems\n * @returns {object}\n */\nexport const removeMenubarItems = (menubar, section, menuitems) => {\n // Create RegExp pattern.\n const regexPattern = new RegExp(menuitems.join('|'), \"ig\");\n\n // Remove menuitems.\n menubar[section].items = menubar[section].items.replace(regexPattern, '');\n\n return menubar;\n};\n\n/**\n * Updates the state of the editor.\n *\n * @param {TinyMCE} editor\n * @param {HTMLElement} target\n */\nexport const updateEditorState = (editor, target) => {\n if (target.hasAttribute('readonly')) {\n editor.mode.set(\"readonly\");\n } else {\n editor.mode.set(\"design\");\n }\n};\n"],"names":["getImagePath","identifier","component","Promise","resolve","M","util","image_url","async","image","editor","filetype","reject","configuration","options","formcallback","core_filepicker","show","Y","toolbar","section","button","name","items","JSON","parse","stringify","map","item","push","buttons","forEach","relativeTo","append","newSection","sectionInserted","some","index","splice","unshift","menubar","menuitem","title","mutatedMenubar","Array","from","Object","entries","_ref","menu","contextmenu","contextmenuItems","split","menuitems","concat","filter","join","getDocumentationLink","pluginName","url","getMetadata","getElement","isConnected","indexOf","submenuitem","menuItems","ui","registry","getAll","submenuitemtitle","addNestedMenuItem","text","getSubmenuItems","newSubmenu","trim","replace","regexPattern","RegExp","target","hasAttribute","mode","set"],"mappings":"6qBA0BaA,aAAe,SAACC,gBAAYC,iEAAY,qBAAkBC,QAAQC,QAAQC,EAAEC,KAAKC,UAAUN,WAAYC,wEAEtFM,eAAMP,gBAAYC,iEAAY,qBAAkB,+BAAiB,6BAA8B,CACzHO,YAAaT,aAAaC,WAAYC,yCAYT,CAACQ,OAAQC,WAAa,IAAIR,SAAQ,CAACC,QAASQ,gBACnEC,eAAgB,0BAAcH,OAAQC,aACxCE,qBACMC,QAAU,IACTD,cACHE,aAAcX,SAElBC,EAAEW,gBAAgBC,KAAKC,EAAGJ,cAG9BF,kCAA2BD,wCAWC,CAACQ,QAASC,QAASC,cAC1CF,cACM,CAAC,CACJG,KAAMF,QACNG,MAAO,CAACF,iBAIOG,KAAKC,MAAMD,KAAKE,UAAUP,UAC3BQ,KAAKC,OACnBA,KAAKN,OAASF,SACdQ,KAAKL,MAAMM,KAAKR,QAGbO,oCAYkB,CAACT,QAASC,QAASU,eAC3CX,cACM,CAAC,CACJG,KAAMF,QACNG,MAAOO,iBAIQN,KAAKC,MAAMD,KAAKE,UAAUP,UAC3BQ,KAAKC,OACnBA,KAAKN,OAASF,SACdU,QAAQC,SAAQV,QAAUO,KAAKL,MAAMM,KAAKR,UAGvCO,oCAakB,SAACT,QAASG,KAAMU,gBAAYC,wEACnDC,WAAa,CACfZ,KAAAA,KACAC,MAAO,IAELY,gBAAkBhB,QAAQiB,MAAK,CAAChB,QAASiB,QACvCjB,QAAQE,OAASU,aACbC,OACAd,QAAQmB,OAAOD,MAAQ,EAAG,EAAGH,YAE7Bf,QAAQmB,OAAOD,MAAO,EAAGH,aAEtB,YAKVC,kBAEGF,OACAd,QAAQU,KAAKK,YAEbf,QAAQoB,QAAQL,aAIjBf,iCAWmB,CAACqB,QAASpB,QAASqB,gBACxCD,QAAS,EACW,IACRpB,SAAW,CACpBsB,MAAOtB,QACPG,MAAOkB,gBAITE,eAAiBnB,KAAKC,MAAMD,KAAKE,UAAUc,iBACjDI,MAAMC,KAAKC,OAAOC,QAAQJ,iBAAiBZ,SAAQiB,WAAE1B,KAAM2B,WACnD3B,OAASF,UACT6B,KAAK1B,gBAAW0B,KAAK1B,kBAASkB,cAI/BE,4CAUuB,SAACO,mBACzBC,kBAAoBD,MAAAA,YAAAA,YAAe,IAAIE,MAAM,mCADJC,6DAAAA,yCAGxCF,iBACFG,OAAOD,WACPE,QAAQ3B,MAAkB,KAATA,OACjB4B,KAAK,uCAWyB,SAACrC,gBAG7BA,eASEsC,qBAAwBC,6DAAyDA,0FAW7DlD,eAAMN,UAAWwD,gBAAYC,2DAAM,WAC1DrC,WAAa,kBAAU,eAAgBpB,iBACtC,CACH0D,YAAa,MACTtC,KAAAA,KACAqC,IAAKA,MAAAA,IAAAA,IAAOF,qBAAqBC,6CAWThD,QAM3BA,OAAOmD,aAAaC,YAIlBpD,OAHI,kCAcqB,CAACS,QAASC,QAASC,cAC9CF,cACM,CAAC,CACJG,KAAMF,QACNG,MAAO,CAACF,iBAIOG,KAAKC,MAAMD,KAAKE,UAAUP,UAC3BQ,KAAKC,OACnBA,KAAKN,OAASF,SACdQ,KAAKL,MAAMe,OAAOV,KAAKL,MAAMwC,QAAQ1C,QAAS,GAG3CO,uCAYsB,CAACT,QAASC,QAASU,eAC/CX,cACM,CAAC,CACJG,KAAMF,QACNG,MAAOO,iBAIQN,KAAKC,MAAMD,KAAKE,UAAUP,UAC3BQ,KAAKC,OACnBA,KAAKN,OAASF,SACdU,QAAQC,SAAQV,QAAUO,KAAKL,MAAMe,OAAOV,KAAKL,MAAMwC,QAAQ1C,QAAS,KAGrEO,oCAYkBpB,MAAME,OAAQU,QAAS4C,qBAE9CC,UAAYvD,OAAOwD,GAAGC,SAASC,SAASH,UAAU7C,SAIlDiD,uBAAyB,kBAAUL,YAAa,eAIlDC,WACAvD,OAAOwD,GAAGC,SAASG,kBACflD,QACA,CACImD,KAAMN,UAAUM,KAChBC,gBAAiB,SACTC,WAAa,UACjBR,UAAUO,kBAAkBzC,SAASH,OAE7BA,KAAK2C,KAAKG,QAAUL,kBACpBI,WAAW5C,KAAKD,SAGjB6C,0CAeM,CAACjC,QAASpB,QAASqB,YAChDD,QAAQpB,SAASG,MAAQiB,QAAQpB,SAASG,MACrCoD,QAAQlC,SAAU,IAEhBD,qCAWuB,CAACA,QAASpB,QAASiC,mBAE3CuB,aAAe,IAAIC,OAAOxB,UAAUG,KAAK,KAAM,aAGrDhB,QAAQpB,SAASG,MAAQiB,QAAQpB,SAASG,MAAMoD,QAAQC,aAAc,IAE/DpC,oCASsB,CAAC9B,OAAQoE,UAClCA,OAAOC,aAAa,YACpBrE,OAAOsE,KAAKC,IAAI,YAEhBvE,OAAOsE,KAAKC,IAAI"} \ No newline at end of file diff --git a/lib/editor/tiny/amd/src/utils.js b/lib/editor/tiny/amd/src/utils.js index ebaeb78be4a..4ae73e8bbc7 100644 --- a/lib/editor/tiny/amd/src/utils.js +++ b/lib/editor/tiny/amd/src/utils.js @@ -15,7 +15,7 @@ import {renderForPromise} from 'core/templates'; import {getFilePicker} from './options'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; /** * Get the image path for the specified image. diff --git a/lib/editor/tiny/plugins/accessibilitychecker/amd/build/checker.min.js b/lib/editor/tiny/plugins/accessibilitychecker/amd/build/checker.min.js index 59a1afeb5b1..ef6c68be148 100644 --- a/lib/editor/tiny/plugins/accessibilitychecker/amd/build/checker.min.js +++ b/lib/editor/tiny/plugins/accessibilitychecker/amd/build/checker.min.js @@ -3,6 +3,6 @@ define("tiny_accessibilitychecker/checker",["exports","core/templates","core/str * @package tiny_accessibilitychecker * @copyright 2022, Stevani Andolo * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),Modal=_interopRequireWildcard(Modal),ModalEvents=_interopRequireWildcard(ModalEvents),_colorbase=_interopRequireDefault(_colorbase);return _exports.default=class{constructor(editor){this.editor=editor,this.colorBase=new _colorbase.default,this.modal=null,this.placeholderSelectors=null;const placeholders=(0,_options.getPlaceholderSelectors)(this.editor);placeholders.length&&(this.placeholderSelectors=placeholders.join(", "))}destroy(){delete this.editor,delete this.colorBase,this.modal.destroy(),delete this.modal}async displayDialogue(){this.modal=await Modal.create({type:Modal.types.DEFAULT,large:!0,title:(0,_str.get_string)("pluginname",_common.component),body:this.getDialogueContent()}),this.modal.getRoot().on(ModalEvents.hidden,(()=>this.destroy())),this.modal.getRoot()[0].addEventListener("click",(event=>{const faultLink=event.target.closest('[data-action="highlightfault"]');if(!faultLink)return;event.preventDefault();const nodeName=faultLink.dataset.nodeName;let selectedNode=null;if(nodeName)if(nodeName.includes(",")||"body"===nodeName)selectedNode=this.editor.dom.select("body")[0];else{var _faultLink$dataset$no;const nodeIndex=null!==(_faultLink$dataset$no=faultLink.dataset.nodeIndex)&&void 0!==_faultLink$dataset$no?_faultLink$dataset$no:0;selectedNode=this.editor.dom.select(nodeName)[nodeIndex]}selectedNode&&"BODY"!==selectedNode.nodeName.toUpperCase()&&this.selectAndScroll(selectedNode),this.modal.hide()})),this.modal.show()}async getAllWarningStrings(){const keys=["emptytext","entiredocument","imagesmissingalt","needsmorecontrast","needsmoreheadings","tablesmissingcaption","tablesmissingheaders","tableswithmergedcells"],stringValues=await(0,_str.get_strings)(keys.map((key=>({key:key,component:_common.component}))));return new Map(keys.map(((key,index)=>[key,stringValues[index]])))}async getDialogueContent(){const langStrings=await this.getAllWarningStrings(),warnings=this.getWarnings().map((warning=>(warning.description&&("langstring"===warning.description.type?warning.description=langStrings.get(warning.description.value):warning.description=warning.description.value),warning.nodeData=warning.nodeData.map((problemNode=>(problemNode.text&&("langstring"===problemNode.text.type?problemNode.text=langStrings.get(problemNode.text.value):problemNode.text=problemNode.text.value),problemNode))),warning)));return _templates.default.render("tiny_accessibilitychecker/warning_content",{warnings:warnings})}selectAndScroll(node){this.editor.selection.select(node).scrollIntoView({behavior:"smooth",block:"nearest"})}getWarnings(){const warnings=[];return warnings.push(this.createWarnings("imagesmissingalt",this.checkImage(),!0)),warnings.push(this.createWarnings("needsmorecontrast",this.checkOtherElements(),!1)),this.editor.getContent({format:"text"}).length>1e3&&this.editor.dom.select("h3,h4,h5").length<1&&warnings.push(this.createWarnings("needsmoreheadings",[this.editor],!1)),warnings.push(this.createWarnings("tablesmissingcaption",this.checkTableCaption(),!1)),warnings.push(this.createWarnings("tableswithmergedcells",this.checkTableMergedCells(),!1)),warnings.push(this.createWarnings("tablesmissingheaders",this.checkTableHeaders(),!1)),warnings.filter((warning=>warning.nodeData.length>0))}createWarnings(description,nodes,isImageType){const getTextValue=node=>{if(node===this.editor)return{type:"langstring",value:"entiredocument"};const emptyStringValue={type:"langstring",value:"emptytext"};if("innerText"in node){const value=node.innerText.trim();return value.length?{type:"raw",value:value}:emptyStringValue}if("textContent"in node){const value=node.textContent.trim();return value.length?{type:"raw",value:value}:emptyStringValue}return{type:"raw",value:node.nodeName}},getEventualNode=node=>{if(node!==this.editor)return node;const childNodes=node.dom.select("body")[0].childNodes;return childNodes.length?document.body:childNodes},warning={description:{type:"langstring",value:description},nodeData:[]};return warning.nodeData=[...nodes].filter((node=>node!==this.editor&&this.placeholderSelectors?!1===node.matches(this.placeholderSelectors):node)).map((node=>{const describedNode=getEventualNode(node),nodeIndex=this.editor.dom.select(describedNode.nodeName).indexOf(describedNode),warning={src:null,text:null,nodeName:describedNode.nodeName,nodeIndex:nodeIndex};return isImageType?warning.src=node.getAttribute("src"):warning.text=getTextValue(node),warning})),warning}checkImage(){const problemNodes=[];return this.editor.dom.select("img").forEach((img=>{img.getAttribute("alt")||"presentation"===img.getAttribute("role")||problemNodes.push(img)})),problemNodes}checkTableCaption(){const problemNodes=[];return this.editor.dom.select("table").forEach((table=>{const caption=table.querySelector("caption");null!=caption&&caption.textContent.trim()||problemNodes.push(table)})),problemNodes}checkOtherElements(){const problemNodes=[];return this.editor.dom.select("body *").filter((node=>node.hasChildNodes()&&null!==node.childNodes[0].nodeValue)).forEach((node=>{const foreground=this.colorBase.fromArray(this.getComputedBackgroundColor(node,window.getComputedStyle(node,null).getPropertyValue("color")),this.colorBase.TYPES.RGBA),background=this.colorBase.fromArray(this.getComputedBackgroundColor(node),this.colorBase.TYPES.RGBA),lum1=this.getLuminanceFromCssColor(foreground),lum2=this.getLuminanceFromCssColor(background),ratio=((lum1,lum2)=>lum1>lum2?(lum1+.05)/(lum2+.05):(lum2+.05)/(lum1+.05))(lum1,lum2);ratio<=4.5&&(window.console.log("\n Contrast ratio is too low: ".concat(ratio,"\n Colour 1: ").concat(foreground,"\n Colour 2: ").concat(background,"\n Luminance 1: ").concat(lum1,"\n Luminance 2: ").concat(lum2,"\n ")),problemNodes.find((existingProblemNode=>existingProblemNode.contains(node)))||problemNodes.push(node))})),problemNodes}checkTableMergedCells(){const problemNodes=[];return this.editor.dom.select("table").forEach((table=>{table.querySelectorAll("[colspan], [rowspan]").length&&problemNodes.push(table)})),problemNodes}checkTableHeaders(){const problemNodes=[];return this.editor.dom.select("table").forEach((table=>{if(table.querySelector("tr").querySelector("td")){[...table.querySelectorAll("tr")].some((row=>{const header=row.querySelector("th");return!header||!header.textContent.trim()}))&&problemNodes.push(table)}else[...table.querySelectorAll("tr th")].some((header=>!header.textContent.trim()))&&problemNodes.push(table)})),problemNodes}getLuminanceFromCssColor(colortext){"transparent"===colortext&&(colortext="#ffffff");const color=this.colorBase.toArray(this.colorBase.toRGB(colortext)),part1=a=>((a=parseInt(a,10)/255)<=.03928?a/=12.92:a=Math.pow((a+.055)/1.055,2.4),a);return.2126*part1(color[0])+.7152*part1(color[1])+.0722*part1(color[2])}getComputedBackgroundColor(node,color){if(!node.parentNode)return this.colorBase.toArray("rgba(255, 255, 255, 1)");"rgba(0, 0, 0, 0)"!==(color=color||window.getComputedStyle(node,null).getPropertyValue("background-color")).toLowerCase()&&"transparent"!==color.toLowerCase()||(color="rgba(1, 1, 1, 0)");const colorParts=this.colorBase.toArray(color),alpha=colorParts[3];if(1===alpha)return colorParts;const parentColor=this.getComputedBackgroundColor(node.parentNode);return[(1-alpha)*parentColor[0]+alpha*colorParts[0],(1-alpha)*parentColor[1]+alpha*colorParts[1],(1-alpha)*parentColor[2]+alpha*colorParts[2],1]}},_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),Modal=_interopRequireWildcard(Modal),ModalEvents=_interopRequireWildcard(ModalEvents),_colorbase=_interopRequireDefault(_colorbase);return _exports.default=class{constructor(editor){this.editor=editor,this.colorBase=new _colorbase.default,this.modal=null,this.placeholderSelectors=null;const placeholders=(0,_options.getPlaceholderSelectors)(this.editor);placeholders.length&&(this.placeholderSelectors=placeholders.join(", "))}destroy(){delete this.editor,delete this.colorBase,this.modal.destroy(),delete this.modal}async displayDialogue(){this.modal=await Modal.create({type:Modal.types.DEFAULT,large:!0,title:(0,_str.getString)("pluginname",_common.component),body:this.getDialogueContent()}),this.modal.getRoot().on(ModalEvents.hidden,(()=>this.destroy())),this.modal.getRoot()[0].addEventListener("click",(event=>{const faultLink=event.target.closest('[data-action="highlightfault"]');if(!faultLink)return;event.preventDefault();const nodeName=faultLink.dataset.nodeName;let selectedNode=null;if(nodeName)if(nodeName.includes(",")||"body"===nodeName)selectedNode=this.editor.dom.select("body")[0];else{var _faultLink$dataset$no;const nodeIndex=null!==(_faultLink$dataset$no=faultLink.dataset.nodeIndex)&&void 0!==_faultLink$dataset$no?_faultLink$dataset$no:0;selectedNode=this.editor.dom.select(nodeName)[nodeIndex]}selectedNode&&"BODY"!==selectedNode.nodeName.toUpperCase()&&this.selectAndScroll(selectedNode),this.modal.hide()})),this.modal.show()}async getAllWarningStrings(){const keys=["emptytext","entiredocument","imagesmissingalt","needsmorecontrast","needsmoreheadings","tablesmissingcaption","tablesmissingheaders","tableswithmergedcells"],stringValues=await(0,_str.getStrings)(keys.map((key=>({key:key,component:_common.component}))));return new Map(keys.map(((key,index)=>[key,stringValues[index]])))}async getDialogueContent(){const langStrings=await this.getAllWarningStrings(),warnings=this.getWarnings().map((warning=>(warning.description&&("langstring"===warning.description.type?warning.description=langStrings.get(warning.description.value):warning.description=warning.description.value),warning.nodeData=warning.nodeData.map((problemNode=>(problemNode.text&&("langstring"===problemNode.text.type?problemNode.text=langStrings.get(problemNode.text.value):problemNode.text=problemNode.text.value),problemNode))),warning)));return _templates.default.render("tiny_accessibilitychecker/warning_content",{warnings:warnings})}selectAndScroll(node){this.editor.selection.select(node).scrollIntoView({behavior:"smooth",block:"nearest"})}getWarnings(){const warnings=[];return warnings.push(this.createWarnings("imagesmissingalt",this.checkImage(),!0)),warnings.push(this.createWarnings("needsmorecontrast",this.checkOtherElements(),!1)),this.editor.getContent({format:"text"}).length>1e3&&this.editor.dom.select("h3,h4,h5").length<1&&warnings.push(this.createWarnings("needsmoreheadings",[this.editor],!1)),warnings.push(this.createWarnings("tablesmissingcaption",this.checkTableCaption(),!1)),warnings.push(this.createWarnings("tableswithmergedcells",this.checkTableMergedCells(),!1)),warnings.push(this.createWarnings("tablesmissingheaders",this.checkTableHeaders(),!1)),warnings.filter((warning=>warning.nodeData.length>0))}createWarnings(description,nodes,isImageType){const getTextValue=node=>{if(node===this.editor)return{type:"langstring",value:"entiredocument"};const emptyStringValue={type:"langstring",value:"emptytext"};if("innerText"in node){const value=node.innerText.trim();return value.length?{type:"raw",value:value}:emptyStringValue}if("textContent"in node){const value=node.textContent.trim();return value.length?{type:"raw",value:value}:emptyStringValue}return{type:"raw",value:node.nodeName}},getEventualNode=node=>{if(node!==this.editor)return node;const childNodes=node.dom.select("body")[0].childNodes;return childNodes.length?document.body:childNodes},warning={description:{type:"langstring",value:description},nodeData:[]};return warning.nodeData=[...nodes].filter((node=>node!==this.editor&&this.placeholderSelectors?!1===node.matches(this.placeholderSelectors):node)).map((node=>{const describedNode=getEventualNode(node),nodeIndex=this.editor.dom.select(describedNode.nodeName).indexOf(describedNode),warning={src:null,text:null,nodeName:describedNode.nodeName,nodeIndex:nodeIndex};return isImageType?warning.src=node.getAttribute("src"):warning.text=getTextValue(node),warning})),warning}checkImage(){const problemNodes=[];return this.editor.dom.select("img").forEach((img=>{img.getAttribute("alt")||"presentation"===img.getAttribute("role")||problemNodes.push(img)})),problemNodes}checkTableCaption(){const problemNodes=[];return this.editor.dom.select("table").forEach((table=>{const caption=table.querySelector("caption");null!=caption&&caption.textContent.trim()||problemNodes.push(table)})),problemNodes}checkOtherElements(){const problemNodes=[];return this.editor.dom.select("body *").filter((node=>node.hasChildNodes()&&null!==node.childNodes[0].nodeValue)).forEach((node=>{const foreground=this.colorBase.fromArray(this.getComputedBackgroundColor(node,window.getComputedStyle(node,null).getPropertyValue("color")),this.colorBase.TYPES.RGBA),background=this.colorBase.fromArray(this.getComputedBackgroundColor(node),this.colorBase.TYPES.RGBA),lum1=this.getLuminanceFromCssColor(foreground),lum2=this.getLuminanceFromCssColor(background),ratio=((lum1,lum2)=>lum1>lum2?(lum1+.05)/(lum2+.05):(lum2+.05)/(lum1+.05))(lum1,lum2);ratio<=4.5&&(window.console.log("\n Contrast ratio is too low: ".concat(ratio,"\n Colour 1: ").concat(foreground,"\n Colour 2: ").concat(background,"\n Luminance 1: ").concat(lum1,"\n Luminance 2: ").concat(lum2,"\n ")),problemNodes.find((existingProblemNode=>existingProblemNode.contains(node)))||problemNodes.push(node))})),problemNodes}checkTableMergedCells(){const problemNodes=[];return this.editor.dom.select("table").forEach((table=>{table.querySelectorAll("[colspan], [rowspan]").length&&problemNodes.push(table)})),problemNodes}checkTableHeaders(){const problemNodes=[];return this.editor.dom.select("table").forEach((table=>{if(table.querySelector("tr").querySelector("td")){[...table.querySelectorAll("tr")].some((row=>{const header=row.querySelector("th");return!header||!header.textContent.trim()}))&&problemNodes.push(table)}else[...table.querySelectorAll("tr th")].some((header=>!header.textContent.trim()))&&problemNodes.push(table)})),problemNodes}getLuminanceFromCssColor(colortext){"transparent"===colortext&&(colortext="#ffffff");const color=this.colorBase.toArray(this.colorBase.toRGB(colortext)),part1=a=>((a=parseInt(a,10)/255)<=.03928?a/=12.92:a=Math.pow((a+.055)/1.055,2.4),a);return.2126*part1(color[0])+.7152*part1(color[1])+.0722*part1(color[2])}getComputedBackgroundColor(node,color){if(!node.parentNode)return this.colorBase.toArray("rgba(255, 255, 255, 1)");"rgba(0, 0, 0, 0)"!==(color=color||window.getComputedStyle(node,null).getPropertyValue("background-color")).toLowerCase()&&"transparent"!==color.toLowerCase()||(color="rgba(1, 1, 1, 0)");const colorParts=this.colorBase.toArray(color),alpha=colorParts[3];if(1===alpha)return colorParts;const parentColor=this.getComputedBackgroundColor(node.parentNode);return[(1-alpha)*parentColor[0]+alpha*colorParts[0],(1-alpha)*parentColor[1]+alpha*colorParts[1],(1-alpha)*parentColor[2]+alpha*colorParts[2],1]}},_exports.default})); //# sourceMappingURL=checker.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/accessibilitychecker/amd/build/checker.min.js.map b/lib/editor/tiny/plugins/accessibilitychecker/amd/build/checker.min.js.map index 2cd2ce46b66..d61245df404 100644 --- a/lib/editor/tiny/plugins/accessibilitychecker/amd/build/checker.min.js.map +++ b/lib/editor/tiny/plugins/accessibilitychecker/amd/build/checker.min.js.map @@ -1 +1 @@ -{"version":3,"file":"checker.min.js","sources":["../src/checker.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 * @package tiny_accessibilitychecker\n * @copyright 2022, Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {get_string as getString, get_strings as getStrings} from 'core/str';\nimport {component} from './common';\nimport * as Modal from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport ColorBase from './colorbase';\nimport {getPlaceholderSelectors} from 'editor_tiny/options';\n\n/**\n * @typedef ProblemDetail\n * @type {object}\n * @param {string} description The description of the problem\n * @param {ProblemNode[]} problemNodes The list of affected nodes\n */\n\n/**\n * @typedef ProblemNode\n * @type {object}\n * @param {string} nodeName The node name for the affected node\n * @param {string} nodeIndex The indexd of the node\n * @param {string} text A description of the issue\n * @param {string} src The source of the image\n */\n\nexport default class {\n\n constructor(editor) {\n this.editor = editor;\n this.colorBase = new ColorBase();\n this.modal = null;\n this.placeholderSelectors = null;\n const placeholders = getPlaceholderSelectors(this.editor);\n if (placeholders.length) {\n this.placeholderSelectors = placeholders.join(', ');\n }\n }\n\n destroy() {\n delete this.editor;\n delete this.colorBase;\n\n this.modal.destroy();\n delete this.modal;\n }\n\n async displayDialogue() {\n this.modal = await Modal.create({\n type: Modal.types.DEFAULT,\n large: true,\n title: getString('pluginname', component),\n body: this.getDialogueContent()\n });\n\n // Destroy the class when hiding the modal.\n this.modal.getRoot().on(ModalEvents.hidden, () => this.destroy());\n\n this.modal.getRoot()[0].addEventListener('click', (event) => {\n const faultLink = event.target.closest('[data-action=\"highlightfault\"]');\n if (!faultLink) {\n return;\n }\n\n event.preventDefault();\n\n const nodeName = faultLink.dataset.nodeName;\n let selectedNode = null;\n if (nodeName) {\n if (nodeName.includes(',') || nodeName === 'body') {\n selectedNode = this.editor.dom.select('body')[0];\n } else {\n const nodeIndex = faultLink.dataset.nodeIndex ?? 0;\n selectedNode = this.editor.dom.select(nodeName)[nodeIndex];\n }\n }\n\n if (selectedNode && selectedNode.nodeName.toUpperCase() !== 'BODY') {\n this.selectAndScroll(selectedNode);\n }\n\n this.modal.hide();\n });\n\n this.modal.show();\n }\n\n async getAllWarningStrings() {\n const keys = [\n 'emptytext',\n 'entiredocument',\n 'imagesmissingalt',\n 'needsmorecontrast',\n 'needsmoreheadings',\n 'tablesmissingcaption',\n 'tablesmissingheaders',\n 'tableswithmergedcells',\n ];\n\n const stringValues = await getStrings(keys.map((key) => ({key, component})));\n return new Map(keys.map((key, index) => ([key, stringValues[index]])));\n }\n\n /**\n * Return the dialogue content.\n *\n * @return {Promise} A template promise containing the rendered dialogue content.\n */\n async getDialogueContent() {\n const langStrings = await this.getAllWarningStrings();\n\n // Translate langstrings into real strings.\n const warnings = this.getWarnings().map((warning) => {\n if (warning.description) {\n if (warning.description.type === 'langstring') {\n warning.description = langStrings.get(warning.description.value);\n } else {\n warning.description = warning.description.value;\n }\n }\n\n warning.nodeData = warning.nodeData.map((problemNode) => {\n if (problemNode.text) {\n if (problemNode.text.type === 'langstring') {\n problemNode.text = langStrings.get(problemNode.text.value);\n } else {\n problemNode.text = problemNode.text.value;\n }\n }\n\n return problemNode;\n });\n\n return warning;\n });\n\n return Templates.render('tiny_accessibilitychecker/warning_content', {\n warnings\n });\n }\n\n /**\n * Set the selection and scroll to the selected element.\n *\n * @param {node} node\n */\n selectAndScroll(node) {\n this.editor.selection.select(node).scrollIntoView({\n behavior: 'smooth',\n block: 'nearest'\n });\n }\n\n /**\n * Find all problems with the content editable region.\n *\n * @return {ProblemDetail[]} A complete list of all warnings and problems.\n */\n getWarnings() {\n const warnings = [];\n\n // Check Images with no alt text or dodgy alt text.\n warnings.push(this.createWarnings('imagesmissingalt', this.checkImage(), true));\n warnings.push(this.createWarnings('needsmorecontrast', this.checkOtherElements(), false));\n\n // Check for no headings.\n if (this.editor.getContent({format: 'text'}).length > 1000 && this.editor.dom.select('h3,h4,h5').length < 1) {\n warnings.push(this.createWarnings('needsmoreheadings', [this.editor], false));\n }\n\n // Check for tables with no captions.\n warnings.push(this.createWarnings('tablesmissingcaption', this.checkTableCaption(), false));\n\n // Check for tables with merged cells.\n warnings.push(this.createWarnings('tableswithmergedcells', this.checkTableMergedCells(), false));\n\n // Check for tables with no row/col headers.\n warnings.push(this.createWarnings('tablesmissingheaders', this.checkTableHeaders(), false));\n\n return warnings.filter((warning) => warning.nodeData.length > 0);\n }\n\n /**\n * Generate the data that describes the issues found.\n *\n * @param {String} description Description of this failure.\n * @param {HTMLElement[]} nodes An array of failing nodes.\n * @param {boolean} isImageType Whether the warnings are related to image type checks\n * @return {ProblemDetail[]} A set of problem details\n */\n createWarnings(description, nodes, isImageType) {\n const getTextValue = (node) => {\n if (node === this.editor) {\n return {\n type: 'langstring',\n value: 'entiredocument',\n };\n }\n\n const emptyStringValue = {\n type: 'langstring',\n value: 'emptytext',\n };\n if ('innerText' in node) {\n const value = node.innerText.trim();\n return value.length ? {type: 'raw', value} : emptyStringValue;\n } else if ('textContent' in node) {\n const value = node.textContent.trim();\n return value.length ? {type: 'raw', value} : emptyStringValue;\n }\n\n return {type: 'raw', value: node.nodeName};\n };\n\n const getEventualNode = (node) => {\n if (node !== this.editor) {\n return node;\n }\n const childNodes = node.dom.select('body')[0].childNodes;\n if (childNodes.length) {\n return document.body;\n } else {\n return childNodes;\n }\n };\n\n const warning = {\n description: {\n type: 'langstring',\n value: description,\n },\n nodeData: [],\n };\n\n warning.nodeData = [...nodes].filter((node) => {\n // If the failed node is a placeholder element. We should remove it from the list.\n if (node !== this.editor && this.placeholderSelectors) {\n return node.matches(this.placeholderSelectors) === false;\n }\n\n return node;\n }).map((node) => {\n const describedNode = getEventualNode(node);\n\n // Find the index of the node within the type of node.\n // This is used to select the correct node when the user selects it.\n const nodeIndex = this.editor.dom.select(describedNode.nodeName).indexOf(describedNode);\n const warning = {\n src: null,\n text: null,\n nodeName: describedNode.nodeName,\n nodeIndex,\n };\n\n if (isImageType) {\n warning.src = node.getAttribute('src');\n } else {\n warning.text = getTextValue(node);\n }\n\n return warning;\n });\n\n return warning;\n }\n\n /**\n * Check accessiblity issue only for img type.\n *\n * @return {Node} A complete list of all warnings and problems.\n */\n checkImage() {\n const problemNodes = [];\n this.editor.dom.select('img').forEach((img) => {\n const alt = img.getAttribute('alt');\n if (!alt && img.getAttribute('role') !== 'presentation') {\n problemNodes.push(img);\n }\n });\n return problemNodes;\n }\n\n /**\n * Look for any table without a caption.\n *\n * @return {Node} A complete list of all warnings and problems.\n */\n checkTableCaption() {\n const problemNodes = [];\n this.editor.dom.select('table').forEach((table) => {\n const caption = table.querySelector('caption');\n if (!caption?.textContent.trim()) {\n problemNodes.push(table);\n }\n });\n\n return problemNodes;\n }\n\n /**\n * Check accessiblity issue for not img and table only.\n *\n * @return {Node} A complete list of all warnings and problems.\n * @private\n */\n checkOtherElements() {\n const problemNodes = [];\n\n const getRatio = (lum1, lum2) => {\n // Algorithm from \"http://www.w3.org/TR/WCAG20-GENERAL/G18.html\".\n if (lum1 > lum2) {\n return (lum1 + 0.05) / (lum2 + 0.05);\n } else {\n return (lum2 + 0.05) / (lum1 + 0.05);\n }\n };\n\n this.editor.dom.select('body *')\n .filter((node) => node.hasChildNodes() && node.childNodes[0].nodeValue !== null)\n .forEach((node) => {\n const foreground = this.colorBase.fromArray(\n this.getComputedBackgroundColor(\n node,\n window.getComputedStyle(node, null).getPropertyValue('color')\n ),\n this.colorBase.TYPES.RGBA\n );\n const background = this.colorBase.fromArray(\n this.getComputedBackgroundColor(\n node\n ),\n this.colorBase.TYPES.RGBA\n );\n\n const lum1 = this.getLuminanceFromCssColor(foreground);\n const lum2 = this.getLuminanceFromCssColor(background);\n const ratio = getRatio(lum1, lum2);\n\n if (ratio <= 4.5) {\n window.console.log(`\n Contrast ratio is too low: ${ratio}\n Colour 1: ${foreground}\n Colour 2: ${background}\n Luminance 1: ${lum1}\n Luminance 2: ${lum2}\n `);\n\n // We only want the highest node with dodgy contrast reported.\n if (!problemNodes.find((existingProblemNode) => existingProblemNode.contains(node))) {\n problemNodes.push(node);\n }\n }\n });\n return problemNodes;\n }\n\n /**\n * Check accessiblity issue only for table with merged cells.\n *\n * @return {Node} A complete list of all warnings and problems.\n * @private\n */\n checkTableMergedCells() {\n const problemNodes = [];\n this.editor.dom.select('table').forEach((table) => {\n const rowcolspan = table.querySelectorAll('[colspan], [rowspan]');\n if (rowcolspan.length) {\n problemNodes.push(table);\n }\n });\n return problemNodes;\n }\n\n /**\n * Check accessiblity issue only for table with no headers.\n *\n * @return {Node} A complete list of all warnings and problems.\n * @private\n */\n checkTableHeaders() {\n const problemNodes = [];\n\n this.editor.dom.select('table').forEach((table) => {\n if (table.querySelector('tr').querySelector('td')) {\n // The first row has a non-header cell, so all rows must have at least one header.\n const missingHeader = [...table.querySelectorAll('tr')].some((row) => {\n const header = row.querySelector('th');\n if (!header) {\n return true;\n }\n\n if (!header.textContent.trim()) {\n return true;\n }\n\n return false;\n });\n if (missingHeader) {\n // At least one row is missing the header, or it is empty.\n problemNodes.push(table);\n }\n } else {\n // Every header must have some content.\n if ([...table.querySelectorAll('tr th')].some((header) => !header.textContent.trim())) {\n problemNodes.push(table);\n }\n }\n });\n return problemNodes;\n }\n\n /**\n * Convert a CSS color to a luminance value.\n *\n * @param {String} colortext The Hex value for the colour\n * @return {Number} The luminance value.\n * @private\n */\n getLuminanceFromCssColor(colortext) {\n if (colortext === 'transparent') {\n colortext = '#ffffff';\n }\n const color = this.colorBase.toArray(this.colorBase.toRGB(colortext));\n\n // Algorithm from \"http://www.w3.org/TR/WCAG20-GENERAL/G18.html\".\n const part1 = (a) => {\n a = parseInt(a, 10) / 255.0;\n if (a <= 0.03928) {\n a = a / 12.92;\n } else {\n a = Math.pow(((a + 0.055) / 1.055), 2.4);\n }\n return a;\n };\n\n const r1 = part1(color[0]);\n const g1 = part1(color[1]);\n const b1 = part1(color[2]);\n\n return 0.2126 * r1 + 0.7152 * g1 + 0.0722 * b1;\n }\n\n /**\n * Get the computed RGB converted to full alpha value, considering the node hierarchy.\n *\n * @param {Node} node\n * @param {String} color The initial colour. If not specified, fetches the backgroundColor from the node.\n * @return {Array} Colour in Array form (RGBA)\n * @private\n */\n getComputedBackgroundColor(node, color) {\n if (!node.parentNode) {\n // This is the document node and has no colour.\n // We cannot use window.getComputedStyle on the document.\n // If we got here, then the document has no background colour. Fall back to white.\n return this.colorBase.toArray('rgba(255, 255, 255, 1)');\n }\n color = color ? color : window.getComputedStyle(node, null).getPropertyValue('background-color');\n\n if (color.toLowerCase() === 'rgba(0, 0, 0, 0)' || color.toLowerCase() === 'transparent') {\n color = 'rgba(1, 1, 1, 0)';\n }\n\n // Convert the colour to its constituent parts in RGBA format, then fetch the alpha.\n const colorParts = this.colorBase.toArray(color);\n const alpha = colorParts[3];\n\n if (alpha === 1) {\n // If the alpha of the background is already 1, then the parent background colour does not change anything.\n return colorParts;\n }\n\n // Fetch the computed background colour of the parent and use it to calculate the RGB of this item.\n const parentColor = this.getComputedBackgroundColor(node.parentNode);\n return [\n // RGB = (alpha * R|G|B) + (1 - alpha * solid parent colour).\n (1 - alpha) * parentColor[0] + alpha * colorParts[0],\n (1 - alpha) * parentColor[1] + alpha * colorParts[1],\n (1 - alpha) * parentColor[2] + alpha * colorParts[2],\n // We always return a colour with full alpha.\n 1\n ];\n }\n}\n"],"names":["constructor","editor","colorBase","ColorBase","modal","placeholderSelectors","placeholders","this","length","join","destroy","Modal","create","type","types","DEFAULT","large","title","component","body","getDialogueContent","getRoot","on","ModalEvents","hidden","addEventListener","event","faultLink","target","closest","preventDefault","nodeName","dataset","selectedNode","includes","dom","select","nodeIndex","toUpperCase","selectAndScroll","hide","show","keys","stringValues","map","key","Map","index","langStrings","getAllWarningStrings","warnings","getWarnings","warning","description","get","value","nodeData","problemNode","text","Templates","render","node","selection","scrollIntoView","behavior","block","push","createWarnings","checkImage","checkOtherElements","getContent","format","checkTableCaption","checkTableMergedCells","checkTableHeaders","filter","nodes","isImageType","getTextValue","emptyStringValue","innerText","trim","textContent","getEventualNode","childNodes","document","matches","describedNode","indexOf","src","getAttribute","problemNodes","forEach","img","table","caption","querySelector","hasChildNodes","nodeValue","foreground","fromArray","getComputedBackgroundColor","window","getComputedStyle","getPropertyValue","TYPES","RGBA","background","lum1","getLuminanceFromCssColor","lum2","ratio","getRatio","console","log","find","existingProblemNode","contains","querySelectorAll","some","row","header","colortext","color","toArray","toRGB","part1","a","parseInt","Math","pow","parentNode","toLowerCase","colorParts","alpha","parentColor"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"checker.min.js","sources":["../src/checker.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 * @package tiny_accessibilitychecker\n * @copyright 2022, Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {getString, getStrings} from 'core/str';\nimport {component} from './common';\nimport * as Modal from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport ColorBase from './colorbase';\nimport {getPlaceholderSelectors} from 'editor_tiny/options';\n\n/**\n * @typedef ProblemDetail\n * @type {object}\n * @param {string} description The description of the problem\n * @param {ProblemNode[]} problemNodes The list of affected nodes\n */\n\n/**\n * @typedef ProblemNode\n * @type {object}\n * @param {string} nodeName The node name for the affected node\n * @param {string} nodeIndex The indexd of the node\n * @param {string} text A description of the issue\n * @param {string} src The source of the image\n */\n\nexport default class {\n\n constructor(editor) {\n this.editor = editor;\n this.colorBase = new ColorBase();\n this.modal = null;\n this.placeholderSelectors = null;\n const placeholders = getPlaceholderSelectors(this.editor);\n if (placeholders.length) {\n this.placeholderSelectors = placeholders.join(', ');\n }\n }\n\n destroy() {\n delete this.editor;\n delete this.colorBase;\n\n this.modal.destroy();\n delete this.modal;\n }\n\n async displayDialogue() {\n this.modal = await Modal.create({\n type: Modal.types.DEFAULT,\n large: true,\n title: getString('pluginname', component),\n body: this.getDialogueContent()\n });\n\n // Destroy the class when hiding the modal.\n this.modal.getRoot().on(ModalEvents.hidden, () => this.destroy());\n\n this.modal.getRoot()[0].addEventListener('click', (event) => {\n const faultLink = event.target.closest('[data-action=\"highlightfault\"]');\n if (!faultLink) {\n return;\n }\n\n event.preventDefault();\n\n const nodeName = faultLink.dataset.nodeName;\n let selectedNode = null;\n if (nodeName) {\n if (nodeName.includes(',') || nodeName === 'body') {\n selectedNode = this.editor.dom.select('body')[0];\n } else {\n const nodeIndex = faultLink.dataset.nodeIndex ?? 0;\n selectedNode = this.editor.dom.select(nodeName)[nodeIndex];\n }\n }\n\n if (selectedNode && selectedNode.nodeName.toUpperCase() !== 'BODY') {\n this.selectAndScroll(selectedNode);\n }\n\n this.modal.hide();\n });\n\n this.modal.show();\n }\n\n async getAllWarningStrings() {\n const keys = [\n 'emptytext',\n 'entiredocument',\n 'imagesmissingalt',\n 'needsmorecontrast',\n 'needsmoreheadings',\n 'tablesmissingcaption',\n 'tablesmissingheaders',\n 'tableswithmergedcells',\n ];\n\n const stringValues = await getStrings(keys.map((key) => ({key, component})));\n return new Map(keys.map((key, index) => ([key, stringValues[index]])));\n }\n\n /**\n * Return the dialogue content.\n *\n * @return {Promise} A template promise containing the rendered dialogue content.\n */\n async getDialogueContent() {\n const langStrings = await this.getAllWarningStrings();\n\n // Translate langstrings into real strings.\n const warnings = this.getWarnings().map((warning) => {\n if (warning.description) {\n if (warning.description.type === 'langstring') {\n warning.description = langStrings.get(warning.description.value);\n } else {\n warning.description = warning.description.value;\n }\n }\n\n warning.nodeData = warning.nodeData.map((problemNode) => {\n if (problemNode.text) {\n if (problemNode.text.type === 'langstring') {\n problemNode.text = langStrings.get(problemNode.text.value);\n } else {\n problemNode.text = problemNode.text.value;\n }\n }\n\n return problemNode;\n });\n\n return warning;\n });\n\n return Templates.render('tiny_accessibilitychecker/warning_content', {\n warnings\n });\n }\n\n /**\n * Set the selection and scroll to the selected element.\n *\n * @param {node} node\n */\n selectAndScroll(node) {\n this.editor.selection.select(node).scrollIntoView({\n behavior: 'smooth',\n block: 'nearest'\n });\n }\n\n /**\n * Find all problems with the content editable region.\n *\n * @return {ProblemDetail[]} A complete list of all warnings and problems.\n */\n getWarnings() {\n const warnings = [];\n\n // Check Images with no alt text or dodgy alt text.\n warnings.push(this.createWarnings('imagesmissingalt', this.checkImage(), true));\n warnings.push(this.createWarnings('needsmorecontrast', this.checkOtherElements(), false));\n\n // Check for no headings.\n if (this.editor.getContent({format: 'text'}).length > 1000 && this.editor.dom.select('h3,h4,h5').length < 1) {\n warnings.push(this.createWarnings('needsmoreheadings', [this.editor], false));\n }\n\n // Check for tables with no captions.\n warnings.push(this.createWarnings('tablesmissingcaption', this.checkTableCaption(), false));\n\n // Check for tables with merged cells.\n warnings.push(this.createWarnings('tableswithmergedcells', this.checkTableMergedCells(), false));\n\n // Check for tables with no row/col headers.\n warnings.push(this.createWarnings('tablesmissingheaders', this.checkTableHeaders(), false));\n\n return warnings.filter((warning) => warning.nodeData.length > 0);\n }\n\n /**\n * Generate the data that describes the issues found.\n *\n * @param {String} description Description of this failure.\n * @param {HTMLElement[]} nodes An array of failing nodes.\n * @param {boolean} isImageType Whether the warnings are related to image type checks\n * @return {ProblemDetail[]} A set of problem details\n */\n createWarnings(description, nodes, isImageType) {\n const getTextValue = (node) => {\n if (node === this.editor) {\n return {\n type: 'langstring',\n value: 'entiredocument',\n };\n }\n\n const emptyStringValue = {\n type: 'langstring',\n value: 'emptytext',\n };\n if ('innerText' in node) {\n const value = node.innerText.trim();\n return value.length ? {type: 'raw', value} : emptyStringValue;\n } else if ('textContent' in node) {\n const value = node.textContent.trim();\n return value.length ? {type: 'raw', value} : emptyStringValue;\n }\n\n return {type: 'raw', value: node.nodeName};\n };\n\n const getEventualNode = (node) => {\n if (node !== this.editor) {\n return node;\n }\n const childNodes = node.dom.select('body')[0].childNodes;\n if (childNodes.length) {\n return document.body;\n } else {\n return childNodes;\n }\n };\n\n const warning = {\n description: {\n type: 'langstring',\n value: description,\n },\n nodeData: [],\n };\n\n warning.nodeData = [...nodes].filter((node) => {\n // If the failed node is a placeholder element. We should remove it from the list.\n if (node !== this.editor && this.placeholderSelectors) {\n return node.matches(this.placeholderSelectors) === false;\n }\n\n return node;\n }).map((node) => {\n const describedNode = getEventualNode(node);\n\n // Find the index of the node within the type of node.\n // This is used to select the correct node when the user selects it.\n const nodeIndex = this.editor.dom.select(describedNode.nodeName).indexOf(describedNode);\n const warning = {\n src: null,\n text: null,\n nodeName: describedNode.nodeName,\n nodeIndex,\n };\n\n if (isImageType) {\n warning.src = node.getAttribute('src');\n } else {\n warning.text = getTextValue(node);\n }\n\n return warning;\n });\n\n return warning;\n }\n\n /**\n * Check accessiblity issue only for img type.\n *\n * @return {Node} A complete list of all warnings and problems.\n */\n checkImage() {\n const problemNodes = [];\n this.editor.dom.select('img').forEach((img) => {\n const alt = img.getAttribute('alt');\n if (!alt && img.getAttribute('role') !== 'presentation') {\n problemNodes.push(img);\n }\n });\n return problemNodes;\n }\n\n /**\n * Look for any table without a caption.\n *\n * @return {Node} A complete list of all warnings and problems.\n */\n checkTableCaption() {\n const problemNodes = [];\n this.editor.dom.select('table').forEach((table) => {\n const caption = table.querySelector('caption');\n if (!caption?.textContent.trim()) {\n problemNodes.push(table);\n }\n });\n\n return problemNodes;\n }\n\n /**\n * Check accessiblity issue for not img and table only.\n *\n * @return {Node} A complete list of all warnings and problems.\n * @private\n */\n checkOtherElements() {\n const problemNodes = [];\n\n const getRatio = (lum1, lum2) => {\n // Algorithm from \"http://www.w3.org/TR/WCAG20-GENERAL/G18.html\".\n if (lum1 > lum2) {\n return (lum1 + 0.05) / (lum2 + 0.05);\n } else {\n return (lum2 + 0.05) / (lum1 + 0.05);\n }\n };\n\n this.editor.dom.select('body *')\n .filter((node) => node.hasChildNodes() && node.childNodes[0].nodeValue !== null)\n .forEach((node) => {\n const foreground = this.colorBase.fromArray(\n this.getComputedBackgroundColor(\n node,\n window.getComputedStyle(node, null).getPropertyValue('color')\n ),\n this.colorBase.TYPES.RGBA\n );\n const background = this.colorBase.fromArray(\n this.getComputedBackgroundColor(\n node\n ),\n this.colorBase.TYPES.RGBA\n );\n\n const lum1 = this.getLuminanceFromCssColor(foreground);\n const lum2 = this.getLuminanceFromCssColor(background);\n const ratio = getRatio(lum1, lum2);\n\n if (ratio <= 4.5) {\n window.console.log(`\n Contrast ratio is too low: ${ratio}\n Colour 1: ${foreground}\n Colour 2: ${background}\n Luminance 1: ${lum1}\n Luminance 2: ${lum2}\n `);\n\n // We only want the highest node with dodgy contrast reported.\n if (!problemNodes.find((existingProblemNode) => existingProblemNode.contains(node))) {\n problemNodes.push(node);\n }\n }\n });\n return problemNodes;\n }\n\n /**\n * Check accessiblity issue only for table with merged cells.\n *\n * @return {Node} A complete list of all warnings and problems.\n * @private\n */\n checkTableMergedCells() {\n const problemNodes = [];\n this.editor.dom.select('table').forEach((table) => {\n const rowcolspan = table.querySelectorAll('[colspan], [rowspan]');\n if (rowcolspan.length) {\n problemNodes.push(table);\n }\n });\n return problemNodes;\n }\n\n /**\n * Check accessiblity issue only for table with no headers.\n *\n * @return {Node} A complete list of all warnings and problems.\n * @private\n */\n checkTableHeaders() {\n const problemNodes = [];\n\n this.editor.dom.select('table').forEach((table) => {\n if (table.querySelector('tr').querySelector('td')) {\n // The first row has a non-header cell, so all rows must have at least one header.\n const missingHeader = [...table.querySelectorAll('tr')].some((row) => {\n const header = row.querySelector('th');\n if (!header) {\n return true;\n }\n\n if (!header.textContent.trim()) {\n return true;\n }\n\n return false;\n });\n if (missingHeader) {\n // At least one row is missing the header, or it is empty.\n problemNodes.push(table);\n }\n } else {\n // Every header must have some content.\n if ([...table.querySelectorAll('tr th')].some((header) => !header.textContent.trim())) {\n problemNodes.push(table);\n }\n }\n });\n return problemNodes;\n }\n\n /**\n * Convert a CSS color to a luminance value.\n *\n * @param {String} colortext The Hex value for the colour\n * @return {Number} The luminance value.\n * @private\n */\n getLuminanceFromCssColor(colortext) {\n if (colortext === 'transparent') {\n colortext = '#ffffff';\n }\n const color = this.colorBase.toArray(this.colorBase.toRGB(colortext));\n\n // Algorithm from \"http://www.w3.org/TR/WCAG20-GENERAL/G18.html\".\n const part1 = (a) => {\n a = parseInt(a, 10) / 255.0;\n if (a <= 0.03928) {\n a = a / 12.92;\n } else {\n a = Math.pow(((a + 0.055) / 1.055), 2.4);\n }\n return a;\n };\n\n const r1 = part1(color[0]);\n const g1 = part1(color[1]);\n const b1 = part1(color[2]);\n\n return 0.2126 * r1 + 0.7152 * g1 + 0.0722 * b1;\n }\n\n /**\n * Get the computed RGB converted to full alpha value, considering the node hierarchy.\n *\n * @param {Node} node\n * @param {String} color The initial colour. If not specified, fetches the backgroundColor from the node.\n * @return {Array} Colour in Array form (RGBA)\n * @private\n */\n getComputedBackgroundColor(node, color) {\n if (!node.parentNode) {\n // This is the document node and has no colour.\n // We cannot use window.getComputedStyle on the document.\n // If we got here, then the document has no background colour. Fall back to white.\n return this.colorBase.toArray('rgba(255, 255, 255, 1)');\n }\n color = color ? color : window.getComputedStyle(node, null).getPropertyValue('background-color');\n\n if (color.toLowerCase() === 'rgba(0, 0, 0, 0)' || color.toLowerCase() === 'transparent') {\n color = 'rgba(1, 1, 1, 0)';\n }\n\n // Convert the colour to its constituent parts in RGBA format, then fetch the alpha.\n const colorParts = this.colorBase.toArray(color);\n const alpha = colorParts[3];\n\n if (alpha === 1) {\n // If the alpha of the background is already 1, then the parent background colour does not change anything.\n return colorParts;\n }\n\n // Fetch the computed background colour of the parent and use it to calculate the RGB of this item.\n const parentColor = this.getComputedBackgroundColor(node.parentNode);\n return [\n // RGB = (alpha * R|G|B) + (1 - alpha * solid parent colour).\n (1 - alpha) * parentColor[0] + alpha * colorParts[0],\n (1 - alpha) * parentColor[1] + alpha * colorParts[1],\n (1 - alpha) * parentColor[2] + alpha * colorParts[2],\n // We always return a colour with full alpha.\n 1\n ];\n }\n}\n"],"names":["constructor","editor","colorBase","ColorBase","modal","placeholderSelectors","placeholders","this","length","join","destroy","Modal","create","type","types","DEFAULT","large","title","component","body","getDialogueContent","getRoot","on","ModalEvents","hidden","addEventListener","event","faultLink","target","closest","preventDefault","nodeName","dataset","selectedNode","includes","dom","select","nodeIndex","toUpperCase","selectAndScroll","hide","show","keys","stringValues","map","key","Map","index","langStrings","getAllWarningStrings","warnings","getWarnings","warning","description","get","value","nodeData","problemNode","text","Templates","render","node","selection","scrollIntoView","behavior","block","push","createWarnings","checkImage","checkOtherElements","getContent","format","checkTableCaption","checkTableMergedCells","checkTableHeaders","filter","nodes","isImageType","getTextValue","emptyStringValue","innerText","trim","textContent","getEventualNode","childNodes","document","matches","describedNode","indexOf","src","getAttribute","problemNodes","forEach","img","table","caption","querySelector","hasChildNodes","nodeValue","foreground","fromArray","getComputedBackgroundColor","window","getComputedStyle","getPropertyValue","TYPES","RGBA","background","lum1","getLuminanceFromCssColor","lum2","ratio","getRatio","console","log","find","existingProblemNode","contains","querySelectorAll","some","row","header","colortext","color","toArray","toRGB","part1","a","parseInt","Math","pow","parentNode","toLowerCase","colorParts","alpha","parentColor"],"mappings":"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"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/accessibilitychecker/amd/build/commands.min.js b/lib/editor/tiny/plugins/accessibilitychecker/amd/build/commands.min.js index 7bd3e12a926..90a6dc3bffc 100644 --- a/lib/editor/tiny/plugins/accessibilitychecker/amd/build/commands.min.js +++ b/lib/editor/tiny/plugins/accessibilitychecker/amd/build/commands.min.js @@ -5,6 +5,6 @@ define("tiny_accessibilitychecker/commands",["exports","core/str","./common","./ * @module tiny_accessibilitychecker/commands * @copyright 2022, Stevani Andolo * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getSetup=void 0,_checker=(obj=_checker)&&obj.__esModule?obj:{default:obj};_exports.getSetup=async()=>{const[buttonTooltip]=await Promise.all([(0,_str.get_string)("pluginname",_common.component)]);return editor=>{editor.ui.registry.addButton(_common.accessbilityButtonName,{icon:_common.icon,tooltip:buttonTooltip,onAction:()=>{new _checker.default(editor).displayDialogue()}}),editor.ui.registry.addMenuItem(_common.accessbilityButtonName,{icon:_common.icon,text:buttonTooltip,onAction:()=>{new _checker.default(editor).displayDialogue()}})}}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getSetup=void 0,_checker=(obj=_checker)&&obj.__esModule?obj:{default:obj};_exports.getSetup=async()=>{const[buttonTooltip]=await Promise.all([(0,_str.getString)("pluginname",_common.component)]);return editor=>{editor.ui.registry.addButton(_common.accessbilityButtonName,{icon:_common.icon,tooltip:buttonTooltip,onAction:()=>{new _checker.default(editor).displayDialogue()}}),editor.ui.registry.addMenuItem(_common.accessbilityButtonName,{icon:_common.icon,text:buttonTooltip,onAction:()=>{new _checker.default(editor).displayDialogue()}})}}})); //# sourceMappingURL=commands.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/accessibilitychecker/amd/build/commands.min.js.map b/lib/editor/tiny/plugins/accessibilitychecker/amd/build/commands.min.js.map index fb7418a0e0a..c313783c3f7 100644 --- a/lib/editor/tiny/plugins/accessibilitychecker/amd/build/commands.min.js.map +++ b/lib/editor/tiny/plugins/accessibilitychecker/amd/build/commands.min.js.map @@ -1 +1 @@ -{"version":3,"file":"commands.min.js","sources":["../src/commands.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 * Tiny Media Manager commands.\n *\n * @module tiny_accessibilitychecker/commands\n * @copyright 2022, Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString} from 'core/str';\nimport {\n component,\n accessbilityButtonName,\n icon,\n} from './common';\nimport Checker from './checker';\n\nexport const getSetup = async() => {\n const [\n buttonTooltip,\n ] = await Promise.all([\n getString('pluginname', component),\n ]);\n\n return (editor) => {\n // Register the Menu Button as a toggle.\n editor.ui.registry.addButton(accessbilityButtonName, {\n icon,\n tooltip: buttonTooltip,\n onAction: () => {\n const checker = new Checker(editor);\n checker.displayDialogue();\n }\n });\n\n editor.ui.registry.addMenuItem(accessbilityButtonName, {\n icon,\n text: buttonTooltip,\n onAction: () => {\n const checker = new Checker(editor);\n checker.displayDialogue();\n }\n });\n };\n};\n"],"names":["async","buttonTooltip","Promise","all","component","editor","ui","registry","addButton","accessbilityButtonName","icon","tooltip","onAction","Checker","displayDialogue","addMenuItem","text"],"mappings":";;;;;;;kKA+BwBA,gBAEhBC,qBACMC,QAAQC,IAAI,EAClB,mBAAU,aAAcC,4BAGpBC,SAEJA,OAAOC,GAAGC,SAASC,UAAUC,+BAAwB,CACjDC,KAAAA,aACAC,QAASV,cACTW,SAAU,KACU,IAAIC,iBAAQR,QACpBS,qBAIhBT,OAAOC,GAAGC,SAASQ,YAAYN,+BAAwB,CACnDC,KAAAA,aACAM,KAAMf,cACNW,SAAU,KACU,IAAIC,iBAAQR,QACpBS"} \ No newline at end of file +{"version":3,"file":"commands.min.js","sources":["../src/commands.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 * Tiny Media Manager commands.\n *\n * @module tiny_accessibilitychecker/commands\n * @copyright 2022, Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getString} from 'core/str';\nimport {\n component,\n accessbilityButtonName,\n icon,\n} from './common';\nimport Checker from './checker';\n\nexport const getSetup = async() => {\n const [\n buttonTooltip,\n ] = await Promise.all([\n getString('pluginname', component),\n ]);\n\n return (editor) => {\n // Register the Menu Button as a toggle.\n editor.ui.registry.addButton(accessbilityButtonName, {\n icon,\n tooltip: buttonTooltip,\n onAction: () => {\n const checker = new Checker(editor);\n checker.displayDialogue();\n }\n });\n\n editor.ui.registry.addMenuItem(accessbilityButtonName, {\n icon,\n text: buttonTooltip,\n onAction: () => {\n const checker = new Checker(editor);\n checker.displayDialogue();\n }\n });\n };\n};\n"],"names":["async","buttonTooltip","Promise","all","component","editor","ui","registry","addButton","accessbilityButtonName","icon","tooltip","onAction","Checker","displayDialogue","addMenuItem","text"],"mappings":";;;;;;;kKA+BwBA,gBAEhBC,qBACMC,QAAQC,IAAI,EAClB,kBAAU,aAAcC,4BAGpBC,SAEJA,OAAOC,GAAGC,SAASC,UAAUC,+BAAwB,CACjDC,KAAAA,aACAC,QAASV,cACTW,SAAU,KACU,IAAIC,iBAAQR,QACpBS,qBAIhBT,OAAOC,GAAGC,SAASQ,YAAYN,+BAAwB,CACnDC,KAAAA,aACAM,KAAMf,cACNW,SAAU,KACU,IAAIC,iBAAQR,QACpBS"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/accessibilitychecker/amd/src/checker.js b/lib/editor/tiny/plugins/accessibilitychecker/amd/src/checker.js index fb43a63df8c..e96f78f556b 100644 --- a/lib/editor/tiny/plugins/accessibilitychecker/amd/src/checker.js +++ b/lib/editor/tiny/plugins/accessibilitychecker/amd/src/checker.js @@ -20,7 +20,7 @@ */ import Templates from 'core/templates'; -import {get_string as getString, get_strings as getStrings} from 'core/str'; +import {getString, getStrings} from 'core/str'; import {component} from './common'; import * as Modal from 'core/modal_factory'; import * as ModalEvents from 'core/modal_events'; diff --git a/lib/editor/tiny/plugins/accessibilitychecker/amd/src/commands.js b/lib/editor/tiny/plugins/accessibilitychecker/amd/src/commands.js index 42b544bd350..de0de46d482 100644 --- a/lib/editor/tiny/plugins/accessibilitychecker/amd/src/commands.js +++ b/lib/editor/tiny/plugins/accessibilitychecker/amd/src/commands.js @@ -21,7 +21,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import { component, accessbilityButtonName, diff --git a/lib/editor/tiny/plugins/equation/amd/build/commands.min.js b/lib/editor/tiny/plugins/equation/amd/build/commands.min.js index 3a214ece4ea..5391a8ac3e6 100644 --- a/lib/editor/tiny/plugins/equation/amd/build/commands.min.js +++ b/lib/editor/tiny/plugins/equation/amd/build/commands.min.js @@ -1,3 +1,3 @@ -define("tiny_equation/commands",["exports","editor_tiny/utils","core/str","tiny_equation/common","tiny_equation/ui","tiny_equation/equation","tiny_equation/options"],(function(_exports,_utils,_str,_common,_ui,_equation,_options){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getSetup=void 0;_exports.getSetup=async()=>{const[buttonText,buttonImage]=await Promise.all([(0,_str.get_string)("buttontitle",_common.component),(0,_utils.getButtonImage)("icon",_common.component)]);return editor=>{(0,_options.isTexFilterActive)(editor)&&(editor.ui.registry.addIcon(_common.icon,buttonImage.html),editor.ui.registry.addToggleButton(_common.buttonName,{icon:_common.icon,tooltip:buttonText,onAction:()=>{(0,_ui.handleAction)(editor)},onSetup:api=>{editor.on("NodeChange",(()=>{const result=(0,_equation.getSelectedEquation)(editor);api.setActive(result)}))}}),editor.ui.registry.addMenuItem(_common.buttonName,{icon:_common.icon,text:buttonText,onAction:()=>(0,_ui.handleAction)(editor)}))}}})); +define("tiny_equation/commands",["exports","editor_tiny/utils","core/str","tiny_equation/common","tiny_equation/ui","tiny_equation/equation","tiny_equation/options"],(function(_exports,_utils,_str,_common,_ui,_equation,_options){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getSetup=void 0;_exports.getSetup=async()=>{const[buttonText,buttonImage]=await Promise.all([(0,_str.getString)("buttontitle",_common.component),(0,_utils.getButtonImage)("icon",_common.component)]);return editor=>{(0,_options.isTexFilterActive)(editor)&&(editor.ui.registry.addIcon(_common.icon,buttonImage.html),editor.ui.registry.addToggleButton(_common.buttonName,{icon:_common.icon,tooltip:buttonText,onAction:()=>{(0,_ui.handleAction)(editor)},onSetup:api=>{editor.on("NodeChange",(()=>{const result=(0,_equation.getSelectedEquation)(editor);api.setActive(result)}))}}),editor.ui.registry.addMenuItem(_common.buttonName,{icon:_common.icon,text:buttonText,onAction:()=>(0,_ui.handleAction)(editor)}))}}})); //# sourceMappingURL=commands.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/equation/amd/build/commands.min.js.map b/lib/editor/tiny/plugins/equation/amd/build/commands.min.js.map index 9e5bf80aeb2..bfae3be3360 100644 --- a/lib/editor/tiny/plugins/equation/amd/build/commands.min.js.map +++ b/lib/editor/tiny/plugins/equation/amd/build/commands.min.js.map @@ -1 +1 @@ -{"version":3,"file":"commands.min.js","sources":["../src/commands.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\nimport {getButtonImage} from 'editor_tiny/utils';\nimport {get_string as getString} from 'core/str';\nimport {component, buttonName, icon} from 'tiny_equation/common';\nimport {handleAction} from 'tiny_equation/ui';\nimport {getSelectedEquation} from 'tiny_equation/equation';\nimport {isTexFilterActive} from 'tiny_equation/options';\n\n/**\n * Tiny Equation commands.\n *\n * @module tiny_equation/commands\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport const getSetup = async() => {\n const [\n buttonText,\n buttonImage,\n ] = await Promise.all([\n getString('buttontitle', component),\n getButtonImage('icon', component),\n ]);\n\n return (editor) => {\n if (isTexFilterActive(editor)) {\n // Register the Equation Icon.\n editor.ui.registry.addIcon(icon, buttonImage.html);\n\n // Register the Menu Button as a toggle.\n // This means that when highlighted over an existing Equation element it will show as toggled on.\n editor.ui.registry.addToggleButton(buttonName, {\n icon,\n tooltip: buttonText,\n onAction: () => {\n handleAction(editor);\n },\n onSetup: (api) => {\n editor.on('NodeChange', () => {\n const result = getSelectedEquation(editor);\n api.setActive(result);\n });\n },\n });\n\n // Add the Equation Menu Item.\n // This allows it to be added to a standard menu, or a context menu.\n editor.ui.registry.addMenuItem(buttonName, {\n icon,\n text: buttonText,\n onAction: () => handleAction(editor),\n });\n }\n };\n};\n"],"names":["async","buttonText","buttonImage","Promise","all","component","editor","ui","registry","addIcon","icon","html","addToggleButton","buttonName","tooltip","onAction","onSetup","api","on","result","setActive","addMenuItem","text"],"mappings":"wUA8BwBA,gBAEhBC,WACAC,mBACMC,QAAQC,IAAI,EAClB,mBAAU,cAAeC,oBACzB,yBAAe,OAAQA,4BAGnBC,UACA,8BAAkBA,UAElBA,OAAOC,GAAGC,SAASC,QAAQC,aAAMR,YAAYS,MAI7CL,OAAOC,GAAGC,SAASI,gBAAgBC,mBAAY,CAC3CH,KAAAA,aACAI,QAASb,WACTc,SAAU,0BACOT,SAEjBU,QAAUC,MACNX,OAAOY,GAAG,cAAc,WACdC,QAAS,iCAAoBb,QACnCW,IAAIG,UAAUD,cAO1Bb,OAAOC,GAAGC,SAASa,YAAYR,mBAAY,CACvCH,KAAAA,aACAY,KAAMrB,WACNc,SAAU,KAAM,oBAAaT"} \ No newline at end of file +{"version":3,"file":"commands.min.js","sources":["../src/commands.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\nimport {getButtonImage} from 'editor_tiny/utils';\nimport {getString} from 'core/str';\nimport {component, buttonName, icon} from 'tiny_equation/common';\nimport {handleAction} from 'tiny_equation/ui';\nimport {getSelectedEquation} from 'tiny_equation/equation';\nimport {isTexFilterActive} from 'tiny_equation/options';\n\n/**\n * Tiny Equation commands.\n *\n * @module tiny_equation/commands\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport const getSetup = async() => {\n const [\n buttonText,\n buttonImage,\n ] = await Promise.all([\n getString('buttontitle', component),\n getButtonImage('icon', component),\n ]);\n\n return (editor) => {\n if (isTexFilterActive(editor)) {\n // Register the Equation Icon.\n editor.ui.registry.addIcon(icon, buttonImage.html);\n\n // Register the Menu Button as a toggle.\n // This means that when highlighted over an existing Equation element it will show as toggled on.\n editor.ui.registry.addToggleButton(buttonName, {\n icon,\n tooltip: buttonText,\n onAction: () => {\n handleAction(editor);\n },\n onSetup: (api) => {\n editor.on('NodeChange', () => {\n const result = getSelectedEquation(editor);\n api.setActive(result);\n });\n },\n });\n\n // Add the Equation Menu Item.\n // This allows it to be added to a standard menu, or a context menu.\n editor.ui.registry.addMenuItem(buttonName, {\n icon,\n text: buttonText,\n onAction: () => handleAction(editor),\n });\n }\n };\n};\n"],"names":["async","buttonText","buttonImage","Promise","all","component","editor","ui","registry","addIcon","icon","html","addToggleButton","buttonName","tooltip","onAction","onSetup","api","on","result","setActive","addMenuItem","text"],"mappings":"wUA8BwBA,gBAEhBC,WACAC,mBACMC,QAAQC,IAAI,EAClB,kBAAU,cAAeC,oBACzB,yBAAe,OAAQA,4BAGnBC,UACA,8BAAkBA,UAElBA,OAAOC,GAAGC,SAASC,QAAQC,aAAMR,YAAYS,MAI7CL,OAAOC,GAAGC,SAASI,gBAAgBC,mBAAY,CAC3CH,KAAAA,aACAI,QAASb,WACTc,SAAU,0BACOT,SAEjBU,QAAUC,MACNX,OAAOY,GAAG,cAAc,WACdC,QAAS,iCAAoBb,QACnCW,IAAIG,UAAUD,cAO1Bb,OAAOC,GAAGC,SAASa,YAAYR,mBAAY,CACvCH,KAAAA,aACAY,KAAMrB,WACNc,SAAU,KAAM,oBAAaT"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/equation/amd/src/commands.js b/lib/editor/tiny/plugins/equation/amd/src/commands.js index 83537d81452..785f72c7ac1 100644 --- a/lib/editor/tiny/plugins/equation/amd/src/commands.js +++ b/lib/editor/tiny/plugins/equation/amd/src/commands.js @@ -14,7 +14,7 @@ // along with Moodle. If not, see . import {getButtonImage} from 'editor_tiny/utils'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {component, buttonName, icon} from 'tiny_equation/common'; import {handleAction} from 'tiny_equation/ui'; import {getSelectedEquation} from 'tiny_equation/equation'; diff --git a/lib/editor/tiny/plugins/h5p/amd/build/commands.min.js b/lib/editor/tiny/plugins/h5p/amd/build/commands.min.js index 2bfa17747ff..19d6b253de7 100644 --- a/lib/editor/tiny/plugins/h5p/amd/build/commands.min.js +++ b/lib/editor/tiny/plugins/h5p/amd/build/commands.min.js @@ -1,3 +1,3 @@ -define("tiny_h5p/commands",["exports","editor_tiny/utils","./ui","core/str","./common","./options"],(function(_exports,_utils,_ui,_str,_common,_options){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getSetup=void 0;_exports.getSetup=async()=>{const[buttonText,buttonImage]=await Promise.all([(0,_str.get_string)("buttontitle",_common.component),(0,_utils.getButtonImage)("icon",_common.component)]);return editor=>{(0,_options.hasAnyH5PPermission)(editor)&&(editor.ui.registry.addIcon(_common.icon,buttonImage.html),editor.ui.registry.addToggleButton(_common.buttonName,{icon:_common.icon,tooltip:buttonText,onAction:()=>(0,_ui.handleAction)(editor),onSetup:api=>{api.setActive(editor.formatter.match("h5p"));const changed=editor.formatter.formatChanged("h5p",(state=>api.setActive(state)));return()=>changed.unbind()}}),editor.ui.registry.addMenuItem(_common.buttonName,{icon:_common.icon,text:buttonText,onAction:()=>(0,_ui.handleAction)(editor)}))}}})); +define("tiny_h5p/commands",["exports","editor_tiny/utils","./ui","core/str","./common","./options"],(function(_exports,_utils,_ui,_str,_common,_options){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getSetup=void 0;_exports.getSetup=async()=>{const[buttonText,buttonImage]=await Promise.all([(0,_str.getString)("buttontitle",_common.component),(0,_utils.getButtonImage)("icon",_common.component)]);return editor=>{(0,_options.hasAnyH5PPermission)(editor)&&(editor.ui.registry.addIcon(_common.icon,buttonImage.html),editor.ui.registry.addToggleButton(_common.buttonName,{icon:_common.icon,tooltip:buttonText,onAction:()=>(0,_ui.handleAction)(editor),onSetup:api=>{api.setActive(editor.formatter.match("h5p"));const changed=editor.formatter.formatChanged("h5p",(state=>api.setActive(state)));return()=>changed.unbind()}}),editor.ui.registry.addMenuItem(_common.buttonName,{icon:_common.icon,text:buttonText,onAction:()=>(0,_ui.handleAction)(editor)}))}}})); //# sourceMappingURL=commands.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/h5p/amd/build/commands.min.js.map b/lib/editor/tiny/plugins/h5p/amd/build/commands.min.js.map index 6146bd4953a..715e49e32af 100644 --- a/lib/editor/tiny/plugins/h5p/amd/build/commands.min.js.map +++ b/lib/editor/tiny/plugins/h5p/amd/build/commands.min.js.map @@ -1 +1 @@ -{"version":3,"file":"commands.min.js","sources":["../src/commands.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 * Tiny H5P Content configuration.\n *\n * @module tiny_h5p/commands\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getButtonImage} from 'editor_tiny/utils';\nimport {handleAction} from './ui';\nimport {get_string as getString} from 'core/str';\nimport {\n component,\n buttonName,\n icon,\n} from './common';\nimport {hasAnyH5PPermission} from './options';\n\nexport const getSetup = async() => {\n const [\n buttonText,\n buttonImage,\n ] = await Promise.all([\n getString('buttontitle', component),\n getButtonImage('icon', component),\n ]);\n\n return (editor) => {\n if (!hasAnyH5PPermission(editor)) {\n return;\n }\n // Register the H5P Icon.\n editor.ui.registry.addIcon(icon, buttonImage.html);\n\n // Register the Menu Button as a toggle.\n // This means that when highlighted over an existing H5P element it will show as toggled on.\n editor.ui.registry.addToggleButton(buttonName, {\n icon,\n tooltip: buttonText,\n onAction: () => handleAction(editor),\n onSetup: (api) => {\n // Set the button to be active if the current selection matches the h5p formatter registered above during PreInit.\n api.setActive(editor.formatter.match('h5p'));\n const changed = editor.formatter.formatChanged('h5p', (state) => api.setActive(state));\n return () => changed.unbind();\n },\n });\n\n // Add the H5P Menu Item.\n // This allows it to be added to a standard menu, or a context menu.\n editor.ui.registry.addMenuItem(buttonName, {\n icon,\n text: buttonText,\n onAction: () => handleAction(editor),\n });\n };\n};\n"],"names":["async","buttonText","buttonImage","Promise","all","component","editor","ui","registry","addIcon","icon","html","addToggleButton","buttonName","tooltip","onAction","onSetup","api","setActive","formatter","match","changed","formatChanged","state","unbind","addMenuItem","text"],"mappings":"4PAiCwBA,gBAEhBC,WACAC,mBACMC,QAAQC,IAAI,EAClB,mBAAU,cAAeC,oBACzB,yBAAe,OAAQA,4BAGnBC,UACC,gCAAoBA,UAIzBA,OAAOC,GAAGC,SAASC,QAAQC,aAAMR,YAAYS,MAI7CL,OAAOC,GAAGC,SAASI,gBAAgBC,mBAAY,CAC3CH,KAAAA,aACAI,QAASb,WACTc,SAAU,KAAM,oBAAaT,QAC7BU,QAAUC,MAENA,IAAIC,UAAUZ,OAAOa,UAAUC,MAAM,cAC/BC,QAAUf,OAAOa,UAAUG,cAAc,OAAQC,OAAUN,IAAIC,UAAUK,eACxE,IAAMF,QAAQG,YAM7BlB,OAAOC,GAAGC,SAASiB,YAAYZ,mBAAY,CACvCH,KAAAA,aACAgB,KAAMzB,WACNc,SAAU,KAAM,oBAAaT"} \ No newline at end of file +{"version":3,"file":"commands.min.js","sources":["../src/commands.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 * Tiny H5P Content configuration.\n *\n * @module tiny_h5p/commands\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getButtonImage} from 'editor_tiny/utils';\nimport {handleAction} from './ui';\nimport {getString} from 'core/str';\nimport {\n component,\n buttonName,\n icon,\n} from './common';\nimport {hasAnyH5PPermission} from './options';\n\nexport const getSetup = async() => {\n const [\n buttonText,\n buttonImage,\n ] = await Promise.all([\n getString('buttontitle', component),\n getButtonImage('icon', component),\n ]);\n\n return (editor) => {\n if (!hasAnyH5PPermission(editor)) {\n return;\n }\n // Register the H5P Icon.\n editor.ui.registry.addIcon(icon, buttonImage.html);\n\n // Register the Menu Button as a toggle.\n // This means that when highlighted over an existing H5P element it will show as toggled on.\n editor.ui.registry.addToggleButton(buttonName, {\n icon,\n tooltip: buttonText,\n onAction: () => handleAction(editor),\n onSetup: (api) => {\n // Set the button to be active if the current selection matches the h5p formatter registered above during PreInit.\n api.setActive(editor.formatter.match('h5p'));\n const changed = editor.formatter.formatChanged('h5p', (state) => api.setActive(state));\n return () => changed.unbind();\n },\n });\n\n // Add the H5P Menu Item.\n // This allows it to be added to a standard menu, or a context menu.\n editor.ui.registry.addMenuItem(buttonName, {\n icon,\n text: buttonText,\n onAction: () => handleAction(editor),\n });\n };\n};\n"],"names":["async","buttonText","buttonImage","Promise","all","component","editor","ui","registry","addIcon","icon","html","addToggleButton","buttonName","tooltip","onAction","onSetup","api","setActive","formatter","match","changed","formatChanged","state","unbind","addMenuItem","text"],"mappings":"4PAiCwBA,gBAEhBC,WACAC,mBACMC,QAAQC,IAAI,EAClB,kBAAU,cAAeC,oBACzB,yBAAe,OAAQA,4BAGnBC,UACC,gCAAoBA,UAIzBA,OAAOC,GAAGC,SAASC,QAAQC,aAAMR,YAAYS,MAI7CL,OAAOC,GAAGC,SAASI,gBAAgBC,mBAAY,CAC3CH,KAAAA,aACAI,QAASb,WACTc,SAAU,KAAM,oBAAaT,QAC7BU,QAAUC,MAENA,IAAIC,UAAUZ,OAAOa,UAAUC,MAAM,cAC/BC,QAAUf,OAAOa,UAAUG,cAAc,OAAQC,OAAUN,IAAIC,UAAUK,eACxE,IAAMF,QAAQG,YAM7BlB,OAAOC,GAAGC,SAASiB,YAAYZ,mBAAY,CACvCH,KAAAA,aACAgB,KAAMzB,WACNc,SAAU,KAAM,oBAAaT"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/h5p/amd/src/commands.js b/lib/editor/tiny/plugins/h5p/amd/src/commands.js index 5e1ec1c3a73..3779ae04038 100644 --- a/lib/editor/tiny/plugins/h5p/amd/src/commands.js +++ b/lib/editor/tiny/plugins/h5p/amd/src/commands.js @@ -23,7 +23,7 @@ import {getButtonImage} from 'editor_tiny/utils'; import {handleAction} from './ui'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import { component, buttonName, diff --git a/lib/editor/tiny/plugins/link/amd/build/commands.min.js b/lib/editor/tiny/plugins/link/amd/build/commands.min.js index e0811d90f4c..b068db10922 100644 --- a/lib/editor/tiny/plugins/link/amd/build/commands.min.js +++ b/lib/editor/tiny/plugins/link/amd/build/commands.min.js @@ -1,3 +1,3 @@ -define("tiny_link/commands",["exports","core/str","tiny_link/common","tiny_link/ui","tiny_link/link"],(function(_exports,_str,_common,_ui,_link){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getSetup=void 0;_exports.getSetup=async()=>{const[linkButtonText,unlinkButtonText]=await Promise.all([(0,_str.get_string)("link",_common.component),(0,_str.get_string)("unlink",_common.component)]);return editor=>{editor.ui.registry.addToggleButton(_common.linkButtonShortName,{icon:"link",tooltip:linkButtonText,onAction:()=>{(0,_ui.handleAction)(editor)},onSetup:(0,_link.toggleActiveState)(editor)}),editor.ui.registry.addMenuItem(_common.linkButtonShortName,{icon:"link",shortcut:"Meta+K",text:linkButtonText,onAction:()=>{(0,_ui.handleAction)(editor)}}),editor.ui.registry.addToggleButton(_common.unlinkButtonShortName,{icon:"unlink",tooltip:unlinkButtonText,onAction:()=>{(0,_ui.handleAction)(editor,!0)},onSetup:(0,_link.toggleActiveState)(editor)}),editor.shortcuts.add("Meta+K","Shortcut for create link",(()=>{(0,_ui.handleAction)(editor)}))}}})); +define("tiny_link/commands",["exports","core/str","tiny_link/common","tiny_link/ui","tiny_link/link"],(function(_exports,_str,_common,_ui,_link){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getSetup=void 0;_exports.getSetup=async()=>{const[linkButtonText,unlinkButtonText]=await Promise.all([(0,_str.getString)("link",_common.component),(0,_str.getString)("unlink",_common.component)]);return editor=>{editor.ui.registry.addToggleButton(_common.linkButtonShortName,{icon:"link",tooltip:linkButtonText,onAction:()=>{(0,_ui.handleAction)(editor)},onSetup:(0,_link.toggleActiveState)(editor)}),editor.ui.registry.addMenuItem(_common.linkButtonShortName,{icon:"link",shortcut:"Meta+K",text:linkButtonText,onAction:()=>{(0,_ui.handleAction)(editor)}}),editor.ui.registry.addToggleButton(_common.unlinkButtonShortName,{icon:"unlink",tooltip:unlinkButtonText,onAction:()=>{(0,_ui.handleAction)(editor,!0)},onSetup:(0,_link.toggleActiveState)(editor)}),editor.shortcuts.add("Meta+K","Shortcut for create link",(()=>{(0,_ui.handleAction)(editor)}))}}})); //# sourceMappingURL=commands.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/link/amd/build/commands.min.js.map b/lib/editor/tiny/plugins/link/amd/build/commands.min.js.map index 26c41337d22..6711b9cd20d 100644 --- a/lib/editor/tiny/plugins/link/amd/build/commands.min.js.map +++ b/lib/editor/tiny/plugins/link/amd/build/commands.min.js.map @@ -1 +1 @@ -{"version":3,"file":"commands.min.js","sources":["../src/commands.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\nimport {get_string as getString} from 'core/str';\nimport {component, linkButtonShortName, unlinkButtonShortName} from 'tiny_link/common';\nimport {handleAction} from 'tiny_link/ui';\nimport {toggleActiveState} from 'tiny_link/link';\n\n/**\n * Tiny Link commands.\n *\n * @module tiny_link/commands\n * @copyright 2023 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport const getSetup = async() => {\n const [\n linkButtonText,\n unlinkButtonText,\n ] = await Promise.all([\n getString('link', component),\n getString('unlink', component),\n ]);\n\n return (editor) => {\n // Register Link button.\n editor.ui.registry.addToggleButton(linkButtonShortName, {\n icon: 'link',\n tooltip: linkButtonText,\n onAction: () => {\n handleAction(editor);\n },\n onSetup: toggleActiveState(editor),\n });\n\n // Register the Link menu item.\n editor.ui.registry.addMenuItem(linkButtonShortName, {\n icon: 'link',\n shortcut: 'Meta+K',\n text: linkButtonText,\n onAction: () => {\n handleAction(editor);\n },\n });\n\n // Register Unlink button.\n editor.ui.registry.addToggleButton(unlinkButtonShortName, {\n icon: 'unlink',\n tooltip: unlinkButtonText,\n onAction: () => {\n handleAction(editor, true);\n },\n onSetup: toggleActiveState(editor),\n });\n\n // Register shortcut.\n editor.shortcuts.add('Meta+K', 'Shortcut for create link', () => {\n handleAction(editor);\n });\n };\n};\n"],"names":["async","linkButtonText","unlinkButtonText","Promise","all","component","editor","ui","registry","addToggleButton","linkButtonShortName","icon","tooltip","onAction","onSetup","addMenuItem","shortcut","text","unlinkButtonShortName","shortcuts","add"],"mappings":"oPA4BwBA,gBAEhBC,eACAC,wBACMC,QAAQC,IAAI,EAClB,mBAAU,OAAQC,oBAClB,mBAAU,SAAUA,4BAGhBC,SAEJA,OAAOC,GAAGC,SAASC,gBAAgBC,4BAAqB,CACpDC,KAAM,OACNC,QAASX,eACTY,SAAU,0BACOP,SAEjBQ,SAAS,2BAAkBR,UAI/BA,OAAOC,GAAGC,SAASO,YAAYL,4BAAqB,CAChDC,KAAM,OACNK,SAAU,SACVC,KAAMhB,eACNY,SAAU,0BACOP,WAKrBA,OAAOC,GAAGC,SAASC,gBAAgBS,8BAAuB,CACtDP,KAAM,SACNC,QAASV,iBACTW,SAAU,0BACOP,QAAQ,IAEzBQ,SAAS,2BAAkBR,UAI/BA,OAAOa,UAAUC,IAAI,SAAU,4BAA4B,0BAC1Cd"} \ No newline at end of file +{"version":3,"file":"commands.min.js","sources":["../src/commands.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\nimport {getString} from 'core/str';\nimport {component, linkButtonShortName, unlinkButtonShortName} from 'tiny_link/common';\nimport {handleAction} from 'tiny_link/ui';\nimport {toggleActiveState} from 'tiny_link/link';\n\n/**\n * Tiny Link commands.\n *\n * @module tiny_link/commands\n * @copyright 2023 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport const getSetup = async() => {\n const [\n linkButtonText,\n unlinkButtonText,\n ] = await Promise.all([\n getString('link', component),\n getString('unlink', component),\n ]);\n\n return (editor) => {\n // Register Link button.\n editor.ui.registry.addToggleButton(linkButtonShortName, {\n icon: 'link',\n tooltip: linkButtonText,\n onAction: () => {\n handleAction(editor);\n },\n onSetup: toggleActiveState(editor),\n });\n\n // Register the Link menu item.\n editor.ui.registry.addMenuItem(linkButtonShortName, {\n icon: 'link',\n shortcut: 'Meta+K',\n text: linkButtonText,\n onAction: () => {\n handleAction(editor);\n },\n });\n\n // Register Unlink button.\n editor.ui.registry.addToggleButton(unlinkButtonShortName, {\n icon: 'unlink',\n tooltip: unlinkButtonText,\n onAction: () => {\n handleAction(editor, true);\n },\n onSetup: toggleActiveState(editor),\n });\n\n // Register shortcut.\n editor.shortcuts.add('Meta+K', 'Shortcut for create link', () => {\n handleAction(editor);\n });\n };\n};\n"],"names":["async","linkButtonText","unlinkButtonText","Promise","all","component","editor","ui","registry","addToggleButton","linkButtonShortName","icon","tooltip","onAction","onSetup","addMenuItem","shortcut","text","unlinkButtonShortName","shortcuts","add"],"mappings":"oPA4BwBA,gBAEhBC,eACAC,wBACMC,QAAQC,IAAI,EAClB,kBAAU,OAAQC,oBAClB,kBAAU,SAAUA,4BAGhBC,SAEJA,OAAOC,GAAGC,SAASC,gBAAgBC,4BAAqB,CACpDC,KAAM,OACNC,QAASX,eACTY,SAAU,0BACOP,SAEjBQ,SAAS,2BAAkBR,UAI/BA,OAAOC,GAAGC,SAASO,YAAYL,4BAAqB,CAChDC,KAAM,OACNK,SAAU,SACVC,KAAMhB,eACNY,SAAU,0BACOP,WAKrBA,OAAOC,GAAGC,SAASC,gBAAgBS,8BAAuB,CACtDP,KAAM,SACNC,QAASV,iBACTW,SAAU,0BACOP,QAAQ,IAEzBQ,SAAS,2BAAkBR,UAI/BA,OAAOa,UAAUC,IAAI,SAAU,4BAA4B,0BAC1Cd"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/link/amd/src/commands.js b/lib/editor/tiny/plugins/link/amd/src/commands.js index 6f3bbe1ea96..1398049f293 100644 --- a/lib/editor/tiny/plugins/link/amd/src/commands.js +++ b/lib/editor/tiny/plugins/link/amd/src/commands.js @@ -13,7 +13,7 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {component, linkButtonShortName, unlinkButtonShortName} from 'tiny_link/common'; import {handleAction} from 'tiny_link/ui'; import {toggleActiveState} from 'tiny_link/link'; diff --git a/lib/editor/tiny/plugins/media/amd/build/commands.min.js b/lib/editor/tiny/plugins/media/amd/build/commands.min.js index 6af7d31c129..e9b0fe5150f 100644 --- a/lib/editor/tiny/plugins/media/amd/build/commands.min.js +++ b/lib/editor/tiny/plugins/media/amd/build/commands.min.js @@ -6,6 +6,6 @@ define("tiny_media/commands",["exports","core/str","./common","./image","./embed * @copyright 2022 Huong Nguyen * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -const isImage=node=>"img"===node.nodeName.toLowerCase(),isVideo=node=>"video"===node.nodeName.toLowerCase()||"audio"===node.nodeName.toLowerCase();_exports.getSetup=async()=>{const[imageButtonText,mediaButtonText,mediaManagerButtonText]=await(0,_str.get_strings)(["imagebuttontitle","mediabuttontitle","mediamanagerbuttontitle"].map((key=>({key:key,component:_common.component})))),[mediaManagerButtonImage]=await Promise.all([(0,_utils.getButtonImage)("filemanager",_common.component)]);return editor=>{((editor,imageButtonText)=>{const handleImageAction=()=>{new _image.MediaImage(editor).displayDialogue()};editor.ui.registry.addToggleButton(_common.imageButtonName,{icon:"image",tooltip:imageButtonText,onAction:handleImageAction,onSetup:api=>editor.selection.selectorChangedWithUnbind("img:not([data-mce-object]):not([data-mce-placeholder]),figure.image",api.setActive).unbind}),editor.ui.registry.addMenuItem(_common.imageButtonName,{icon:"image",text:imageButtonText,onAction:handleImageAction}),editor.ui.registry.addContextToolbar(_common.imageButtonName,{predicate:isImage,items:_common.imageButtonName,position:"node",scope:"node"}),editor.ui.registry.addContextMenu(_common.imageButtonName,{update:isImage})})(editor,imageButtonText),((editor,videoButtonText)=>{const handleEmbedAction=()=>{new _embed.MediaEmbed(editor).displayDialogue()};editor.ui.registry.addToggleButton(_common.videoButtonName,{icon:"embed",tooltip:videoButtonText,onAction:handleEmbedAction,onSetup:api=>editor.selection.selectorChangedWithUnbind("video:not([data-mce-object]):not([data-mce-placeholder]),audio:not([data-mce-object]):not([data-mce-placeholder])",api.setActive).unbind}),editor.ui.registry.addMenuItem(_common.videoButtonName,{icon:"embed",text:videoButtonText,onAction:handleEmbedAction}),editor.ui.registry.addContextMenu(_common.videoButtonName,{update:isVideo}),editor.ui.registry.addContextToolbar(_common.videoButtonName,{predicate:isVideo,items:_common.videoButtonName,position:"node",scope:"node"})})(editor,mediaButtonText),((editor,mediaManagerButtonText,mediaManagerButtonImage)=>{const handleMediaManager=()=>{new _manager.MediaManager(editor).displayDialogue()};editor.ui.registry.addIcon("filemanager",mediaManagerButtonImage.html),editor.ui.registry.addButton(_common.mediaManagerButtonName,{icon:"filemanager",tooltip:mediaManagerButtonText,onAction:()=>{handleMediaManager()}}),editor.ui.registry.addMenuItem(_common.mediaManagerButtonName,{icon:"filemanager",text:mediaManagerButtonText,onAction:()=>{handleMediaManager()}})})(editor,mediaManagerButtonText,mediaManagerButtonImage)}}})); +const isImage=node=>"img"===node.nodeName.toLowerCase(),isVideo=node=>"video"===node.nodeName.toLowerCase()||"audio"===node.nodeName.toLowerCase();_exports.getSetup=async()=>{const[imageButtonText,mediaButtonText,mediaManagerButtonText]=await(0,_str.getStrings)(["imagebuttontitle","mediabuttontitle","mediamanagerbuttontitle"].map((key=>({key:key,component:_common.component})))),[mediaManagerButtonImage]=await Promise.all([(0,_utils.getButtonImage)("filemanager",_common.component)]);return editor=>{((editor,imageButtonText)=>{const handleImageAction=()=>{new _image.MediaImage(editor).displayDialogue()};editor.ui.registry.addToggleButton(_common.imageButtonName,{icon:"image",tooltip:imageButtonText,onAction:handleImageAction,onSetup:api=>editor.selection.selectorChangedWithUnbind("img:not([data-mce-object]):not([data-mce-placeholder]),figure.image",api.setActive).unbind}),editor.ui.registry.addMenuItem(_common.imageButtonName,{icon:"image",text:imageButtonText,onAction:handleImageAction}),editor.ui.registry.addContextToolbar(_common.imageButtonName,{predicate:isImage,items:_common.imageButtonName,position:"node",scope:"node"}),editor.ui.registry.addContextMenu(_common.imageButtonName,{update:isImage})})(editor,imageButtonText),((editor,videoButtonText)=>{const handleEmbedAction=()=>{new _embed.MediaEmbed(editor).displayDialogue()};editor.ui.registry.addToggleButton(_common.videoButtonName,{icon:"embed",tooltip:videoButtonText,onAction:handleEmbedAction,onSetup:api=>editor.selection.selectorChangedWithUnbind("video:not([data-mce-object]):not([data-mce-placeholder]),audio:not([data-mce-object]):not([data-mce-placeholder])",api.setActive).unbind}),editor.ui.registry.addMenuItem(_common.videoButtonName,{icon:"embed",text:videoButtonText,onAction:handleEmbedAction}),editor.ui.registry.addContextMenu(_common.videoButtonName,{update:isVideo}),editor.ui.registry.addContextToolbar(_common.videoButtonName,{predicate:isVideo,items:_common.videoButtonName,position:"node",scope:"node"})})(editor,mediaButtonText),((editor,mediaManagerButtonText,mediaManagerButtonImage)=>{const handleMediaManager=()=>{new _manager.MediaManager(editor).displayDialogue()};editor.ui.registry.addIcon("filemanager",mediaManagerButtonImage.html),editor.ui.registry.addButton(_common.mediaManagerButtonName,{icon:"filemanager",tooltip:mediaManagerButtonText,onAction:()=>{handleMediaManager()}}),editor.ui.registry.addMenuItem(_common.mediaManagerButtonName,{icon:"filemanager",text:mediaManagerButtonText,onAction:()=>{handleMediaManager()}})})(editor,mediaManagerButtonText,mediaManagerButtonImage)}}})); //# sourceMappingURL=commands.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/commands.min.js.map b/lib/editor/tiny/plugins/media/amd/build/commands.min.js.map index ecb49fee816..842d547ec4a 100644 --- a/lib/editor/tiny/plugins/media/amd/build/commands.min.js.map +++ b/lib/editor/tiny/plugins/media/amd/build/commands.min.js.map @@ -1 +1 @@ -{"version":3,"file":"commands.min.js","sources":["../src/commands.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 * Tiny Media commands.\n *\n * @module tiny_media/commands\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_strings as getStrings} from 'core/str';\nimport {\n component,\n imageButtonName,\n videoButtonName,\n mediaManagerButtonName\n} from './common';\nimport {MediaImage} from './image';\nimport {MediaEmbed} from './embed';\nimport {MediaManager} from './manager';\nimport {getButtonImage} from 'editor_tiny/utils';\n\nconst isImage = (node) => node.nodeName.toLowerCase() === 'img';\nconst isVideo = (node) => node.nodeName.toLowerCase() === 'video' || node.nodeName.toLowerCase() === 'audio';\n\nconst registerImageCommand = (editor, imageButtonText) => {\n const imageIcon = 'image';\n const handleImageAction = () => {\n const mediaImage = new MediaImage(editor);\n mediaImage.displayDialogue();\n };\n\n // Register the Menu Button as a toggle.\n // This means that when highlighted over an existing Media Image element it will show as toggled on.\n editor.ui.registry.addToggleButton(imageButtonName, {\n icon: imageIcon,\n tooltip: imageButtonText,\n onAction: handleImageAction,\n onSetup: api => {\n return editor.selection.selectorChangedWithUnbind(\n 'img:not([data-mce-object]):not([data-mce-placeholder]),figure.image',\n api.setActive\n ).unbind;\n }\n });\n\n editor.ui.registry.addMenuItem(imageButtonName, {\n icon: imageIcon,\n text: imageButtonText,\n onAction: handleImageAction,\n });\n\n editor.ui.registry.addContextToolbar(imageButtonName, {\n predicate: isImage,\n items: imageButtonName,\n position: 'node',\n scope: 'node'\n });\n\n editor.ui.registry.addContextMenu(imageButtonName, {\n update: isImage,\n });\n};\n\nconst registerEmbedCommand = (editor, videoButtonText) => {\n const videoIcon = 'embed';\n const handleEmbedAction = () => {\n const mediaImage = new MediaEmbed(editor);\n mediaImage.displayDialogue();\n };\n\n // Register the Menu Button as a toggle.\n // This means that when highlighted over an existing Media Video element it will show as toggled on.\n editor.ui.registry.addToggleButton(videoButtonName, {\n icon: videoIcon,\n tooltip: videoButtonText,\n onAction: handleEmbedAction,\n onSetup: api => {\n return editor.selection.selectorChangedWithUnbind(\n 'video:not([data-mce-object]):not([data-mce-placeholder]),' +\n 'audio:not([data-mce-object]):not([data-mce-placeholder])',\n api.setActive\n ).unbind;\n }\n });\n\n editor.ui.registry.addMenuItem(videoButtonName, {\n icon: videoIcon,\n text: videoButtonText,\n onAction: handleEmbedAction,\n });\n\n editor.ui.registry.addContextMenu(videoButtonName, {\n update: isVideo,\n });\n\n editor.ui.registry.addContextToolbar(videoButtonName, {\n predicate: isVideo,\n items: videoButtonName,\n position: 'node',\n scope: 'node'\n });\n\n};\n\nconst registerManagerCommand = (editor, mediaManagerButtonText, mediaManagerButtonImage) => {\n const mediaManagerIcon = 'filemanager';\n const handleMediaManager = () => {\n const mediaManager = new MediaManager(editor);\n mediaManager.displayDialogue();\n };\n\n // Register the Menu Button as a toggle.\n editor.ui.registry.addIcon(mediaManagerIcon, mediaManagerButtonImage.html);\n editor.ui.registry.addButton(mediaManagerButtonName, {\n icon: mediaManagerIcon,\n tooltip: mediaManagerButtonText,\n onAction: () => {\n handleMediaManager();\n }\n });\n\n editor.ui.registry.addMenuItem(mediaManagerButtonName, {\n icon: mediaManagerIcon,\n text: mediaManagerButtonText,\n onAction: () => {\n handleMediaManager();\n }\n });\n};\n\nexport const getSetup = async() => {\n const [\n imageButtonText,\n mediaButtonText,\n mediaManagerButtonText\n ] = await getStrings(['imagebuttontitle', 'mediabuttontitle', 'mediamanagerbuttontitle'].map((key) => ({key, component})));\n\n const [\n mediaManagerButtonImage,\n ] = await Promise.all([\n getButtonImage('filemanager', component)\n ]);\n\n // Note: The function returned here must be synchronous and cannot use promises.\n // All promises must be resolved prior to returning the function.\n return (editor) => {\n registerImageCommand(editor, imageButtonText);\n registerEmbedCommand(editor, mediaButtonText);\n registerManagerCommand(editor, mediaManagerButtonText, mediaManagerButtonImage);\n };\n};\n"],"names":["isImage","node","nodeName","toLowerCase","isVideo","async","imageButtonText","mediaButtonText","mediaManagerButtonText","map","key","component","mediaManagerButtonImage","Promise","all","editor","handleImageAction","MediaImage","displayDialogue","ui","registry","addToggleButton","imageButtonName","icon","tooltip","onAction","onSetup","api","selection","selectorChangedWithUnbind","setActive","unbind","addMenuItem","text","addContextToolbar","predicate","items","position","scope","addContextMenu","update","registerImageCommand","videoButtonText","handleEmbedAction","MediaEmbed","videoButtonName","registerEmbedCommand","handleMediaManager","MediaManager","addIcon","html","addButton","mediaManagerButtonName","registerManagerCommand"],"mappings":";;;;;;;;MAmCMA,QAAWC,MAAyC,QAAhCA,KAAKC,SAASC,cAClCC,QAAWH,MAAyC,UAAhCA,KAAKC,SAASC,eAA6D,UAAhCF,KAAKC,SAASC,gCA4G3DE,gBAEhBC,gBACAC,gBACAC,8BACM,oBAAW,CAAC,mBAAoB,mBAAoB,2BAA2BC,KAAKC,OAAUA,IAAAA,IAAKC,UAAAA,wBAGzGC,+BACMC,QAAQC,IAAI,EAClB,yBAAe,cAAeH,4BAK1BI,SAzHiB,EAACA,OAAQT,yBAE5BU,kBAAoB,KACH,IAAIC,kBAAWF,QACvBG,mBAKfH,OAAOI,GAAGC,SAASC,gBAAgBC,wBAAiB,CAChDC,KATc,QAUdC,QAASlB,gBACTmB,SAAUT,kBACVU,QAASC,KACEZ,OAAOa,UAAUC,0BACpB,sEACAF,IAAIG,WACNC,SAIVhB,OAAOI,GAAGC,SAASY,YAAYV,wBAAiB,CAC5CC,KArBc,QAsBdU,KAAM3B,gBACNmB,SAAUT,oBAGdD,OAAOI,GAAGC,SAASc,kBAAkBZ,wBAAiB,CAClDa,UAAWnC,QACXoC,MAAOd,wBACPe,SAAU,OACVC,MAAO,SAGXvB,OAAOI,GAAGC,SAASmB,eAAejB,wBAAiB,CAC/CkB,OAAQxC,WAuFRyC,CAAqB1B,OAAQT,iBAnFR,EAACS,OAAQ2B,yBAE5BC,kBAAoB,KACH,IAAIC,kBAAW7B,QACvBG,mBAKfH,OAAOI,GAAGC,SAASC,gBAAgBwB,wBAAiB,CAChDtB,KATc,QAUdC,QAASkB,gBACTjB,SAAUkB,kBACVjB,QAASC,KACEZ,OAAOa,UAAUC,0BACpB,oHAEAF,IAAIG,WACNC,SAIVhB,OAAOI,GAAGC,SAASY,YAAYa,wBAAiB,CAC5CtB,KAtBc,QAuBdU,KAAMS,gBACNjB,SAAUkB,oBAGd5B,OAAOI,GAAGC,SAASmB,eAAeM,wBAAiB,CAC/CL,OAAQpC,UAGZW,OAAOI,GAAGC,SAASc,kBAAkBW,wBAAiB,CAClDV,UAAW/B,QACXgC,MAAOS,wBACPR,SAAU,OACVC,MAAO,UAgDPQ,CAAqB/B,OAAQR,iBA3CN,EAACQ,OAAQP,uBAAwBI,iCAEtDmC,mBAAqB,KACF,IAAIC,sBAAajC,QACzBG,mBAIjBH,OAAOI,GAAGC,SAAS6B,QAPM,cAOoBrC,wBAAwBsC,MACrEnC,OAAOI,GAAGC,SAAS+B,UAAUC,+BAAwB,CACjD7B,KATqB,cAUrBC,QAAShB,uBACTiB,SAAU,KACNsB,wBAIRhC,OAAOI,GAAGC,SAASY,YAAYoB,+BAAwB,CACnD7B,KAjBqB,cAkBrBU,KAAMzB,uBACNiB,SAAU,KACNsB,yBAuBJM,CAAuBtC,OAAQP,uBAAwBI"} \ No newline at end of file +{"version":3,"file":"commands.min.js","sources":["../src/commands.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 * Tiny Media commands.\n *\n * @module tiny_media/commands\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getStrings} from 'core/str';\nimport {\n component,\n imageButtonName,\n videoButtonName,\n mediaManagerButtonName\n} from './common';\nimport {MediaImage} from './image';\nimport {MediaEmbed} from './embed';\nimport {MediaManager} from './manager';\nimport {getButtonImage} from 'editor_tiny/utils';\n\nconst isImage = (node) => node.nodeName.toLowerCase() === 'img';\nconst isVideo = (node) => node.nodeName.toLowerCase() === 'video' || node.nodeName.toLowerCase() === 'audio';\n\nconst registerImageCommand = (editor, imageButtonText) => {\n const imageIcon = 'image';\n const handleImageAction = () => {\n const mediaImage = new MediaImage(editor);\n mediaImage.displayDialogue();\n };\n\n // Register the Menu Button as a toggle.\n // This means that when highlighted over an existing Media Image element it will show as toggled on.\n editor.ui.registry.addToggleButton(imageButtonName, {\n icon: imageIcon,\n tooltip: imageButtonText,\n onAction: handleImageAction,\n onSetup: api => {\n return editor.selection.selectorChangedWithUnbind(\n 'img:not([data-mce-object]):not([data-mce-placeholder]),figure.image',\n api.setActive\n ).unbind;\n }\n });\n\n editor.ui.registry.addMenuItem(imageButtonName, {\n icon: imageIcon,\n text: imageButtonText,\n onAction: handleImageAction,\n });\n\n editor.ui.registry.addContextToolbar(imageButtonName, {\n predicate: isImage,\n items: imageButtonName,\n position: 'node',\n scope: 'node'\n });\n\n editor.ui.registry.addContextMenu(imageButtonName, {\n update: isImage,\n });\n};\n\nconst registerEmbedCommand = (editor, videoButtonText) => {\n const videoIcon = 'embed';\n const handleEmbedAction = () => {\n const mediaImage = new MediaEmbed(editor);\n mediaImage.displayDialogue();\n };\n\n // Register the Menu Button as a toggle.\n // This means that when highlighted over an existing Media Video element it will show as toggled on.\n editor.ui.registry.addToggleButton(videoButtonName, {\n icon: videoIcon,\n tooltip: videoButtonText,\n onAction: handleEmbedAction,\n onSetup: api => {\n return editor.selection.selectorChangedWithUnbind(\n 'video:not([data-mce-object]):not([data-mce-placeholder]),' +\n 'audio:not([data-mce-object]):not([data-mce-placeholder])',\n api.setActive\n ).unbind;\n }\n });\n\n editor.ui.registry.addMenuItem(videoButtonName, {\n icon: videoIcon,\n text: videoButtonText,\n onAction: handleEmbedAction,\n });\n\n editor.ui.registry.addContextMenu(videoButtonName, {\n update: isVideo,\n });\n\n editor.ui.registry.addContextToolbar(videoButtonName, {\n predicate: isVideo,\n items: videoButtonName,\n position: 'node',\n scope: 'node'\n });\n\n};\n\nconst registerManagerCommand = (editor, mediaManagerButtonText, mediaManagerButtonImage) => {\n const mediaManagerIcon = 'filemanager';\n const handleMediaManager = () => {\n const mediaManager = new MediaManager(editor);\n mediaManager.displayDialogue();\n };\n\n // Register the Menu Button as a toggle.\n editor.ui.registry.addIcon(mediaManagerIcon, mediaManagerButtonImage.html);\n editor.ui.registry.addButton(mediaManagerButtonName, {\n icon: mediaManagerIcon,\n tooltip: mediaManagerButtonText,\n onAction: () => {\n handleMediaManager();\n }\n });\n\n editor.ui.registry.addMenuItem(mediaManagerButtonName, {\n icon: mediaManagerIcon,\n text: mediaManagerButtonText,\n onAction: () => {\n handleMediaManager();\n }\n });\n};\n\nexport const getSetup = async() => {\n const [\n imageButtonText,\n mediaButtonText,\n mediaManagerButtonText\n ] = await getStrings(['imagebuttontitle', 'mediabuttontitle', 'mediamanagerbuttontitle'].map((key) => ({key, component})));\n\n const [\n mediaManagerButtonImage,\n ] = await Promise.all([\n getButtonImage('filemanager', component)\n ]);\n\n // Note: The function returned here must be synchronous and cannot use promises.\n // All promises must be resolved prior to returning the function.\n return (editor) => {\n registerImageCommand(editor, imageButtonText);\n registerEmbedCommand(editor, mediaButtonText);\n registerManagerCommand(editor, mediaManagerButtonText, mediaManagerButtonImage);\n };\n};\n"],"names":["isImage","node","nodeName","toLowerCase","isVideo","async","imageButtonText","mediaButtonText","mediaManagerButtonText","map","key","component","mediaManagerButtonImage","Promise","all","editor","handleImageAction","MediaImage","displayDialogue","ui","registry","addToggleButton","imageButtonName","icon","tooltip","onAction","onSetup","api","selection","selectorChangedWithUnbind","setActive","unbind","addMenuItem","text","addContextToolbar","predicate","items","position","scope","addContextMenu","update","registerImageCommand","videoButtonText","handleEmbedAction","MediaEmbed","videoButtonName","registerEmbedCommand","handleMediaManager","MediaManager","addIcon","html","addButton","mediaManagerButtonName","registerManagerCommand"],"mappings":";;;;;;;;MAmCMA,QAAWC,MAAyC,QAAhCA,KAAKC,SAASC,cAClCC,QAAWH,MAAyC,UAAhCA,KAAKC,SAASC,eAA6D,UAAhCF,KAAKC,SAASC,gCA4G3DE,gBAEhBC,gBACAC,gBACAC,8BACM,mBAAW,CAAC,mBAAoB,mBAAoB,2BAA2BC,KAAKC,OAAUA,IAAAA,IAAKC,UAAAA,wBAGzGC,+BACMC,QAAQC,IAAI,EAClB,yBAAe,cAAeH,4BAK1BI,SAzHiB,EAACA,OAAQT,yBAE5BU,kBAAoB,KACH,IAAIC,kBAAWF,QACvBG,mBAKfH,OAAOI,GAAGC,SAASC,gBAAgBC,wBAAiB,CAChDC,KATc,QAUdC,QAASlB,gBACTmB,SAAUT,kBACVU,QAASC,KACEZ,OAAOa,UAAUC,0BACpB,sEACAF,IAAIG,WACNC,SAIVhB,OAAOI,GAAGC,SAASY,YAAYV,wBAAiB,CAC5CC,KArBc,QAsBdU,KAAM3B,gBACNmB,SAAUT,oBAGdD,OAAOI,GAAGC,SAASc,kBAAkBZ,wBAAiB,CAClDa,UAAWnC,QACXoC,MAAOd,wBACPe,SAAU,OACVC,MAAO,SAGXvB,OAAOI,GAAGC,SAASmB,eAAejB,wBAAiB,CAC/CkB,OAAQxC,WAuFRyC,CAAqB1B,OAAQT,iBAnFR,EAACS,OAAQ2B,yBAE5BC,kBAAoB,KACH,IAAIC,kBAAW7B,QACvBG,mBAKfH,OAAOI,GAAGC,SAASC,gBAAgBwB,wBAAiB,CAChDtB,KATc,QAUdC,QAASkB,gBACTjB,SAAUkB,kBACVjB,QAASC,KACEZ,OAAOa,UAAUC,0BACpB,oHAEAF,IAAIG,WACNC,SAIVhB,OAAOI,GAAGC,SAASY,YAAYa,wBAAiB,CAC5CtB,KAtBc,QAuBdU,KAAMS,gBACNjB,SAAUkB,oBAGd5B,OAAOI,GAAGC,SAASmB,eAAeM,wBAAiB,CAC/CL,OAAQpC,UAGZW,OAAOI,GAAGC,SAASc,kBAAkBW,wBAAiB,CAClDV,UAAW/B,QACXgC,MAAOS,wBACPR,SAAU,OACVC,MAAO,UAgDPQ,CAAqB/B,OAAQR,iBA3CN,EAACQ,OAAQP,uBAAwBI,iCAEtDmC,mBAAqB,KACF,IAAIC,sBAAajC,QACzBG,mBAIjBH,OAAOI,GAAGC,SAAS6B,QAPM,cAOoBrC,wBAAwBsC,MACrEnC,OAAOI,GAAGC,SAAS+B,UAAUC,+BAAwB,CACjD7B,KATqB,cAUrBC,QAAShB,uBACTiB,SAAU,KACNsB,wBAIRhC,OAAOI,GAAGC,SAASY,YAAYoB,+BAAwB,CACnD7B,KAjBqB,cAkBrBU,KAAMzB,uBACNiB,SAAU,KACNsB,yBAuBJM,CAAuBtC,OAAQP,uBAAwBI"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/embed.min.js b/lib/editor/tiny/plugins/media/amd/build/embed.min.js index 55f08de7def..3057a1410a0 100644 --- a/lib/editor/tiny/plugins/media/amd/build/embed.min.js +++ b/lib/editor/tiny/plugins/media/amd/build/embed.min.js @@ -1,3 +1,3 @@ -define("tiny_media/embed",["exports","core/templates","core/str","core/modal_factory","core/modal_events","editor_tiny/utils","editor_tiny/options","./common","./embedmodal","./selectors","./options"],(function(_exports,_templates,_str,ModalFactory,ModalEvents,_utils,_options,_common,_embedmodal,_selectors,_options2){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.MediaEmbed=void 0,_templates=_interopRequireDefault(_templates),ModalFactory=_interopRequireWildcard(ModalFactory),ModalEvents=_interopRequireWildcard(ModalEvents),_embedmodal=_interopRequireDefault(_embedmodal),_selectors=_interopRequireDefault(_selectors);_exports.MediaEmbed=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"canShowFilePicker",!1),_defineProperty(this,"helpStrings",null),_defineProperty(this,"isUpdating",!1);const permissions=(0,_options2.getEmbedPermissions)(editor);this.canShowFilePicker=permissions.filepicker,this.editor=editor}async getHelpStrings(){if(!this.helpStrings){const[addSource,tracks,subtitles,captions,descriptions,chapters,metadata]=await(0,_str.get_strings)(["addsource_help","tracks_help","subtitles_help","captions_help","descriptions_help","chapters_help","metadata_help"].map((key=>({key:key,component:_common.component}))));this.helpStrings={addSource:addSource,tracks:tracks,subtitles:subtitles,captions:captions,descriptions:descriptions,chapters:chapters,metadata:metadata}}return this.helpStrings}async getTemplateContext(data){const languages=this.prepareMoodleLang(),helpIcons=Array.from(Object.entries(await this.getHelpStrings())).forEach((_ref=>{let[key,text]=_ref;data["".concat(key.toLowerCase(),"helpicon")]={text:text}}));return Object.assign({},{elementid:this.editor.getElement().id,showfilepicker:this.canShowFilePicker,langsinstalled:languages.installed,langsavailable:languages.available,link:!0,video:!1,audio:!1,isupdating:this.isUpdating},data,helpIcons)}async displayDialogue(){const data=Object.assign({},this.getCurrentEmbedData());this.isUpdating=0!==Object.keys(data).length;const modal=await ModalFactory.create({type:_embedmodal.default.TYPE,title:(0,_str.get_string)("createmedia","tiny_media"),templateContext:await this.getTemplateContext(data),removeOnClose:!0,large:!0});this.currentModal=modal,await this.registerEventListeners(modal),modal.show()}getCurrentEmbedData(){const properties=this.getMediumProperties();if(!properties)return{};const processedProperties={};return processedProperties[properties.type.toLowerCase()]=properties,processedProperties.link=!1,processedProperties}getSelectedMedia(){const mediaElm=this.editor.selection.getNode();return mediaElm?"video"===mediaElm.nodeName.toLowerCase()||"audio"===mediaElm.nodeName.toLowerCase()?mediaElm:mediaElm.querySelector("video")?mediaElm.querySelector("video"):mediaElm.querySelector("audio")?mediaElm.querySelector("audio"):null:null}getMediumProperties(){const boolAttr=(elem,attr)=>elem.hasAttribute(attr)&&(elem.getAttribute(attr)||""===elem.getAttribute(attr)),tracks={subtitles:[],captions:[],descriptions:[],chapters:[],metadata:[]},sources=[],medium=this.getSelectedMedia();return medium?(medium.querySelectorAll("track").forEach((track=>{tracks[track.getAttribute("kind")].push({src:track.getAttribute("src"),srclang:track.getAttribute("srclang"),label:track.getAttribute("label"),defaultTrack:boolAttr(track,"default")})})),medium.querySelectorAll("source").forEach((source=>{sources.push(source.src)})),{type:"video"===medium.nodeName.toLowerCase()?_selectors.default.EMBED.mediaTypes.video:_selectors.default.EMBED.mediaTypes.audio,sources:sources,poster:medium.getAttribute("poster"),title:medium.getAttribute("title"),width:medium.getAttribute("width"),height:medium.getAttribute("height"),autoplay:boolAttr(medium,"autoplay"),loop:boolAttr(medium,"loop"),muted:boolAttr(medium,"muted"),controls:boolAttr(medium,"controls"),tracks:tracks}):null}prepareMoodleLang(){const moodleLangs=(0,_options.getMoodleLang)(this.editor),currentLanguage=(0,_options.getCurrentLanguage)(this.editor);return{installed:Object.entries(moodleLangs.installed).map((_ref2=>{let[lang,code]=_ref2;return{lang:lang,code:code,default:lang===currentLanguage}})),available:Object.entries(moodleLangs.available).map((_ref3=>{let[lang,code]=_ref3;return{lang:lang,code:code,default:lang===currentLanguage}}))}}getMoodleLangObj(subtitleLang){const{available:available}=(0,_options.getMoodleLang)(this.editor);return available[subtitleLang]?{lang:subtitleLang,code:available[subtitleLang]}:null}filePickerCallback(params,element,fpType){if(""!==params.url){const tabPane=element.closest(".tab-pane");if(element.closest(_selectors.default.EMBED.elements.source).querySelector(_selectors.default.EMBED.elements.url).value=params.url,tabPane.id===this.editor.getElement().id+"_"+_selectors.default.EMBED.mediaTypes.link.toLowerCase()&&(tabPane.querySelector(_selectors.default.EMBED.elements.name).value=params.file),"subtitle"===fpType){const subtitleLang=params.file.split(".vtt")[0].split("-").slice(-1)[0],langObj=this.getMoodleLangObj(subtitleLang);if(langObj){const track=element.closest(_selectors.default.EMBED.elements.track);track.querySelector(_selectors.default.EMBED.elements.trackLabel).value=langObj.lang.trim(),track.querySelector(_selectors.default.EMBED.elements.trackLang).value=langObj.code}}}}addMediaSourceComponent(element,callback){const sourceElement=element.closest(_selectors.default.EMBED.elements.source+_selectors.default.EMBED.elements.mediaSource),clone=sourceElement.cloneNode(!0);sourceElement.querySelector(".removecomponent-wrapper").classList.remove("hidden"),sourceElement.querySelector(".addcomponent-wrapper").classList.add("hidden"),sourceElement.parentNode.insertBefore(clone,sourceElement.nextSibling),callback&&callback(clone)}removeMediaSourceComponent(element){element.closest(_selectors.default.EMBED.elements.source+_selectors.default.EMBED.elements.mediaSource).remove()}addTrackComponent(element,callback){const trackElement=element.closest(_selectors.default.EMBED.elements.track),clone=trackElement.cloneNode(!0);trackElement.querySelector(".removecomponent-wrapper").classList.remove("hidden"),trackElement.querySelector(".addcomponent-wrapper").classList.add("hidden"),trackElement.parentNode.insertBefore(clone,trackElement.nextSibling),callback&&callback(clone)}removeTrackComponent(element){element.closest(_selectors.default.EMBED.elements.track).remove()}getMediumTypeFromTabPane(tabPane){return tabPane.getAttribute("data-medium-type")}getTrackTypeFromTabPane(tabPane){return tabPane.getAttribute("data-track-kind")}getMediaHTML(form){const mediumType=this.getMediumTypeFromTabPane(form.querySelector(".root.tab-content > .tab-pane.active")),tabContent=form.querySelector(_selectors.default.EMBED.elements[mediumType.toLowerCase()+"Pane"]);return this["getMediaHTML"+mediumType[0].toUpperCase()+mediumType.substr(1)](tabContent)}getMediaHTMLLink(tab){const context={url:tab.querySelector(_selectors.default.EMBED.elements.url).value,name:tab.querySelector(_selectors.default.EMBED.elements.name).value||!1};return context.url?_templates.default.renderForPromise("tiny_media/embed_media_link",context):""}getMediaHTMLVideo(tab){const context=this.getContextForMediaHTML(tab);return context.width=tab.querySelector(_selectors.default.EMBED.elements.width).value||!1,context.height=tab.querySelector(_selectors.default.EMBED.elements.height).value||!1,context.poster=tab.querySelector("".concat(_selectors.default.EMBED.elements.posterSource," ").concat(_selectors.default.EMBED.elements.url)).value||!1,context.sources.length?_templates.default.renderForPromise("tiny_media/embed_media_video",context):""}getMediaHTMLAudio(tab){const context=this.getContextForMediaHTML(tab);return context.sources.length?_templates.default.renderForPromise("tiny_media/embed_media_audio",context):""}getContextForMediaHTML(tab){const tracks=Array.from(tab.querySelectorAll(_selectors.default.EMBED.elements.track)).map((track=>({track:track.querySelector(_selectors.default.EMBED.elements.trackSource+" "+_selectors.default.EMBED.elements.url).value,kind:this.getTrackTypeFromTabPane(track.closest(".tab-pane")),label:track.querySelector(_selectors.default.EMBED.elements.trackLabel).value||track.querySelector(_selectors.default.EMBED.elements.trackLang).value,srclang:track.querySelector(_selectors.default.EMBED.elements.trackLang).value,defaultTrack:track.querySelector(_selectors.default.EMBED.elements.trackDefault).checked?"true":null}))).filter((track=>!!track.track));return{sources:Array.from(tab.querySelectorAll(_selectors.default.EMBED.elements.mediaSource+" "+_selectors.default.EMBED.elements.url)).filter((source=>!!source.value)).map((source=>source.value)),description:tab.querySelector(_selectors.default.EMBED.elements.mediaSource+" "+_selectors.default.EMBED.elements.url).value||!1,tracks:tracks,showControls:tab.querySelector(_selectors.default.EMBED.elements.mediaControl).checked,autoplay:tab.querySelector(_selectors.default.EMBED.elements.mediaAutoplay).checked,muted:tab.querySelector(_selectors.default.EMBED.elements.mediaMute).checked,loop:tab.querySelector(_selectors.default.EMBED.elements.mediaLoop).checked,title:tab.querySelector(_selectors.default.EMBED.elements.title).value||!1}}getFilepickerTypeFromElement(element){return element.closest(_selectors.default.EMBED.elements.posterSource)?"image":element.closest(_selectors.default.EMBED.elements.trackSource)?"subtitle":"media"}async clickHandler(e){const element=e.target;if(element.closest(_selectors.default.EMBED.actions.mediaBrowser)){e.preventDefault();const fpType=this.getFilepickerTypeFromElement(element),params=await(0,_utils.displayFilepicker)(this.editor,fpType);this.filePickerCallback(params,element,fpType)}element.closest(_selectors.default.EMBED.elements.mediaSource+" .addcomponent")&&(e.preventDefault(),this.addMediaSourceComponent(element));element.closest(_selectors.default.EMBED.elements.mediaSource+" .removecomponent")&&(e.preventDefault(),this.removeMediaSourceComponent(element));element.closest(_selectors.default.EMBED.elements.track+" .addcomponent")&&(e.preventDefault(),this.addTrackComponent(element));element.closest(_selectors.default.EMBED.elements.track+" .removecomponent")&&(e.preventDefault(),this.removeTrackComponent(element));const trackDefaultAction=element.closest(_selectors.default.EMBED.elements.trackDefault);if(trackDefaultAction&&trackDefaultAction.checked){const getKind=el=>this.getTrackTypeFromTabPane(el.parentElement.closest(".tab-pane"));element.parentElement.closest(".root.tab-content").querySelectorAll(_selectors.default.EMBED.elements.trackDefault).forEach((select=>{select!==element&&getKind(element)===getKind(select)&&(select.checked=!1)}))}}async handleDialogueSubmission(event,modal){const{html:html}=await this.getMediaHTML(modal.getRoot()[0]);if(html)if(this.isUpdating){this.getSelectedMedia().outerHTML=html,this.isUpdating=!1}else this.editor.insertContent(html)}async registerEventListeners(modal){await modal.getBody();const $root=modal.getRoot(),root=$root[0];this.canShowFilePicker&&root.addEventListener("click",this.clickHandler.bind(this)),$root.on(ModalEvents.save,this.handleDialogueSubmission.bind(this)),$root.on(ModalEvents.hidden,(()=>{this.currentModal.destroy()})),$root.on(ModalEvents.shown,(()=>{root.querySelectorAll(_selectors.default.EMBED.elements.trackLang).forEach((dropdown=>{const defaultVal=dropdown.getAttribute("data-value");defaultVal&&(dropdown.value=defaultVal)}))}))}}})); +define("tiny_media/embed",["exports","core/templates","core/str","core/modal_factory","core/modal_events","editor_tiny/utils","editor_tiny/options","./common","./embedmodal","./selectors","./options"],(function(_exports,_templates,_str,ModalFactory,ModalEvents,_utils,_options,_common,_embedmodal,_selectors,_options2){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.MediaEmbed=void 0,_templates=_interopRequireDefault(_templates),ModalFactory=_interopRequireWildcard(ModalFactory),ModalEvents=_interopRequireWildcard(ModalEvents),_embedmodal=_interopRequireDefault(_embedmodal),_selectors=_interopRequireDefault(_selectors);_exports.MediaEmbed=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"canShowFilePicker",!1),_defineProperty(this,"helpStrings",null),_defineProperty(this,"isUpdating",!1);const permissions=(0,_options2.getEmbedPermissions)(editor);this.canShowFilePicker=permissions.filepicker,this.editor=editor}async getHelpStrings(){if(!this.helpStrings){const[addSource,tracks,subtitles,captions,descriptions,chapters,metadata]=await(0,_str.getStrings)(["addsource_help","tracks_help","subtitles_help","captions_help","descriptions_help","chapters_help","metadata_help"].map((key=>({key:key,component:_common.component}))));this.helpStrings={addSource:addSource,tracks:tracks,subtitles:subtitles,captions:captions,descriptions:descriptions,chapters:chapters,metadata:metadata}}return this.helpStrings}async getTemplateContext(data){const languages=this.prepareMoodleLang(),helpIcons=Array.from(Object.entries(await this.getHelpStrings())).forEach((_ref=>{let[key,text]=_ref;data["".concat(key.toLowerCase(),"helpicon")]={text:text}}));return Object.assign({},{elementid:this.editor.getElement().id,showfilepicker:this.canShowFilePicker,langsinstalled:languages.installed,langsavailable:languages.available,link:!0,video:!1,audio:!1,isupdating:this.isUpdating},data,helpIcons)}async displayDialogue(){const data=Object.assign({},this.getCurrentEmbedData());this.isUpdating=0!==Object.keys(data).length;const modal=await ModalFactory.create({type:_embedmodal.default.TYPE,title:(0,_str.getString)("createmedia","tiny_media"),templateContext:await this.getTemplateContext(data),removeOnClose:!0,large:!0});this.currentModal=modal,await this.registerEventListeners(modal),modal.show()}getCurrentEmbedData(){const properties=this.getMediumProperties();if(!properties)return{};const processedProperties={};return processedProperties[properties.type.toLowerCase()]=properties,processedProperties.link=!1,processedProperties}getSelectedMedia(){const mediaElm=this.editor.selection.getNode();return mediaElm?"video"===mediaElm.nodeName.toLowerCase()||"audio"===mediaElm.nodeName.toLowerCase()?mediaElm:mediaElm.querySelector("video")?mediaElm.querySelector("video"):mediaElm.querySelector("audio")?mediaElm.querySelector("audio"):null:null}getMediumProperties(){const boolAttr=(elem,attr)=>elem.hasAttribute(attr)&&(elem.getAttribute(attr)||""===elem.getAttribute(attr)),tracks={subtitles:[],captions:[],descriptions:[],chapters:[],metadata:[]},sources=[],medium=this.getSelectedMedia();return medium?(medium.querySelectorAll("track").forEach((track=>{tracks[track.getAttribute("kind")].push({src:track.getAttribute("src"),srclang:track.getAttribute("srclang"),label:track.getAttribute("label"),defaultTrack:boolAttr(track,"default")})})),medium.querySelectorAll("source").forEach((source=>{sources.push(source.src)})),{type:"video"===medium.nodeName.toLowerCase()?_selectors.default.EMBED.mediaTypes.video:_selectors.default.EMBED.mediaTypes.audio,sources:sources,poster:medium.getAttribute("poster"),title:medium.getAttribute("title"),width:medium.getAttribute("width"),height:medium.getAttribute("height"),autoplay:boolAttr(medium,"autoplay"),loop:boolAttr(medium,"loop"),muted:boolAttr(medium,"muted"),controls:boolAttr(medium,"controls"),tracks:tracks}):null}prepareMoodleLang(){const moodleLangs=(0,_options.getMoodleLang)(this.editor),currentLanguage=(0,_options.getCurrentLanguage)(this.editor);return{installed:Object.entries(moodleLangs.installed).map((_ref2=>{let[lang,code]=_ref2;return{lang:lang,code:code,default:lang===currentLanguage}})),available:Object.entries(moodleLangs.available).map((_ref3=>{let[lang,code]=_ref3;return{lang:lang,code:code,default:lang===currentLanguage}}))}}getMoodleLangObj(subtitleLang){const{available:available}=(0,_options.getMoodleLang)(this.editor);return available[subtitleLang]?{lang:subtitleLang,code:available[subtitleLang]}:null}filePickerCallback(params,element,fpType){if(""!==params.url){const tabPane=element.closest(".tab-pane");if(element.closest(_selectors.default.EMBED.elements.source).querySelector(_selectors.default.EMBED.elements.url).value=params.url,tabPane.id===this.editor.getElement().id+"_"+_selectors.default.EMBED.mediaTypes.link.toLowerCase()&&(tabPane.querySelector(_selectors.default.EMBED.elements.name).value=params.file),"subtitle"===fpType){const subtitleLang=params.file.split(".vtt")[0].split("-").slice(-1)[0],langObj=this.getMoodleLangObj(subtitleLang);if(langObj){const track=element.closest(_selectors.default.EMBED.elements.track);track.querySelector(_selectors.default.EMBED.elements.trackLabel).value=langObj.lang.trim(),track.querySelector(_selectors.default.EMBED.elements.trackLang).value=langObj.code}}}}addMediaSourceComponent(element,callback){const sourceElement=element.closest(_selectors.default.EMBED.elements.source+_selectors.default.EMBED.elements.mediaSource),clone=sourceElement.cloneNode(!0);sourceElement.querySelector(".removecomponent-wrapper").classList.remove("hidden"),sourceElement.querySelector(".addcomponent-wrapper").classList.add("hidden"),sourceElement.parentNode.insertBefore(clone,sourceElement.nextSibling),callback&&callback(clone)}removeMediaSourceComponent(element){element.closest(_selectors.default.EMBED.elements.source+_selectors.default.EMBED.elements.mediaSource).remove()}addTrackComponent(element,callback){const trackElement=element.closest(_selectors.default.EMBED.elements.track),clone=trackElement.cloneNode(!0);trackElement.querySelector(".removecomponent-wrapper").classList.remove("hidden"),trackElement.querySelector(".addcomponent-wrapper").classList.add("hidden"),trackElement.parentNode.insertBefore(clone,trackElement.nextSibling),callback&&callback(clone)}removeTrackComponent(element){element.closest(_selectors.default.EMBED.elements.track).remove()}getMediumTypeFromTabPane(tabPane){return tabPane.getAttribute("data-medium-type")}getTrackTypeFromTabPane(tabPane){return tabPane.getAttribute("data-track-kind")}getMediaHTML(form){const mediumType=this.getMediumTypeFromTabPane(form.querySelector(".root.tab-content > .tab-pane.active")),tabContent=form.querySelector(_selectors.default.EMBED.elements[mediumType.toLowerCase()+"Pane"]);return this["getMediaHTML"+mediumType[0].toUpperCase()+mediumType.substr(1)](tabContent)}getMediaHTMLLink(tab){const context={url:tab.querySelector(_selectors.default.EMBED.elements.url).value,name:tab.querySelector(_selectors.default.EMBED.elements.name).value||!1};return context.url?_templates.default.renderForPromise("tiny_media/embed_media_link",context):""}getMediaHTMLVideo(tab){const context=this.getContextForMediaHTML(tab);return context.width=tab.querySelector(_selectors.default.EMBED.elements.width).value||!1,context.height=tab.querySelector(_selectors.default.EMBED.elements.height).value||!1,context.poster=tab.querySelector("".concat(_selectors.default.EMBED.elements.posterSource," ").concat(_selectors.default.EMBED.elements.url)).value||!1,context.sources.length?_templates.default.renderForPromise("tiny_media/embed_media_video",context):""}getMediaHTMLAudio(tab){const context=this.getContextForMediaHTML(tab);return context.sources.length?_templates.default.renderForPromise("tiny_media/embed_media_audio",context):""}getContextForMediaHTML(tab){const tracks=Array.from(tab.querySelectorAll(_selectors.default.EMBED.elements.track)).map((track=>({track:track.querySelector(_selectors.default.EMBED.elements.trackSource+" "+_selectors.default.EMBED.elements.url).value,kind:this.getTrackTypeFromTabPane(track.closest(".tab-pane")),label:track.querySelector(_selectors.default.EMBED.elements.trackLabel).value||track.querySelector(_selectors.default.EMBED.elements.trackLang).value,srclang:track.querySelector(_selectors.default.EMBED.elements.trackLang).value,defaultTrack:track.querySelector(_selectors.default.EMBED.elements.trackDefault).checked?"true":null}))).filter((track=>!!track.track));return{sources:Array.from(tab.querySelectorAll(_selectors.default.EMBED.elements.mediaSource+" "+_selectors.default.EMBED.elements.url)).filter((source=>!!source.value)).map((source=>source.value)),description:tab.querySelector(_selectors.default.EMBED.elements.mediaSource+" "+_selectors.default.EMBED.elements.url).value||!1,tracks:tracks,showControls:tab.querySelector(_selectors.default.EMBED.elements.mediaControl).checked,autoplay:tab.querySelector(_selectors.default.EMBED.elements.mediaAutoplay).checked,muted:tab.querySelector(_selectors.default.EMBED.elements.mediaMute).checked,loop:tab.querySelector(_selectors.default.EMBED.elements.mediaLoop).checked,title:tab.querySelector(_selectors.default.EMBED.elements.title).value||!1}}getFilepickerTypeFromElement(element){return element.closest(_selectors.default.EMBED.elements.posterSource)?"image":element.closest(_selectors.default.EMBED.elements.trackSource)?"subtitle":"media"}async clickHandler(e){const element=e.target;if(element.closest(_selectors.default.EMBED.actions.mediaBrowser)){e.preventDefault();const fpType=this.getFilepickerTypeFromElement(element),params=await(0,_utils.displayFilepicker)(this.editor,fpType);this.filePickerCallback(params,element,fpType)}element.closest(_selectors.default.EMBED.elements.mediaSource+" .addcomponent")&&(e.preventDefault(),this.addMediaSourceComponent(element));element.closest(_selectors.default.EMBED.elements.mediaSource+" .removecomponent")&&(e.preventDefault(),this.removeMediaSourceComponent(element));element.closest(_selectors.default.EMBED.elements.track+" .addcomponent")&&(e.preventDefault(),this.addTrackComponent(element));element.closest(_selectors.default.EMBED.elements.track+" .removecomponent")&&(e.preventDefault(),this.removeTrackComponent(element));const trackDefaultAction=element.closest(_selectors.default.EMBED.elements.trackDefault);if(trackDefaultAction&&trackDefaultAction.checked){const getKind=el=>this.getTrackTypeFromTabPane(el.parentElement.closest(".tab-pane"));element.parentElement.closest(".root.tab-content").querySelectorAll(_selectors.default.EMBED.elements.trackDefault).forEach((select=>{select!==element&&getKind(element)===getKind(select)&&(select.checked=!1)}))}}async handleDialogueSubmission(event,modal){const{html:html}=await this.getMediaHTML(modal.getRoot()[0]);if(html)if(this.isUpdating){this.getSelectedMedia().outerHTML=html,this.isUpdating=!1}else this.editor.insertContent(html)}async registerEventListeners(modal){await modal.getBody();const $root=modal.getRoot(),root=$root[0];this.canShowFilePicker&&root.addEventListener("click",this.clickHandler.bind(this)),$root.on(ModalEvents.save,this.handleDialogueSubmission.bind(this)),$root.on(ModalEvents.hidden,(()=>{this.currentModal.destroy()})),$root.on(ModalEvents.shown,(()=>{root.querySelectorAll(_selectors.default.EMBED.elements.trackLang).forEach((dropdown=>{const defaultVal=dropdown.getAttribute("data-value");defaultVal&&(dropdown.value=defaultVal)}))}))}}})); //# sourceMappingURL=embed.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/embed.min.js.map b/lib/editor/tiny/plugins/media/amd/build/embed.min.js.map index 00f2f47086f..11524999864 100644 --- a/lib/editor/tiny/plugins/media/amd/build/embed.min.js.map +++ b/lib/editor/tiny/plugins/media/amd/build/embed.min.js.map @@ -1 +1 @@ -{"version":3,"file":"embed.min.js","sources":["../src/embed.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 * Tiny Media plugin Embed class for Moodle.\n *\n * @module tiny_media/embed\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {\n get_string as getString,\n get_strings as getStrings,\n} from 'core/str';\nimport * as ModalFactory from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport {getCurrentLanguage, getMoodleLang} from 'editor_tiny/options';\nimport {component} from \"./common\";\nimport Modal from './embedmodal';\nimport Selectors from './selectors';\nimport {getEmbedPermissions} from './options';\n\nexport const MediaEmbed = class {\n editor = null;\n canShowFilePicker = false;\n\n /**\n * @property {Object} The names of the alignment options.\n */\n helpStrings = null;\n\n /**\n * @property {boolean} Indicate that the user is updating the media or not.\n */\n isUpdating = false;\n\n constructor(editor) {\n const permissions = getEmbedPermissions(editor);\n this.canShowFilePicker = permissions.filepicker;\n this.editor = editor;\n }\n\n async getHelpStrings() {\n if (!this.helpStrings) {\n const [addSource, tracks, subtitles, captions, descriptions, chapters, metadata] = await getStrings([\n 'addsource_help',\n 'tracks_help',\n 'subtitles_help',\n 'captions_help',\n 'descriptions_help',\n 'chapters_help',\n 'metadata_help',\n ].map((key) => ({\n key,\n component,\n })));\n\n this.helpStrings = {addSource, tracks, subtitles, captions, descriptions, chapters, metadata};\n }\n\n return this.helpStrings;\n }\n\n async getTemplateContext(data) {\n const languages = this.prepareMoodleLang();\n\n const helpIcons = Array.from(Object.entries(await this.getHelpStrings())).forEach(([key, text]) => {\n data[`${key.toLowerCase()}helpicon`] = {text};\n });\n\n return Object.assign({}, {\n elementid: this.editor.getElement().id,\n showfilepicker: this.canShowFilePicker,\n langsinstalled: languages.installed,\n langsavailable: languages.available,\n link: true,\n video: false,\n audio: false,\n isupdating: this.isUpdating,\n }, data, helpIcons);\n }\n\n async displayDialogue() {\n const data = Object.assign({}, this.getCurrentEmbedData());\n this.isUpdating = Object.keys(data).length !== 0;\n\n const modal = await ModalFactory.create({\n type: Modal.TYPE,\n title: getString('createmedia', 'tiny_media'),\n templateContext: await this.getTemplateContext(data),\n removeOnClose: true,\n large: true,\n });\n\n this.currentModal = modal;\n await this.registerEventListeners(modal);\n modal.show();\n }\n\n getCurrentEmbedData() {\n const properties = this.getMediumProperties();\n if (!properties) {\n return {};\n }\n\n const processedProperties = {};\n processedProperties[properties.type.toLowerCase()] = properties;\n processedProperties.link = false;\n\n return processedProperties;\n }\n\n getSelectedMedia() {\n const mediaElm = this.editor.selection.getNode();\n\n if (!mediaElm) {\n return null;\n }\n\n if (mediaElm.nodeName.toLowerCase() === 'video' || mediaElm.nodeName.toLowerCase() === 'audio') {\n return mediaElm;\n }\n\n if (mediaElm.querySelector('video')) {\n return mediaElm.querySelector('video');\n }\n\n if (mediaElm.querySelector('audio')) {\n return mediaElm.querySelector('audio');\n }\n\n return null;\n }\n\n getMediumProperties() {\n const boolAttr = (elem, attr) => {\n // As explained in MDL-64175, some OS (like Ubuntu), are removing the value for these attributes.\n // So in order to check if attr=\"true\", we need to check if the attribute exists and if the value is empty or true.\n return (elem.hasAttribute(attr) && (elem.getAttribute(attr) || elem.getAttribute(attr) === ''));\n };\n\n const tracks = {\n subtitles: [],\n captions: [],\n descriptions: [],\n chapters: [],\n metadata: []\n };\n const sources = [];\n\n const medium = this.getSelectedMedia();\n if (!medium) {\n return null;\n }\n medium.querySelectorAll('track').forEach((track) => {\n tracks[track.getAttribute('kind')].push({\n src: track.getAttribute('src'),\n srclang: track.getAttribute('srclang'),\n label: track.getAttribute('label'),\n defaultTrack: boolAttr(track, 'default')\n });\n });\n\n medium.querySelectorAll('source').forEach((source) => {\n sources.push(source.src);\n });\n\n return {\n type: medium.nodeName.toLowerCase() === 'video' ? Selectors.EMBED.mediaTypes.video : Selectors.EMBED.mediaTypes.audio,\n sources,\n poster: medium.getAttribute('poster'),\n title: medium.getAttribute('title'),\n width: medium.getAttribute('width'),\n height: medium.getAttribute('height'),\n autoplay: boolAttr(medium, 'autoplay'),\n loop: boolAttr(medium, 'loop'),\n muted: boolAttr(medium, 'muted'),\n controls: boolAttr(medium, 'controls'),\n tracks,\n };\n }\n\n prepareMoodleLang() {\n const moodleLangs = getMoodleLang(this.editor);\n const currentLanguage = getCurrentLanguage(this.editor);\n\n const installed = Object.entries(moodleLangs.installed).map(([lang, code]) => ({\n lang,\n code,\n \"default\": lang === currentLanguage,\n }));\n\n const available = Object.entries(moodleLangs.available).map(([lang, code]) => ({\n lang,\n code,\n \"default\": lang === currentLanguage,\n }));\n\n return {\n installed,\n available,\n };\n }\n\n getMoodleLangObj(subtitleLang) {\n const {available} = getMoodleLang(this.editor);\n\n if (available[subtitleLang]) {\n return {\n lang: subtitleLang,\n code: available[subtitleLang],\n };\n }\n\n return null;\n }\n\n filePickerCallback(params, element, fpType) {\n if (params.url !== '') {\n const tabPane = element.closest('.tab-pane');\n element.closest(Selectors.EMBED.elements.source).querySelector(Selectors.EMBED.elements.url).value = params.url;\n\n if (tabPane.id === this.editor.getElement().id + '_' + Selectors.EMBED.mediaTypes.link.toLowerCase()) {\n tabPane.querySelector(Selectors.EMBED.elements.name).value = params.file;\n }\n\n if (fpType === 'subtitle') {\n // If the file is subtitle file. We need to match the language and label for that file.\n const subtitleLang = params.file.split('.vtt')[0].split('-').slice(-1)[0];\n const langObj = this.getMoodleLangObj(subtitleLang);\n if (langObj) {\n const track = element.closest(Selectors.EMBED.elements.track);\n track.querySelector(Selectors.EMBED.elements.trackLabel).value = langObj.lang.trim();\n track.querySelector(Selectors.EMBED.elements.trackLang).value = langObj.code;\n }\n }\n }\n }\n\n addMediaSourceComponent(element, callback) {\n const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);\n const clone = sourceElement.cloneNode(true);\n\n sourceElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');\n sourceElement.querySelector('.addcomponent-wrapper').classList.add('hidden');\n\n sourceElement.parentNode.insertBefore(clone, sourceElement.nextSibling);\n\n if (callback) {\n callback(clone);\n }\n }\n\n removeMediaSourceComponent(element) {\n const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);\n sourceElement.remove();\n }\n\n addTrackComponent(element, callback) {\n const trackElement = element.closest(Selectors.EMBED.elements.track);\n const clone = trackElement.cloneNode(true);\n\n trackElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');\n trackElement.querySelector('.addcomponent-wrapper').classList.add('hidden');\n\n trackElement.parentNode.insertBefore(clone, trackElement.nextSibling);\n\n if (callback) {\n callback(clone);\n }\n }\n\n removeTrackComponent(element) {\n const sourceElement = element.closest(Selectors.EMBED.elements.track);\n sourceElement.remove();\n }\n\n getMediumTypeFromTabPane(tabPane) {\n return tabPane.getAttribute('data-medium-type');\n }\n\n getTrackTypeFromTabPane(tabPane) {\n return tabPane.getAttribute('data-track-kind');\n }\n\n getMediaHTML(form) {\n const mediumType = this.getMediumTypeFromTabPane(form.querySelector('.root.tab-content > .tab-pane.active'));\n const tabContent = form.querySelector(Selectors.EMBED.elements[mediumType.toLowerCase() + 'Pane']);\n\n return this['getMediaHTML' + mediumType[0].toUpperCase() + mediumType.substr(1)](tabContent);\n }\n\n getMediaHTMLLink(tab) {\n const context = {\n url: tab.querySelector(Selectors.EMBED.elements.url).value,\n name: tab.querySelector(Selectors.EMBED.elements.name).value || false\n };\n\n return context.url ? Templates.renderForPromise('tiny_media/embed_media_link', context) : '';\n }\n\n getMediaHTMLVideo(tab) {\n const context = this.getContextForMediaHTML(tab);\n context.width = tab.querySelector(Selectors.EMBED.elements.width).value || false;\n context.height = tab.querySelector(Selectors.EMBED.elements.height).value || false;\n context.poster = tab.querySelector(\n `${Selectors.EMBED.elements.posterSource} ${Selectors.EMBED.elements.url}`\n ).value || false;\n\n return context.sources.length ? Templates.renderForPromise('tiny_media/embed_media_video', context) : '';\n }\n\n getMediaHTMLAudio(tab) {\n const context = this.getContextForMediaHTML(tab);\n\n return context.sources.length ? Templates.renderForPromise('tiny_media/embed_media_audio', context) : '';\n }\n\n getContextForMediaHTML(tab) {\n const tracks = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.track)).map(track => ({\n track: track.querySelector(Selectors.EMBED.elements.trackSource + ' ' + Selectors.EMBED.elements.url).value,\n kind: this.getTrackTypeFromTabPane(track.closest('.tab-pane')),\n label: track.querySelector(Selectors.EMBED.elements.trackLabel).value ||\n track.querySelector(Selectors.EMBED.elements.trackLang).value,\n srclang: track.querySelector(Selectors.EMBED.elements.trackLang).value,\n defaultTrack: track.querySelector(Selectors.EMBED.elements.trackDefault).checked ? \"true\" : null\n })).filter((track) => !!track.track);\n\n const sources = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.mediaSource + ' '\n + Selectors.EMBED.elements.url))\n .filter((source) => !!source.value)\n .map((source) => source.value);\n\n return {\n sources,\n description: tab.querySelector(Selectors.EMBED.elements.mediaSource + ' '\n + Selectors.EMBED.elements.url).value || false,\n tracks,\n showControls: tab.querySelector(Selectors.EMBED.elements.mediaControl).checked,\n autoplay: tab.querySelector(Selectors.EMBED.elements.mediaAutoplay).checked,\n muted: tab.querySelector(Selectors.EMBED.elements.mediaMute).checked,\n loop: tab.querySelector(Selectors.EMBED.elements.mediaLoop).checked,\n title: tab.querySelector(Selectors.EMBED.elements.title).value || false\n };\n }\n\n getFilepickerTypeFromElement(element) {\n if (element.closest(Selectors.EMBED.elements.posterSource)) {\n return 'image';\n }\n if (element.closest(Selectors.EMBED.elements.trackSource)) {\n return 'subtitle';\n }\n\n return 'media';\n }\n\n async clickHandler(e) {\n const element = e.target;\n\n const mediaBrowser = element.closest(Selectors.EMBED.actions.mediaBrowser);\n if (mediaBrowser) {\n e.preventDefault();\n const fpType = this.getFilepickerTypeFromElement(element);\n const params = await displayFilepicker(this.editor, fpType);\n this.filePickerCallback(params, element, fpType);\n }\n\n const addComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .addcomponent');\n if (addComponentSourceAction) {\n e.preventDefault();\n this.addMediaSourceComponent(element);\n }\n\n const removeComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .removecomponent');\n if (removeComponentSourceAction) {\n e.preventDefault();\n this.removeMediaSourceComponent(element);\n }\n\n const addComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .addcomponent');\n if (addComponentTrackAction) {\n e.preventDefault();\n this.addTrackComponent(element);\n }\n\n const removeComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .removecomponent');\n if (removeComponentTrackAction) {\n e.preventDefault();\n this.removeTrackComponent(element);\n }\n\n // Only allow one track per tab to be selected as \"default\".\n const trackDefaultAction = element.closest(Selectors.EMBED.elements.trackDefault);\n if (trackDefaultAction && trackDefaultAction.checked) {\n const getKind = (el) => this.getTrackTypeFromTabPane(el.parentElement.closest('.tab-pane'));\n\n element.parentElement\n .closest('.root.tab-content')\n .querySelectorAll(Selectors.EMBED.elements.trackDefault)\n .forEach((select) => {\n if (select !== element && getKind(element) === getKind(select)) {\n select.checked = false;\n }\n });\n }\n }\n\n async handleDialogueSubmission(event, modal) {\n const {html} = await this.getMediaHTML(modal.getRoot()[0]);\n if (html) {\n if (this.isUpdating) {\n const selectedNode = this.getSelectedMedia();\n selectedNode.outerHTML = html;\n this.isUpdating = false;\n } else {\n this.editor.insertContent(html);\n }\n }\n }\n\n async registerEventListeners(modal) {\n await modal.getBody();\n const $root = modal.getRoot();\n const root = $root[0];\n if (this.canShowFilePicker) {\n root.addEventListener('click', this.clickHandler.bind(this));\n }\n\n $root.on(ModalEvents.save, this.handleDialogueSubmission.bind(this));\n $root.on(ModalEvents.hidden, () => {\n this.currentModal.destroy();\n });\n $root.on(ModalEvents.shown, () => {\n root.querySelectorAll(Selectors.EMBED.elements.trackLang).forEach((dropdown) => {\n const defaultVal = dropdown.getAttribute('data-value');\n if (defaultVal) {\n dropdown.value = defaultVal;\n }\n });\n });\n }\n};\n"],"names":["constructor","editor","permissions","canShowFilePicker","filepicker","this","helpStrings","addSource","tracks","subtitles","captions","descriptions","chapters","metadata","map","key","component","data","languages","prepareMoodleLang","helpIcons","Array","from","Object","entries","getHelpStrings","forEach","_ref","text","toLowerCase","assign","elementid","getElement","id","showfilepicker","langsinstalled","installed","langsavailable","available","link","video","audio","isupdating","isUpdating","getCurrentEmbedData","keys","length","modal","ModalFactory","create","type","Modal","TYPE","title","templateContext","getTemplateContext","removeOnClose","large","currentModal","registerEventListeners","show","properties","getMediumProperties","processedProperties","getSelectedMedia","mediaElm","selection","getNode","nodeName","querySelector","boolAttr","elem","attr","hasAttribute","getAttribute","sources","medium","querySelectorAll","track","push","src","srclang","label","defaultTrack","source","Selectors","EMBED","mediaTypes","poster","width","height","autoplay","loop","muted","controls","moodleLangs","currentLanguage","_ref2","lang","code","_ref3","getMoodleLangObj","subtitleLang","filePickerCallback","params","element","fpType","url","tabPane","closest","elements","value","name","file","split","slice","langObj","trackLabel","trim","trackLang","addMediaSourceComponent","callback","sourceElement","mediaSource","clone","cloneNode","classList","remove","add","parentNode","insertBefore","nextSibling","removeMediaSourceComponent","addTrackComponent","trackElement","removeTrackComponent","getMediumTypeFromTabPane","getTrackTypeFromTabPane","getMediaHTML","form","mediumType","tabContent","toUpperCase","substr","getMediaHTMLLink","tab","context","Templates","renderForPromise","getMediaHTMLVideo","getContextForMediaHTML","posterSource","getMediaHTMLAudio","trackSource","kind","trackDefault","checked","filter","description","showControls","mediaControl","mediaAutoplay","mediaMute","mediaLoop","getFilepickerTypeFromElement","e","target","actions","mediaBrowser","preventDefault","trackDefaultAction","getKind","el","parentElement","select","event","html","getRoot","outerHTML","insertContent","getBody","$root","root","addEventListener","clickHandler","bind","on","ModalEvents","save","handleDialogueSubmission","hidden","destroy","shown","dropdown","defaultVal"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"embed.min.js","sources":["../src/embed.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 * Tiny Media plugin Embed class for Moodle.\n *\n * @module tiny_media/embed\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {\n getString,\n getStrings,\n} from 'core/str';\nimport * as ModalFactory from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport {getCurrentLanguage, getMoodleLang} from 'editor_tiny/options';\nimport {component} from \"./common\";\nimport Modal from './embedmodal';\nimport Selectors from './selectors';\nimport {getEmbedPermissions} from './options';\n\nexport const MediaEmbed = class {\n editor = null;\n canShowFilePicker = false;\n\n /**\n * @property {Object} The names of the alignment options.\n */\n helpStrings = null;\n\n /**\n * @property {boolean} Indicate that the user is updating the media or not.\n */\n isUpdating = false;\n\n constructor(editor) {\n const permissions = getEmbedPermissions(editor);\n this.canShowFilePicker = permissions.filepicker;\n this.editor = editor;\n }\n\n async getHelpStrings() {\n if (!this.helpStrings) {\n const [addSource, tracks, subtitles, captions, descriptions, chapters, metadata] = await getStrings([\n 'addsource_help',\n 'tracks_help',\n 'subtitles_help',\n 'captions_help',\n 'descriptions_help',\n 'chapters_help',\n 'metadata_help',\n ].map((key) => ({\n key,\n component,\n })));\n\n this.helpStrings = {addSource, tracks, subtitles, captions, descriptions, chapters, metadata};\n }\n\n return this.helpStrings;\n }\n\n async getTemplateContext(data) {\n const languages = this.prepareMoodleLang();\n\n const helpIcons = Array.from(Object.entries(await this.getHelpStrings())).forEach(([key, text]) => {\n data[`${key.toLowerCase()}helpicon`] = {text};\n });\n\n return Object.assign({}, {\n elementid: this.editor.getElement().id,\n showfilepicker: this.canShowFilePicker,\n langsinstalled: languages.installed,\n langsavailable: languages.available,\n link: true,\n video: false,\n audio: false,\n isupdating: this.isUpdating,\n }, data, helpIcons);\n }\n\n async displayDialogue() {\n const data = Object.assign({}, this.getCurrentEmbedData());\n this.isUpdating = Object.keys(data).length !== 0;\n\n const modal = await ModalFactory.create({\n type: Modal.TYPE,\n title: getString('createmedia', 'tiny_media'),\n templateContext: await this.getTemplateContext(data),\n removeOnClose: true,\n large: true,\n });\n\n this.currentModal = modal;\n await this.registerEventListeners(modal);\n modal.show();\n }\n\n getCurrentEmbedData() {\n const properties = this.getMediumProperties();\n if (!properties) {\n return {};\n }\n\n const processedProperties = {};\n processedProperties[properties.type.toLowerCase()] = properties;\n processedProperties.link = false;\n\n return processedProperties;\n }\n\n getSelectedMedia() {\n const mediaElm = this.editor.selection.getNode();\n\n if (!mediaElm) {\n return null;\n }\n\n if (mediaElm.nodeName.toLowerCase() === 'video' || mediaElm.nodeName.toLowerCase() === 'audio') {\n return mediaElm;\n }\n\n if (mediaElm.querySelector('video')) {\n return mediaElm.querySelector('video');\n }\n\n if (mediaElm.querySelector('audio')) {\n return mediaElm.querySelector('audio');\n }\n\n return null;\n }\n\n getMediumProperties() {\n const boolAttr = (elem, attr) => {\n // As explained in MDL-64175, some OS (like Ubuntu), are removing the value for these attributes.\n // So in order to check if attr=\"true\", we need to check if the attribute exists and if the value is empty or true.\n return (elem.hasAttribute(attr) && (elem.getAttribute(attr) || elem.getAttribute(attr) === ''));\n };\n\n const tracks = {\n subtitles: [],\n captions: [],\n descriptions: [],\n chapters: [],\n metadata: []\n };\n const sources = [];\n\n const medium = this.getSelectedMedia();\n if (!medium) {\n return null;\n }\n medium.querySelectorAll('track').forEach((track) => {\n tracks[track.getAttribute('kind')].push({\n src: track.getAttribute('src'),\n srclang: track.getAttribute('srclang'),\n label: track.getAttribute('label'),\n defaultTrack: boolAttr(track, 'default')\n });\n });\n\n medium.querySelectorAll('source').forEach((source) => {\n sources.push(source.src);\n });\n\n return {\n type: medium.nodeName.toLowerCase() === 'video' ? Selectors.EMBED.mediaTypes.video : Selectors.EMBED.mediaTypes.audio,\n sources,\n poster: medium.getAttribute('poster'),\n title: medium.getAttribute('title'),\n width: medium.getAttribute('width'),\n height: medium.getAttribute('height'),\n autoplay: boolAttr(medium, 'autoplay'),\n loop: boolAttr(medium, 'loop'),\n muted: boolAttr(medium, 'muted'),\n controls: boolAttr(medium, 'controls'),\n tracks,\n };\n }\n\n prepareMoodleLang() {\n const moodleLangs = getMoodleLang(this.editor);\n const currentLanguage = getCurrentLanguage(this.editor);\n\n const installed = Object.entries(moodleLangs.installed).map(([lang, code]) => ({\n lang,\n code,\n \"default\": lang === currentLanguage,\n }));\n\n const available = Object.entries(moodleLangs.available).map(([lang, code]) => ({\n lang,\n code,\n \"default\": lang === currentLanguage,\n }));\n\n return {\n installed,\n available,\n };\n }\n\n getMoodleLangObj(subtitleLang) {\n const {available} = getMoodleLang(this.editor);\n\n if (available[subtitleLang]) {\n return {\n lang: subtitleLang,\n code: available[subtitleLang],\n };\n }\n\n return null;\n }\n\n filePickerCallback(params, element, fpType) {\n if (params.url !== '') {\n const tabPane = element.closest('.tab-pane');\n element.closest(Selectors.EMBED.elements.source).querySelector(Selectors.EMBED.elements.url).value = params.url;\n\n if (tabPane.id === this.editor.getElement().id + '_' + Selectors.EMBED.mediaTypes.link.toLowerCase()) {\n tabPane.querySelector(Selectors.EMBED.elements.name).value = params.file;\n }\n\n if (fpType === 'subtitle') {\n // If the file is subtitle file. We need to match the language and label for that file.\n const subtitleLang = params.file.split('.vtt')[0].split('-').slice(-1)[0];\n const langObj = this.getMoodleLangObj(subtitleLang);\n if (langObj) {\n const track = element.closest(Selectors.EMBED.elements.track);\n track.querySelector(Selectors.EMBED.elements.trackLabel).value = langObj.lang.trim();\n track.querySelector(Selectors.EMBED.elements.trackLang).value = langObj.code;\n }\n }\n }\n }\n\n addMediaSourceComponent(element, callback) {\n const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);\n const clone = sourceElement.cloneNode(true);\n\n sourceElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');\n sourceElement.querySelector('.addcomponent-wrapper').classList.add('hidden');\n\n sourceElement.parentNode.insertBefore(clone, sourceElement.nextSibling);\n\n if (callback) {\n callback(clone);\n }\n }\n\n removeMediaSourceComponent(element) {\n const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);\n sourceElement.remove();\n }\n\n addTrackComponent(element, callback) {\n const trackElement = element.closest(Selectors.EMBED.elements.track);\n const clone = trackElement.cloneNode(true);\n\n trackElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');\n trackElement.querySelector('.addcomponent-wrapper').classList.add('hidden');\n\n trackElement.parentNode.insertBefore(clone, trackElement.nextSibling);\n\n if (callback) {\n callback(clone);\n }\n }\n\n removeTrackComponent(element) {\n const sourceElement = element.closest(Selectors.EMBED.elements.track);\n sourceElement.remove();\n }\n\n getMediumTypeFromTabPane(tabPane) {\n return tabPane.getAttribute('data-medium-type');\n }\n\n getTrackTypeFromTabPane(tabPane) {\n return tabPane.getAttribute('data-track-kind');\n }\n\n getMediaHTML(form) {\n const mediumType = this.getMediumTypeFromTabPane(form.querySelector('.root.tab-content > .tab-pane.active'));\n const tabContent = form.querySelector(Selectors.EMBED.elements[mediumType.toLowerCase() + 'Pane']);\n\n return this['getMediaHTML' + mediumType[0].toUpperCase() + mediumType.substr(1)](tabContent);\n }\n\n getMediaHTMLLink(tab) {\n const context = {\n url: tab.querySelector(Selectors.EMBED.elements.url).value,\n name: tab.querySelector(Selectors.EMBED.elements.name).value || false\n };\n\n return context.url ? Templates.renderForPromise('tiny_media/embed_media_link', context) : '';\n }\n\n getMediaHTMLVideo(tab) {\n const context = this.getContextForMediaHTML(tab);\n context.width = tab.querySelector(Selectors.EMBED.elements.width).value || false;\n context.height = tab.querySelector(Selectors.EMBED.elements.height).value || false;\n context.poster = tab.querySelector(\n `${Selectors.EMBED.elements.posterSource} ${Selectors.EMBED.elements.url}`\n ).value || false;\n\n return context.sources.length ? Templates.renderForPromise('tiny_media/embed_media_video', context) : '';\n }\n\n getMediaHTMLAudio(tab) {\n const context = this.getContextForMediaHTML(tab);\n\n return context.sources.length ? Templates.renderForPromise('tiny_media/embed_media_audio', context) : '';\n }\n\n getContextForMediaHTML(tab) {\n const tracks = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.track)).map(track => ({\n track: track.querySelector(Selectors.EMBED.elements.trackSource + ' ' + Selectors.EMBED.elements.url).value,\n kind: this.getTrackTypeFromTabPane(track.closest('.tab-pane')),\n label: track.querySelector(Selectors.EMBED.elements.trackLabel).value ||\n track.querySelector(Selectors.EMBED.elements.trackLang).value,\n srclang: track.querySelector(Selectors.EMBED.elements.trackLang).value,\n defaultTrack: track.querySelector(Selectors.EMBED.elements.trackDefault).checked ? \"true\" : null\n })).filter((track) => !!track.track);\n\n const sources = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.mediaSource + ' '\n + Selectors.EMBED.elements.url))\n .filter((source) => !!source.value)\n .map((source) => source.value);\n\n return {\n sources,\n description: tab.querySelector(Selectors.EMBED.elements.mediaSource + ' '\n + Selectors.EMBED.elements.url).value || false,\n tracks,\n showControls: tab.querySelector(Selectors.EMBED.elements.mediaControl).checked,\n autoplay: tab.querySelector(Selectors.EMBED.elements.mediaAutoplay).checked,\n muted: tab.querySelector(Selectors.EMBED.elements.mediaMute).checked,\n loop: tab.querySelector(Selectors.EMBED.elements.mediaLoop).checked,\n title: tab.querySelector(Selectors.EMBED.elements.title).value || false\n };\n }\n\n getFilepickerTypeFromElement(element) {\n if (element.closest(Selectors.EMBED.elements.posterSource)) {\n return 'image';\n }\n if (element.closest(Selectors.EMBED.elements.trackSource)) {\n return 'subtitle';\n }\n\n return 'media';\n }\n\n async clickHandler(e) {\n const element = e.target;\n\n const mediaBrowser = element.closest(Selectors.EMBED.actions.mediaBrowser);\n if (mediaBrowser) {\n e.preventDefault();\n const fpType = this.getFilepickerTypeFromElement(element);\n const params = await displayFilepicker(this.editor, fpType);\n this.filePickerCallback(params, element, fpType);\n }\n\n const addComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .addcomponent');\n if (addComponentSourceAction) {\n e.preventDefault();\n this.addMediaSourceComponent(element);\n }\n\n const removeComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .removecomponent');\n if (removeComponentSourceAction) {\n e.preventDefault();\n this.removeMediaSourceComponent(element);\n }\n\n const addComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .addcomponent');\n if (addComponentTrackAction) {\n e.preventDefault();\n this.addTrackComponent(element);\n }\n\n const removeComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .removecomponent');\n if (removeComponentTrackAction) {\n e.preventDefault();\n this.removeTrackComponent(element);\n }\n\n // Only allow one track per tab to be selected as \"default\".\n const trackDefaultAction = element.closest(Selectors.EMBED.elements.trackDefault);\n if (trackDefaultAction && trackDefaultAction.checked) {\n const getKind = (el) => this.getTrackTypeFromTabPane(el.parentElement.closest('.tab-pane'));\n\n element.parentElement\n .closest('.root.tab-content')\n .querySelectorAll(Selectors.EMBED.elements.trackDefault)\n .forEach((select) => {\n if (select !== element && getKind(element) === getKind(select)) {\n select.checked = false;\n }\n });\n }\n }\n\n async handleDialogueSubmission(event, modal) {\n const {html} = await this.getMediaHTML(modal.getRoot()[0]);\n if (html) {\n if (this.isUpdating) {\n const selectedNode = this.getSelectedMedia();\n selectedNode.outerHTML = html;\n this.isUpdating = false;\n } else {\n this.editor.insertContent(html);\n }\n }\n }\n\n async registerEventListeners(modal) {\n await modal.getBody();\n const $root = modal.getRoot();\n const root = $root[0];\n if (this.canShowFilePicker) {\n root.addEventListener('click', this.clickHandler.bind(this));\n }\n\n $root.on(ModalEvents.save, this.handleDialogueSubmission.bind(this));\n $root.on(ModalEvents.hidden, () => {\n this.currentModal.destroy();\n });\n $root.on(ModalEvents.shown, () => {\n root.querySelectorAll(Selectors.EMBED.elements.trackLang).forEach((dropdown) => {\n const defaultVal = dropdown.getAttribute('data-value');\n if (defaultVal) {\n dropdown.value = defaultVal;\n }\n });\n });\n }\n};\n"],"names":["constructor","editor","permissions","canShowFilePicker","filepicker","this","helpStrings","addSource","tracks","subtitles","captions","descriptions","chapters","metadata","map","key","component","data","languages","prepareMoodleLang","helpIcons","Array","from","Object","entries","getHelpStrings","forEach","_ref","text","toLowerCase","assign","elementid","getElement","id","showfilepicker","langsinstalled","installed","langsavailable","available","link","video","audio","isupdating","isUpdating","getCurrentEmbedData","keys","length","modal","ModalFactory","create","type","Modal","TYPE","title","templateContext","getTemplateContext","removeOnClose","large","currentModal","registerEventListeners","show","properties","getMediumProperties","processedProperties","getSelectedMedia","mediaElm","selection","getNode","nodeName","querySelector","boolAttr","elem","attr","hasAttribute","getAttribute","sources","medium","querySelectorAll","track","push","src","srclang","label","defaultTrack","source","Selectors","EMBED","mediaTypes","poster","width","height","autoplay","loop","muted","controls","moodleLangs","currentLanguage","_ref2","lang","code","_ref3","getMoodleLangObj","subtitleLang","filePickerCallback","params","element","fpType","url","tabPane","closest","elements","value","name","file","split","slice","langObj","trackLabel","trim","trackLang","addMediaSourceComponent","callback","sourceElement","mediaSource","clone","cloneNode","classList","remove","add","parentNode","insertBefore","nextSibling","removeMediaSourceComponent","addTrackComponent","trackElement","removeTrackComponent","getMediumTypeFromTabPane","getTrackTypeFromTabPane","getMediaHTML","form","mediumType","tabContent","toUpperCase","substr","getMediaHTMLLink","tab","context","Templates","renderForPromise","getMediaHTMLVideo","getContextForMediaHTML","posterSource","getMediaHTMLAudio","trackSource","kind","trackDefault","checked","filter","description","showControls","mediaControl","mediaAutoplay","mediaMute","mediaLoop","getFilepickerTypeFromElement","e","target","actions","mediaBrowser","preventDefault","trackDefaultAction","getKind","el","parentElement","select","event","html","getRoot","outerHTML","insertContent","getBody","$root","root","addEventListener","clickHandler","bind","on","ModalEvents","save","handleDialogueSubmission","hidden","destroy","shown","dropdown","defaultVal"],"mappings":"m0DAqC0B,MActBA,YAAYC,sCAbH,gDACW,sCAKN,yCAKD,SAGHC,aAAc,iCAAoBD,aACnCE,kBAAoBD,YAAYE,gBAChCH,OAASA,kCAITI,KAAKC,YAAa,OACZC,UAAWC,OAAQC,UAAWC,SAAUC,aAAcC,SAAUC,gBAAkB,mBAAW,CAChG,iBACA,cACA,iBACA,gBACA,oBACA,gBACA,iBACFC,KAAKC,OACHA,IAAAA,IACAC,UAAAA,4BAGCV,YAAc,CAACC,UAAAA,UAAWC,OAAAA,OAAQC,UAAAA,UAAWC,SAAAA,SAAUC,aAAAA,aAAcC,SAAAA,SAAUC,SAAAA,iBAGjFR,KAAKC,qCAGSW,YACfC,UAAYb,KAAKc,oBAEjBC,UAAYC,MAAMC,KAAKC,OAAOC,cAAcnB,KAAKoB,mBAAmBC,SAAQC,WAAEZ,IAAKa,WACrFX,eAAQF,IAAIc,2BAA2B,CAACD,KAAAA,gBAGrCL,OAAOO,OAAO,GAAI,CACrBC,UAAW1B,KAAKJ,OAAO+B,aAAaC,GACpCC,eAAgB7B,KAAKF,kBACrBgC,eAAgBjB,UAAUkB,UAC1BC,eAAgBnB,UAAUoB,UAC1BC,MAAM,EACNC,OAAO,EACPC,OAAO,EACPC,WAAYrC,KAAKsC,YAClB1B,KAAMG,yCAIHH,KAAOM,OAAOO,OAAO,GAAIzB,KAAKuC,4BAC/BD,WAA0C,IAA7BpB,OAAOsB,KAAK5B,MAAM6B,aAE9BC,YAAcC,aAAaC,OAAO,CACpCC,KAAMC,oBAAMC,KACZC,OAAO,kBAAU,cAAe,cAChCC,sBAAuBjD,KAAKkD,mBAAmBtC,MAC/CuC,eAAe,EACfC,OAAO,SAGNC,aAAeX,YACd1C,KAAKsD,uBAAuBZ,OAClCA,MAAMa,OAGVhB,4BACUiB,WAAaxD,KAAKyD,0BACnBD,iBACM,SAGLE,oBAAsB,UAC5BA,oBAAoBF,WAAWX,KAAKrB,eAAiBgC,WACrDE,oBAAoBxB,MAAO,EAEpBwB,oBAGXC,yBACUC,SAAW5D,KAAKJ,OAAOiE,UAAUC,iBAElCF,SAImC,UAApCA,SAASG,SAASvC,eAAiE,UAApCoC,SAASG,SAASvC,cAC1DoC,SAGPA,SAASI,cAAc,SAChBJ,SAASI,cAAc,SAG9BJ,SAASI,cAAc,SAChBJ,SAASI,cAAc,SAG3B,KAfI,KAkBfP,4BACUQ,SAAW,CAACC,KAAMC,OAGZD,KAAKE,aAAaD,QAAUD,KAAKG,aAAaF,OAAqC,KAA5BD,KAAKG,aAAaF,OAG/EhE,OAAS,CACXC,UAAW,GACXC,SAAU,GACVC,aAAc,GACdC,SAAU,GACVC,SAAU,IAER8D,QAAU,GAEVC,OAASvE,KAAK2D,0BACfY,QAGLA,OAAOC,iBAAiB,SAASnD,SAASoD,QACtCtE,OAAOsE,MAAMJ,aAAa,SAASK,KAAK,CACpCC,IAAKF,MAAMJ,aAAa,OACxBO,QAASH,MAAMJ,aAAa,WAC5BQ,MAAOJ,MAAMJ,aAAa,SAC1BS,aAAcb,SAASQ,MAAO,gBAItCF,OAAOC,iBAAiB,UAAUnD,SAAS0D,SACvCT,QAAQI,KAAKK,OAAOJ,QAGjB,CACH9B,KAAwC,UAAlC0B,OAAOR,SAASvC,cAA4BwD,mBAAUC,MAAMC,WAAW/C,MAAQ6C,mBAAUC,MAAMC,WAAW9C,MAChHkC,QAAAA,QACAa,OAAQZ,OAAOF,aAAa,UAC5BrB,MAAOuB,OAAOF,aAAa,SAC3Be,MAAOb,OAAOF,aAAa,SAC3BgB,OAAQd,OAAOF,aAAa,UAC5BiB,SAAUrB,SAASM,OAAQ,YAC3BgB,KAAMtB,SAASM,OAAQ,QACvBiB,MAAOvB,SAASM,OAAQ,SACxBkB,SAAUxB,SAASM,OAAQ,YAC3BpE,OAAAA,SA1BO,KA8BfW,0BACU4E,aAAc,0BAAc1F,KAAKJ,QACjC+F,iBAAkB,+BAAmB3F,KAAKJ,cAczC,CACHmC,UAbcb,OAAOC,QAAQuE,YAAY3D,WAAWtB,KAAImF,YAAEC,KAAMC,kBAAW,CAC3ED,KAAAA,KACAC,KAAAA,aACWD,OAASF,oBAWpB1D,UARcf,OAAOC,QAAQuE,YAAYzD,WAAWxB,KAAIsF,YAAEF,KAAMC,kBAAW,CAC3ED,KAAAA,KACAC,KAAAA,aACWD,OAASF,qBAS5BK,iBAAiBC,oBACPhE,UAACA,YAAa,0BAAcjC,KAAKJ,eAEnCqC,UAAUgE,cACH,CACHJ,KAAMI,aACNH,KAAM7D,UAAUgE,eAIjB,KAGXC,mBAAmBC,OAAQC,QAASC,WACb,KAAfF,OAAOG,IAAY,OACbC,QAAUH,QAAQI,QAAQ,gBAChCJ,QAAQI,QAAQxB,mBAAUC,MAAMwB,SAAS1B,QAAQf,cAAcgB,mBAAUC,MAAMwB,SAASH,KAAKI,MAAQP,OAAOG,IAExGC,QAAQ3E,KAAO5B,KAAKJ,OAAO+B,aAAaC,GAAK,IAAMoD,mBAAUC,MAAMC,WAAWhD,KAAKV,gBACnF+E,QAAQvC,cAAcgB,mBAAUC,MAAMwB,SAASE,MAAMD,MAAQP,OAAOS,MAGzD,aAAXP,OAAuB,OAEjBJ,aAAeE,OAAOS,KAAKC,MAAM,QAAQ,GAAGA,MAAM,KAAKC,OAAO,GAAG,GACjEC,QAAU/G,KAAKgG,iBAAiBC,iBAClCc,QAAS,OACHtC,MAAQ2B,QAAQI,QAAQxB,mBAAUC,MAAMwB,SAAShC,OACvDA,MAAMT,cAAcgB,mBAAUC,MAAMwB,SAASO,YAAYN,MAAQK,QAAQlB,KAAKoB,OAC9ExC,MAAMT,cAAcgB,mBAAUC,MAAMwB,SAASS,WAAWR,MAAQK,QAAQjB,QAMxFqB,wBAAwBf,QAASgB,gBACvBC,cAAgBjB,QAAQI,QAAQxB,mBAAUC,MAAMwB,SAAS1B,OAASC,mBAAUC,MAAMwB,SAASa,aAC3FC,MAAQF,cAAcG,WAAU,GAEtCH,cAAcrD,cAAc,4BAA4ByD,UAAUC,OAAO,UACzEL,cAAcrD,cAAc,yBAAyByD,UAAUE,IAAI,UAEnEN,cAAcO,WAAWC,aAAaN,MAAOF,cAAcS,aAEvDV,UACAA,SAASG,OAIjBQ,2BAA2B3B,SACDA,QAAQI,QAAQxB,mBAAUC,MAAMwB,SAAS1B,OAASC,mBAAUC,MAAMwB,SAASa,aACnFI,SAGlBM,kBAAkB5B,QAASgB,gBACjBa,aAAe7B,QAAQI,QAAQxB,mBAAUC,MAAMwB,SAAShC,OACxD8C,MAAQU,aAAaT,WAAU,GAErCS,aAAajE,cAAc,4BAA4ByD,UAAUC,OAAO,UACxEO,aAAajE,cAAc,yBAAyByD,UAAUE,IAAI,UAElEM,aAAaL,WAAWC,aAAaN,MAAOU,aAAaH,aAErDV,UACAA,SAASG,OAIjBW,qBAAqB9B,SACKA,QAAQI,QAAQxB,mBAAUC,MAAMwB,SAAShC,OACjDiD,SAGlBS,yBAAyB5B,gBACdA,QAAQlC,aAAa,oBAGhC+D,wBAAwB7B,gBACbA,QAAQlC,aAAa,mBAGhCgE,aAAaC,YACHC,WAAavI,KAAKmI,yBAAyBG,KAAKtE,cAAc,yCAC9DwE,WAAaF,KAAKtE,cAAcgB,mBAAUC,MAAMwB,SAAS8B,WAAW/G,cAAgB,gBAEnFxB,KAAK,eAAiBuI,WAAW,GAAGE,cAAgBF,WAAWG,OAAO,IAAIF,YAGrFG,iBAAiBC,WACPC,QAAU,CACZvC,IAAKsC,IAAI5E,cAAcgB,mBAAUC,MAAMwB,SAASH,KAAKI,MACrDC,KAAMiC,IAAI5E,cAAcgB,mBAAUC,MAAMwB,SAASE,MAAMD,QAAS,UAG7DmC,QAAQvC,IAAMwC,mBAAUC,iBAAiB,8BAA+BF,SAAW,GAG9FG,kBAAkBJ,WACRC,QAAU7I,KAAKiJ,uBAAuBL,YAC5CC,QAAQzD,MAAQwD,IAAI5E,cAAcgB,mBAAUC,MAAMwB,SAASrB,OAAOsB,QAAS,EAC3EmC,QAAQxD,OAASuD,IAAI5E,cAAcgB,mBAAUC,MAAMwB,SAASpB,QAAQqB,QAAS,EAC7EmC,QAAQ1D,OAASyD,IAAI5E,wBACdgB,mBAAUC,MAAMwB,SAASyC,yBAAgBlE,mBAAUC,MAAMwB,SAASH,MACvEI,QAAS,EAEJmC,QAAQvE,QAAQ7B,OAASqG,mBAAUC,iBAAiB,+BAAgCF,SAAW,GAG1GM,kBAAkBP,WACRC,QAAU7I,KAAKiJ,uBAAuBL,YAErCC,QAAQvE,QAAQ7B,OAASqG,mBAAUC,iBAAiB,+BAAgCF,SAAW,GAG1GI,uBAAuBL,WACbzI,OAASa,MAAMC,KAAK2H,IAAIpE,iBAAiBQ,mBAAUC,MAAMwB,SAAShC,QAAQhE,KAAIgE,SAChFA,MAAOA,MAAMT,cAAcgB,mBAAUC,MAAMwB,SAAS2C,YAAc,IAAMpE,mBAAUC,MAAMwB,SAASH,KAAKI,MACtG2C,KAAMrJ,KAAKoI,wBAAwB3D,MAAM+B,QAAQ,cACjD3B,MAAOJ,MAAMT,cAAcgB,mBAAUC,MAAMwB,SAASO,YAAYN,OAC5DjC,MAAMT,cAAcgB,mBAAUC,MAAMwB,SAASS,WAAWR,MAC5D9B,QAASH,MAAMT,cAAcgB,mBAAUC,MAAMwB,SAASS,WAAWR,MACjE5B,aAAcL,MAAMT,cAAcgB,mBAAUC,MAAMwB,SAAS6C,cAAcC,QAAU,OAAS,SAC5FC,QAAQ/E,SAAYA,MAAMA,cAOvB,CACHH,QANYtD,MAAMC,KAAK2H,IAAIpE,iBAAiBQ,mBAAUC,MAAMwB,SAASa,YAAc,IACjFtC,mBAAUC,MAAMwB,SAASH,MACtBkD,QAAQzE,UAAaA,OAAO2B,QAC5BjG,KAAKsE,QAAWA,OAAO2B,QAI5B+C,YAAab,IAAI5E,cAAcgB,mBAAUC,MAAMwB,SAASa,YAAc,IAChEtC,mBAAUC,MAAMwB,SAASH,KAAKI,QAAS,EAC7CvG,OAAAA,OACAuJ,aAAcd,IAAI5E,cAAcgB,mBAAUC,MAAMwB,SAASkD,cAAcJ,QACvEjE,SAAUsD,IAAI5E,cAAcgB,mBAAUC,MAAMwB,SAASmD,eAAeL,QACpE/D,MAAOoD,IAAI5E,cAAcgB,mBAAUC,MAAMwB,SAASoD,WAAWN,QAC7DhE,KAAMqD,IAAI5E,cAAcgB,mBAAUC,MAAMwB,SAASqD,WAAWP,QAC5DvG,MAAO4F,IAAI5E,cAAcgB,mBAAUC,MAAMwB,SAASzD,OAAO0D,QAAS,GAI1EqD,6BAA6B3D,gBACrBA,QAAQI,QAAQxB,mBAAUC,MAAMwB,SAASyC,cAClC,QAEP9C,QAAQI,QAAQxB,mBAAUC,MAAMwB,SAAS2C,aAClC,WAGJ,2BAGQY,SACT5D,QAAU4D,EAAEC,UAEG7D,QAAQI,QAAQxB,mBAAUC,MAAMiF,QAAQC,cAC3C,CACdH,EAAEI,uBACI/D,OAASrG,KAAK+J,6BAA6B3D,SAC3CD,aAAe,4BAAkBnG,KAAKJ,OAAQyG,aAC/CH,mBAAmBC,OAAQC,QAASC,QAGZD,QAAQI,QAAQxB,mBAAUC,MAAMwB,SAASa,YAAc,oBAEpF0C,EAAEI,sBACGjD,wBAAwBf,UAGGA,QAAQI,QAAQxB,mBAAUC,MAAMwB,SAASa,YAAc,uBAEvF0C,EAAEI,sBACGrC,2BAA2B3B,UAGJA,QAAQI,QAAQxB,mBAAUC,MAAMwB,SAAShC,MAAQ,oBAE7EuF,EAAEI,sBACGpC,kBAAkB5B,UAGQA,QAAQI,QAAQxB,mBAAUC,MAAMwB,SAAShC,MAAQ,uBAEhFuF,EAAEI,sBACGlC,qBAAqB9B,gBAIxBiE,mBAAqBjE,QAAQI,QAAQxB,mBAAUC,MAAMwB,SAAS6C,iBAChEe,oBAAsBA,mBAAmBd,QAAS,OAC5Ce,QAAWC,IAAOvK,KAAKoI,wBAAwBmC,GAAGC,cAAchE,QAAQ,cAE9EJ,QAAQoE,cACHhE,QAAQ,qBACRhC,iBAAiBQ,mBAAUC,MAAMwB,SAAS6C,cAC1CjI,SAASoJ,SACFA,SAAWrE,SAAWkE,QAAQlE,WAAakE,QAAQG,UACnDA,OAAOlB,SAAU,sCAMNmB,MAAOhI,aAC5BiI,KAACA,YAAc3K,KAAKqI,aAAa3F,MAAMkI,UAAU,OACnDD,QACI3K,KAAKsC,WAAY,CACItC,KAAK2D,mBACbkH,UAAYF,UACpBrI,YAAa,YAEb1C,OAAOkL,cAAcH,mCAKTjI,aACnBA,MAAMqI,gBACNC,MAAQtI,MAAMkI,UACdK,KAAOD,MAAM,GACfhL,KAAKF,mBACLmL,KAAKC,iBAAiB,QAASlL,KAAKmL,aAAaC,KAAKpL,OAG1DgL,MAAMK,GAAGC,YAAYC,KAAMvL,KAAKwL,yBAAyBJ,KAAKpL,OAC9DgL,MAAMK,GAAGC,YAAYG,QAAQ,UACpBpI,aAAaqI,aAEtBV,MAAMK,GAAGC,YAAYK,OAAO,KACxBV,KAAKzG,iBAAiBQ,mBAAUC,MAAMwB,SAASS,WAAW7F,SAASuK,iBACzDC,WAAaD,SAASvH,aAAa,cACrCwH,aACAD,SAASlF,MAAQmF"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/image.min.js b/lib/editor/tiny/plugins/media/amd/build/image.min.js index 0569571f5b3..9c23f9fe006 100644 --- a/lib/editor/tiny/plugins/media/amd/build/image.min.js +++ b/lib/editor/tiny/plugins/media/amd/build/image.min.js @@ -1,3 +1,3 @@ -define("tiny_media/image",["exports","core/templates","core/str","core/modal_factory","core/pending","editor_tiny/utils","./selectors","./imagemodal","./options","./common"],(function(_exports,_templates,_str,ModalFactory,_pending,_utils,_selectors,_imagemodal,_options,_common){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.MediaImage=void 0,_templates=_interopRequireDefault(_templates),ModalFactory=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalFactory),_pending=_interopRequireDefault(_pending),_selectors=_interopRequireDefault(_selectors),_imagemodal=_interopRequireDefault(_imagemodal);_exports.MediaImage=class{constructor(editor){_defineProperty(this,"DEFAULTS",{WIDTH:160,HEIGHT:160}),_defineProperty(this,"form",null),_defineProperty(this,"rawImageDimensions",null),_defineProperty(this,"canShowFilePicker",!1),_defineProperty(this,"editor",null),_defineProperty(this,"currentModal",null),_defineProperty(this,"selectedImage",null),_defineProperty(this,"imageAlignment",null);const permissions=(0,_options.getImagePermissions)(editor);this.canShowFilePicker=permissions.filepicker,this.editor=editor}async displayDialogue(){this.rawImageDimensions=null;const currentImageData=await this.getCurrentImageData(),modal=await ModalFactory.create({type:_imagemodal.default.TYPE,title:(0,_str.get_string)("imageproperties","tiny_media"),templateContext:await this.getTemplateContext(currentImageData),removeOnClose:!0,large:!0});this.currentModal=modal,currentImageData&¤tImageData.src&&this.loadPreviewImage(currentImageData.src),await this.registerEventListeners(modal),modal.show()}async getAlignmentTitles(){if(!this.alignmentTitles){const[top,middle,bottom]=await(0,_str.get_strings)(["alignment_top","alignment_middle","alignment_bottom"].map((key=>({key:key,component:_common.component}))));this.alignmentTitles={top:top,middle:middle,bottom:bottom}}return this.alignmentTitles}async getImageAlignment(){let selected=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";const titles=await this.getAlignmentTitles(),alignments=[{text:titles.top,value:"align-top"},{text:titles.middle,value:"align-middle"},{text:titles.bottom,value:"align-bottom"}];return selected&&alignments.forEach(((alignment,index,array)=>{alignment.value===selected&&(array[index].selected=!0)})),alignments}async getTemplateContext(data){return{elementid:this.editor.id,showfilepicker:this.canShowFilePicker,alignoptions:await this.getImageAlignment(),...data}}async getCurrentImageData(){const selectedImageProperties=this.getSelectedImageProperties();if(!selectedImageProperties)return{};const properties={...selectedImageProperties};return properties.align&&(properties.alignoptions=await this.getImageAlignment(properties.align)),properties.src&&(properties.haspreview=!0),properties.alt||(properties.presentation=!0),properties}filePickerCallback(params,self){if(params.url){self.form.querySelector(_selectors.default.IMAGE.elements.url).value=params.url,self.form.querySelector(_selectors.default.IMAGE.elements.width).value="",self.form.querySelector(_selectors.default.IMAGE.elements.height).value="",self.loadPreviewImage(params.url)}}storeImageDimensions(image){this.rawImageDimensions={width:image.width||this.DEFAULTS.WIDTH,height:image.height||this.DEFAULTS.HEIGHT};const currentWidth=(element=>(""===element.value&&(element.value=this.rawImageDimensions.width),element.value))(this.form.querySelector(_selectors.default.IMAGE.elements.width)),currentHeight=(element=>(""===element.value&&(element.value=this.rawImageDimensions.height),element.value))(this.form.querySelector(_selectors.default.IMAGE.elements.height)),preview=this.form.querySelector(_selectors.default.IMAGE.elements.preview);preview.setAttribute("src",image.src),preview.style.display="inline";const constrain=this.form.querySelector(_selectors.default.IMAGE.elements.constrain);if(this.isPercentageValue(currentWidth)&&this.isPercentageValue(currentHeight))constrain.checked=currentWidth===currentHeight;else if(0===image.width||0===image.height)constrain.disabled="disabled";else{const widthRatio=Math.round(1e3*parseInt(currentWidth,10)/image.width),heightRatio=Math.round(1e3*parseInt(currentHeight,10)/image.height);constrain.checked=widthRatio===heightRatio}}loadPreviewImage(url){const image=new Image;image.addEventListener("error",(()=>{this.form.querySelector(_selectors.default.IMAGE.elements.preview).style.display="none"})),image.addEventListener("load",(()=>{this.storeImageDimensions(image),this.autoAdjustSize()})),image.src=url}urlChanged(){const input=this.form.querySelector(_selectors.default.IMAGE.elements.url);input.value&&this.loadPreviewImage(input.value)}hasErrorUrlField(){const urlError=""===this.form.querySelector(_selectors.default.IMAGE.elements.url).value;return this.toggleVisibility(_selectors.default.IMAGE.elements.urlWarning,urlError),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.url],urlError),urlError}hasErrorAltField(){const alt=this.form.querySelector(_selectors.default.IMAGE.elements.alt).value,presentation=this.form.querySelector(_selectors.default.IMAGE.elements.presentation).checked,imageAltError=""===alt&&!presentation;return this.toggleVisibility(_selectors.default.IMAGE.elements.altWarning,imageAltError),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.alt,_selectors.default.IMAGE.elements.presentation],imageAltError),imageAltError}toggleVisibility(selector,predicate){this.form.querySelectorAll(selector).forEach((element=>{element.style.display=predicate?"block":"none"}))}toggleAriaInvalid(selectors,predicate){selectors.forEach((selector=>{this.form.querySelectorAll(selector).forEach((element=>element.setAttribute("aria-invalid",predicate)))}))}getAlignmentClass(alignment){return alignment}updateWarning(){const urlError=this.hasErrorUrlField(),imageAltError=this.hasErrorAltField();return urlError||imageAltError}getImageContext(){if(this.updateWarning())return null;const classList=[];this.form.querySelector(_selectors.default.IMAGE.elements.constrain).value&&classList.push(_selectors.default.IMAGE.styles.responsive);const alignment=this.getAlignmentClass(this.form.querySelector(_selectors.default.IMAGE.elements.alignment).value);return classList.push(alignment),{url:this.form.querySelector(_selectors.default.IMAGE.elements.url).value,alt:this.form.querySelector(_selectors.default.IMAGE.elements.alt).value,width:this.form.querySelector(_selectors.default.IMAGE.elements.width).value,height:this.form.querySelector(_selectors.default.IMAGE.elements.height).value,presentation:this.form.querySelector(_selectors.default.IMAGE.elements.presentation).checked,customStyle:this.form.querySelector(_selectors.default.IMAGE.elements.customStyle).value,classlist:classList.join(" ")}}setImage(){const pendingPromise=new _pending.default("tiny_media:setImage");if(""===this.form.querySelector(_selectors.default.IMAGE.elements.url).value)return;if(this.updateWarning())return void pendingPromise.resolve();const width=this.form.querySelector(_selectors.default.IMAGE.elements.width).value;if(!this.isPercentageValue(width)&&isNaN(parseInt(width,10)))return this.form.querySelector(_selectors.default.IMAGE.elements.width).focus(),void pendingPromise.resolve();const height=this.form.querySelector(_selectors.default.IMAGE.elements.height).value;if(!this.isPercentageValue(height)&&isNaN(parseInt(height,10)))return this.form.querySelector(_selectors.default.IMAGE.elements.height).focus(),void pendingPromise.resolve();_templates.default.render("tiny_media/image",this.getImageContext()).then((html=>(this.editor.insertContent(html),this.currentModal.destroy(),pendingPromise.resolve(),html))).catch()}handleKeyupCharacterCount(){const alt=this.form.querySelector(_selectors.default.IMAGE.elements.alt).value;this.form.querySelector("#currentcount").innerHTML=alt.length}autoAdjustSize(){let forceHeight=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!this.rawImageDimensions)return;const widthField=this.form.querySelector(_selectors.default.IMAGE.elements.width),heightField=this.form.querySelector(_selectors.default.IMAGE.elements.height),normalizeFieldData=fieldData=>(fieldData.isPercentageValue=!!this.isPercentageValue(fieldData.field.value),fieldData.isPercentageValue?(fieldData.percentValue=parseInt(fieldData.field.value,10),fieldData.pixelSize=this.rawImageDimensions[fieldData.type]/100*fieldData.percentValue):(fieldData.pixelSize=parseInt(fieldData.field.value,10),fieldData.percentValue=fieldData.pixelSize/this.rawImageDimensions[fieldData.type]*100),fieldData),imagePreview=this.form.querySelector(_selectors.default.IMAGE.elements.preview);imagePreview.style.width="",imagePreview.style.height="";const constrainField=this.form.querySelector(_selectors.default.IMAGE.elements.constrain),keyField=(()=>{const currentValue=forceHeight?{field:heightField,type:"height"}:{field:widthField,type:"width"};return""===currentValue.field.value&&(currentValue.field.value=this.rawImageDimensions[currentValue.type]),normalizeFieldData(currentValue)})(),relativeField=normalizeFieldData(forceHeight?{field:widthField,type:"width"}:{field:heightField,type:"height"});constrainField.checked&&(keyField.isPercentageValue?(relativeField.field.value=keyField.field.value,relativeField.percentValue=keyField.percentValue):(relativeField.pixelSize=Math.round(keyField.pixelSize/this.rawImageDimensions[keyField.type]*this.rawImageDimensions[relativeField.type]),relativeField.field.value=relativeField.pixelSize)),function(image,keyField,relativeField){let forceHeight=arguments.length>3&&void 0!==arguments[3]&&arguments[3];const getStyleValue=field=>field.isPercentageValue?"".concat(field.percentValue,"%"):"".concat(field.pixelSize,"px");forceHeight?"width"===keyField.type?image.style.width=getStyleValue(keyField):image.style.width=getStyleValue(relativeField):(image.style[keyField.type]=getStyleValue(keyField),image.style[relativeField.type]=getStyleValue(relativeField))}(imagePreview,keyField,relativeField,!!constrainField.checked)}getSelectedImageProperties(){const image=this.getSelectedImage();if(!image)return this.selectedImage=null,null;const properties={src:null,alt:null,width:null,height:null,align:"",presentation:!1};this.removeLegacyAlignment(image),this.selectedImage=image,properties.customStyle=image.style.cssText;const width=(image=>this.isPercentageValue(String(image.width))?image.width:parseInt(image.width,10))(image);0!==width&&(properties.width=width);const height=(image=>this.isPercentageValue(String(image.height))?image.height:parseInt(image.height,10))(image);0!==height&&(properties.height=height);const alignment=this.getAlignmentProperties(image,properties);return alignment&&(properties.align=alignment.value),properties.src=image.getAttribute("src"),properties.alt=image.getAttribute("alt")||"",properties.presentation="presentation"===image.getAttribute("role"),properties}removeLegacyAlignment(imageNode){return imageNode.style.margin?(_selectors.default.IMAGE.alignments.some((alignment=>{if(imageNode.style[alignment.name]!==alignment.value)return!1;const normalisedNode=document.createElement("div");return normalisedNode.style.margin=alignment.margin,imageNode.style.margin===normalisedNode.style.margin&&(imageNode.classList.add(this.getAlignmentClass(alignment.value)),imageNode.style[alignment.name]=null,imageNode.style.margin=null,!0)})),imageNode):imageNode}getAlignmentProperties(image){const currentAlignment=_selectors.default.IMAGE.alignments.find((alignment=>!!image.classList.contains(this.getAlignmentClass(alignment.value))||!!alignment.legacyValues&&alignment.legacyValues.some((legacyValue=>image.classList.contains(legacyValue)))));return currentAlignment||_selectors.default.IMAGE.alignments.find((alignment=>alignment.isDefault))}getSelectedImage(){const imgElm=this.editor.selection.getNode(),figureElm=this.editor.dom.getParent(imgElm,"figure.image");return figureElm?this.editor.dom.select("img",figureElm)[0]:imgElm&&("IMG"!==imgElm.nodeName.toUpperCase()||this.isPlaceholderImage(imgElm))?null:imgElm}isPlaceholderImage(imgElm){return"IMG"===imgElm.nodeName.toUpperCase()&&(imgElm.hasAttribute("data-mce-object")||imgElm.hasAttribute("data-mce-placeholder"))}isPercentageValue(value){return value.match(/\d+%/)}async registerEventListeners(modal){await modal.getBody();const root=modal.getRoot()[0];this.form=root.querySelector(_selectors.default.IMAGE.elements.form),root.addEventListener("click",(e=>{const submitAction=e.target.closest(_selectors.default.IMAGE.actions.submit),imageBrowserAction=e.target.closest(_selectors.default.IMAGE.actions.imageBrowser);submitAction&&(e.preventDefault(),this.setImage()),imageBrowserAction&&this.canShowFilePicker&&(e.preventDefault(),(0,_utils.displayFilepicker)(this.editor,"image").then((params=>{this.filePickerCallback(params,this)})).catch())})),root.addEventListener("change",(e=>{e.target.closest(_selectors.default.IMAGE.elements.url)&&this.hasErrorUrlField();e.target.closest(_selectors.default.IMAGE.elements.presentation)&&this.hasErrorAltField();e.target.closest(_selectors.default.IMAGE.elements.constrain)&&this.autoAdjustSize(!0)})),root.addEventListener("blur",(e=>{if(e.target.nodeType===Node.ELEMENT_NODE){e.target.closest(_selectors.default.IMAGE.elements.url)&&this.urlChanged();e.target.closest(_selectors.default.IMAGE.elements.alt)&&this.hasErrorAltField();e.target.closest(_selectors.default.IMAGE.elements.width)&&this.autoAdjustSize();e.target.closest(_selectors.default.IMAGE.elements.height)&&this.autoAdjustSize(!0)}}),!0),root.addEventListener("keyup",(e=>{e.target.closest(_selectors.default.IMAGE.elements.alt)&&this.handleKeyupCharacterCount()}))}}})); +define("tiny_media/image",["exports","core/templates","core/str","core/modal_factory","core/pending","editor_tiny/utils","./selectors","./imagemodal","./options","./common"],(function(_exports,_templates,_str,ModalFactory,_pending,_utils,_selectors,_imagemodal,_options,_common){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.MediaImage=void 0,_templates=_interopRequireDefault(_templates),ModalFactory=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalFactory),_pending=_interopRequireDefault(_pending),_selectors=_interopRequireDefault(_selectors),_imagemodal=_interopRequireDefault(_imagemodal);_exports.MediaImage=class{constructor(editor){_defineProperty(this,"DEFAULTS",{WIDTH:160,HEIGHT:160}),_defineProperty(this,"form",null),_defineProperty(this,"rawImageDimensions",null),_defineProperty(this,"canShowFilePicker",!1),_defineProperty(this,"editor",null),_defineProperty(this,"currentModal",null),_defineProperty(this,"selectedImage",null),_defineProperty(this,"imageAlignment",null);const permissions=(0,_options.getImagePermissions)(editor);this.canShowFilePicker=permissions.filepicker,this.editor=editor}async displayDialogue(){this.rawImageDimensions=null;const currentImageData=await this.getCurrentImageData(),modal=await ModalFactory.create({type:_imagemodal.default.TYPE,title:(0,_str.getString)("imageproperties","tiny_media"),templateContext:await this.getTemplateContext(currentImageData),removeOnClose:!0,large:!0});this.currentModal=modal,currentImageData&¤tImageData.src&&this.loadPreviewImage(currentImageData.src),await this.registerEventListeners(modal),modal.show()}async getAlignmentTitles(){if(!this.alignmentTitles){const[top,middle,bottom]=await(0,_str.getStrings)(["alignment_top","alignment_middle","alignment_bottom"].map((key=>({key:key,component:_common.component}))));this.alignmentTitles={top:top,middle:middle,bottom:bottom}}return this.alignmentTitles}async getImageAlignment(){let selected=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";const titles=await this.getAlignmentTitles(),alignments=[{text:titles.top,value:"align-top"},{text:titles.middle,value:"align-middle"},{text:titles.bottom,value:"align-bottom"}];return selected&&alignments.forEach(((alignment,index,array)=>{alignment.value===selected&&(array[index].selected=!0)})),alignments}async getTemplateContext(data){return{elementid:this.editor.id,showfilepicker:this.canShowFilePicker,alignoptions:await this.getImageAlignment(),...data}}async getCurrentImageData(){const selectedImageProperties=this.getSelectedImageProperties();if(!selectedImageProperties)return{};const properties={...selectedImageProperties};return properties.align&&(properties.alignoptions=await this.getImageAlignment(properties.align)),properties.src&&(properties.haspreview=!0),properties.alt||(properties.presentation=!0),properties}filePickerCallback(params,self){if(params.url){self.form.querySelector(_selectors.default.IMAGE.elements.url).value=params.url,self.form.querySelector(_selectors.default.IMAGE.elements.width).value="",self.form.querySelector(_selectors.default.IMAGE.elements.height).value="",self.loadPreviewImage(params.url)}}storeImageDimensions(image){this.rawImageDimensions={width:image.width||this.DEFAULTS.WIDTH,height:image.height||this.DEFAULTS.HEIGHT};const currentWidth=(element=>(""===element.value&&(element.value=this.rawImageDimensions.width),element.value))(this.form.querySelector(_selectors.default.IMAGE.elements.width)),currentHeight=(element=>(""===element.value&&(element.value=this.rawImageDimensions.height),element.value))(this.form.querySelector(_selectors.default.IMAGE.elements.height)),preview=this.form.querySelector(_selectors.default.IMAGE.elements.preview);preview.setAttribute("src",image.src),preview.style.display="inline";const constrain=this.form.querySelector(_selectors.default.IMAGE.elements.constrain);if(this.isPercentageValue(currentWidth)&&this.isPercentageValue(currentHeight))constrain.checked=currentWidth===currentHeight;else if(0===image.width||0===image.height)constrain.disabled="disabled";else{const widthRatio=Math.round(1e3*parseInt(currentWidth,10)/image.width),heightRatio=Math.round(1e3*parseInt(currentHeight,10)/image.height);constrain.checked=widthRatio===heightRatio}}loadPreviewImage(url){const image=new Image;image.addEventListener("error",(()=>{this.form.querySelector(_selectors.default.IMAGE.elements.preview).style.display="none"})),image.addEventListener("load",(()=>{this.storeImageDimensions(image),this.autoAdjustSize()})),image.src=url}urlChanged(){const input=this.form.querySelector(_selectors.default.IMAGE.elements.url);input.value&&this.loadPreviewImage(input.value)}hasErrorUrlField(){const urlError=""===this.form.querySelector(_selectors.default.IMAGE.elements.url).value;return this.toggleVisibility(_selectors.default.IMAGE.elements.urlWarning,urlError),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.url],urlError),urlError}hasErrorAltField(){const alt=this.form.querySelector(_selectors.default.IMAGE.elements.alt).value,presentation=this.form.querySelector(_selectors.default.IMAGE.elements.presentation).checked,imageAltError=""===alt&&!presentation;return this.toggleVisibility(_selectors.default.IMAGE.elements.altWarning,imageAltError),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.alt,_selectors.default.IMAGE.elements.presentation],imageAltError),imageAltError}toggleVisibility(selector,predicate){this.form.querySelectorAll(selector).forEach((element=>{element.style.display=predicate?"block":"none"}))}toggleAriaInvalid(selectors,predicate){selectors.forEach((selector=>{this.form.querySelectorAll(selector).forEach((element=>element.setAttribute("aria-invalid",predicate)))}))}getAlignmentClass(alignment){return alignment}updateWarning(){const urlError=this.hasErrorUrlField(),imageAltError=this.hasErrorAltField();return urlError||imageAltError}getImageContext(){if(this.updateWarning())return null;const classList=[];this.form.querySelector(_selectors.default.IMAGE.elements.constrain).value&&classList.push(_selectors.default.IMAGE.styles.responsive);const alignment=this.getAlignmentClass(this.form.querySelector(_selectors.default.IMAGE.elements.alignment).value);return classList.push(alignment),{url:this.form.querySelector(_selectors.default.IMAGE.elements.url).value,alt:this.form.querySelector(_selectors.default.IMAGE.elements.alt).value,width:this.form.querySelector(_selectors.default.IMAGE.elements.width).value,height:this.form.querySelector(_selectors.default.IMAGE.elements.height).value,presentation:this.form.querySelector(_selectors.default.IMAGE.elements.presentation).checked,customStyle:this.form.querySelector(_selectors.default.IMAGE.elements.customStyle).value,classlist:classList.join(" ")}}setImage(){const pendingPromise=new _pending.default("tiny_media:setImage");if(""===this.form.querySelector(_selectors.default.IMAGE.elements.url).value)return;if(this.updateWarning())return void pendingPromise.resolve();const width=this.form.querySelector(_selectors.default.IMAGE.elements.width).value;if(!this.isPercentageValue(width)&&isNaN(parseInt(width,10)))return this.form.querySelector(_selectors.default.IMAGE.elements.width).focus(),void pendingPromise.resolve();const height=this.form.querySelector(_selectors.default.IMAGE.elements.height).value;if(!this.isPercentageValue(height)&&isNaN(parseInt(height,10)))return this.form.querySelector(_selectors.default.IMAGE.elements.height).focus(),void pendingPromise.resolve();_templates.default.render("tiny_media/image",this.getImageContext()).then((html=>(this.editor.insertContent(html),this.currentModal.destroy(),pendingPromise.resolve(),html))).catch()}handleKeyupCharacterCount(){const alt=this.form.querySelector(_selectors.default.IMAGE.elements.alt).value;this.form.querySelector("#currentcount").innerHTML=alt.length}autoAdjustSize(){let forceHeight=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!this.rawImageDimensions)return;const widthField=this.form.querySelector(_selectors.default.IMAGE.elements.width),heightField=this.form.querySelector(_selectors.default.IMAGE.elements.height),normalizeFieldData=fieldData=>(fieldData.isPercentageValue=!!this.isPercentageValue(fieldData.field.value),fieldData.isPercentageValue?(fieldData.percentValue=parseInt(fieldData.field.value,10),fieldData.pixelSize=this.rawImageDimensions[fieldData.type]/100*fieldData.percentValue):(fieldData.pixelSize=parseInt(fieldData.field.value,10),fieldData.percentValue=fieldData.pixelSize/this.rawImageDimensions[fieldData.type]*100),fieldData),imagePreview=this.form.querySelector(_selectors.default.IMAGE.elements.preview);imagePreview.style.width="",imagePreview.style.height="";const constrainField=this.form.querySelector(_selectors.default.IMAGE.elements.constrain),keyField=(()=>{const currentValue=forceHeight?{field:heightField,type:"height"}:{field:widthField,type:"width"};return""===currentValue.field.value&&(currentValue.field.value=this.rawImageDimensions[currentValue.type]),normalizeFieldData(currentValue)})(),relativeField=normalizeFieldData(forceHeight?{field:widthField,type:"width"}:{field:heightField,type:"height"});constrainField.checked&&(keyField.isPercentageValue?(relativeField.field.value=keyField.field.value,relativeField.percentValue=keyField.percentValue):(relativeField.pixelSize=Math.round(keyField.pixelSize/this.rawImageDimensions[keyField.type]*this.rawImageDimensions[relativeField.type]),relativeField.field.value=relativeField.pixelSize)),function(image,keyField,relativeField){let forceHeight=arguments.length>3&&void 0!==arguments[3]&&arguments[3];const getStyleValue=field=>field.isPercentageValue?"".concat(field.percentValue,"%"):"".concat(field.pixelSize,"px");forceHeight?"width"===keyField.type?image.style.width=getStyleValue(keyField):image.style.width=getStyleValue(relativeField):(image.style[keyField.type]=getStyleValue(keyField),image.style[relativeField.type]=getStyleValue(relativeField))}(imagePreview,keyField,relativeField,!!constrainField.checked)}getSelectedImageProperties(){const image=this.getSelectedImage();if(!image)return this.selectedImage=null,null;const properties={src:null,alt:null,width:null,height:null,align:"",presentation:!1};this.removeLegacyAlignment(image),this.selectedImage=image,properties.customStyle=image.style.cssText;const width=(image=>this.isPercentageValue(String(image.width))?image.width:parseInt(image.width,10))(image);0!==width&&(properties.width=width);const height=(image=>this.isPercentageValue(String(image.height))?image.height:parseInt(image.height,10))(image);0!==height&&(properties.height=height);const alignment=this.getAlignmentProperties(image,properties);return alignment&&(properties.align=alignment.value),properties.src=image.getAttribute("src"),properties.alt=image.getAttribute("alt")||"",properties.presentation="presentation"===image.getAttribute("role"),properties}removeLegacyAlignment(imageNode){return imageNode.style.margin?(_selectors.default.IMAGE.alignments.some((alignment=>{if(imageNode.style[alignment.name]!==alignment.value)return!1;const normalisedNode=document.createElement("div");return normalisedNode.style.margin=alignment.margin,imageNode.style.margin===normalisedNode.style.margin&&(imageNode.classList.add(this.getAlignmentClass(alignment.value)),imageNode.style[alignment.name]=null,imageNode.style.margin=null,!0)})),imageNode):imageNode}getAlignmentProperties(image){const currentAlignment=_selectors.default.IMAGE.alignments.find((alignment=>!!image.classList.contains(this.getAlignmentClass(alignment.value))||!!alignment.legacyValues&&alignment.legacyValues.some((legacyValue=>image.classList.contains(legacyValue)))));return currentAlignment||_selectors.default.IMAGE.alignments.find((alignment=>alignment.isDefault))}getSelectedImage(){const imgElm=this.editor.selection.getNode(),figureElm=this.editor.dom.getParent(imgElm,"figure.image");return figureElm?this.editor.dom.select("img",figureElm)[0]:imgElm&&("IMG"!==imgElm.nodeName.toUpperCase()||this.isPlaceholderImage(imgElm))?null:imgElm}isPlaceholderImage(imgElm){return"IMG"===imgElm.nodeName.toUpperCase()&&(imgElm.hasAttribute("data-mce-object")||imgElm.hasAttribute("data-mce-placeholder"))}isPercentageValue(value){return value.match(/\d+%/)}async registerEventListeners(modal){await modal.getBody();const root=modal.getRoot()[0];this.form=root.querySelector(_selectors.default.IMAGE.elements.form),root.addEventListener("click",(e=>{const submitAction=e.target.closest(_selectors.default.IMAGE.actions.submit),imageBrowserAction=e.target.closest(_selectors.default.IMAGE.actions.imageBrowser);submitAction&&(e.preventDefault(),this.setImage()),imageBrowserAction&&this.canShowFilePicker&&(e.preventDefault(),(0,_utils.displayFilepicker)(this.editor,"image").then((params=>{this.filePickerCallback(params,this)})).catch())})),root.addEventListener("change",(e=>{e.target.closest(_selectors.default.IMAGE.elements.url)&&this.hasErrorUrlField();e.target.closest(_selectors.default.IMAGE.elements.presentation)&&this.hasErrorAltField();e.target.closest(_selectors.default.IMAGE.elements.constrain)&&this.autoAdjustSize(!0)})),root.addEventListener("blur",(e=>{if(e.target.nodeType===Node.ELEMENT_NODE){e.target.closest(_selectors.default.IMAGE.elements.url)&&this.urlChanged();e.target.closest(_selectors.default.IMAGE.elements.alt)&&this.hasErrorAltField();e.target.closest(_selectors.default.IMAGE.elements.width)&&this.autoAdjustSize();e.target.closest(_selectors.default.IMAGE.elements.height)&&this.autoAdjustSize(!0)}}),!0),root.addEventListener("keyup",(e=>{e.target.closest(_selectors.default.IMAGE.elements.alt)&&this.handleKeyupCharacterCount()}))}}})); //# sourceMappingURL=image.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/image.min.js.map b/lib/editor/tiny/plugins/media/amd/build/image.min.js.map index 137734d8a5f..cc32b6768f2 100644 --- a/lib/editor/tiny/plugins/media/amd/build/image.min.js.map +++ b/lib/editor/tiny/plugins/media/amd/build/image.min.js.map @@ -1 +1 @@ -{"version":3,"file":"image.min.js","sources":["../src/image.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 * Tiny Media plugin Image class for Moodle.\n *\n * @module tiny_media/image\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {get_string as getString, get_strings as getStrings} from 'core/str';\nimport * as ModalFactory from 'core/modal_factory';\nimport Pending from 'core/pending';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport Selectors from './selectors';\nimport Modal from './imagemodal';\nimport {getImagePermissions} from './options';\nimport {component} from \"./common\";\n\nexport const MediaImage = class {\n\n DEFAULTS = {\n WIDTH: 160,\n HEIGHT: 160,\n };\n\n form = null;\n rawImageDimensions = null;\n canShowFilePicker = false;\n editor = null;\n currentModal = null;\n selectedImage = null;\n imageAlignment = null;\n\n constructor(editor) {\n const permissions = getImagePermissions(editor);\n this.canShowFilePicker = permissions.filepicker;\n this.editor = editor;\n }\n\n async displayDialogue() {\n // Reset the image dimensions.\n this.rawImageDimensions = null;\n\n const currentImageData = await this.getCurrentImageData();\n const modal = await ModalFactory.create({\n type: Modal.TYPE,\n title: getString('imageproperties', 'tiny_media'),\n templateContext: await this.getTemplateContext(currentImageData),\n removeOnClose: true,\n large: true,\n });\n\n this.currentModal = modal;\n if (currentImageData && currentImageData.src) {\n this.loadPreviewImage(currentImageData.src);\n }\n\n await this.registerEventListeners(modal);\n modal.show();\n }\n\n async getAlignmentTitles() {\n if (!this.alignmentTitles) {\n const [top, middle, bottom] = await getStrings([\n 'alignment_top',\n 'alignment_middle',\n 'alignment_bottom',\n ].map((key) => ({key, component})));\n\n this.alignmentTitles = {\n top,\n middle,\n bottom,\n };\n }\n\n return this.alignmentTitles;\n }\n\n async getImageAlignment(selected = '') {\n const titles = await this.getAlignmentTitles();\n const alignments = [\n {\n text: titles.top,\n value: 'align-top',\n },\n {\n text: titles.middle,\n value: 'align-middle',\n },\n {\n text: titles.bottom,\n value: 'align-bottom',\n },\n ];\n\n if (selected) {\n alignments.forEach((alignment, index, array) => {\n if (alignment.value === selected) {\n array[index].selected = true;\n }\n });\n }\n\n return alignments;\n }\n\n async getTemplateContext(data) {\n return {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n alignoptions: await this.getImageAlignment(),\n ...data,\n };\n }\n\n async getCurrentImageData() {\n const selectedImageProperties = this.getSelectedImageProperties();\n if (!selectedImageProperties) {\n return {};\n }\n\n const properties = {...selectedImageProperties};\n if (properties.align) {\n properties.alignoptions = await this.getImageAlignment(properties.align);\n }\n\n if (properties.src) {\n properties.haspreview = true;\n }\n\n if (!properties.alt) {\n properties.presentation = true;\n }\n\n return properties;\n }\n\n filePickerCallback(params, self) {\n if (params.url) {\n const input = self.form.querySelector(Selectors.IMAGE.elements.url);\n input.value = params.url;\n\n // Auto set the width and height.\n self.form.querySelector(Selectors.IMAGE.elements.width).value = '';\n self.form.querySelector(Selectors.IMAGE.elements.height).value = '';\n\n // Load the preview image.\n self.loadPreviewImage(params.url);\n }\n }\n\n storeImageDimensions(image) {\n // Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG).\n this.rawImageDimensions = {\n width: image.width || this.DEFAULTS.WIDTH,\n height: image.height || this.DEFAULTS.HEIGHT,\n };\n\n const getCurrentWidth = (element) => {\n if (element.value === '') {\n element.value = this.rawImageDimensions.width;\n }\n return element.value;\n };\n const getCurrentHeight = (element) => {\n if (element.value === '') {\n element.value = this.rawImageDimensions.height;\n }\n return element.value;\n };\n\n const widthInput = this.form.querySelector(Selectors.IMAGE.elements.width);\n const currentWidth = getCurrentWidth(widthInput);\n\n const heightInput = this.form.querySelector(Selectors.IMAGE.elements.height);\n const currentHeight = getCurrentHeight(heightInput);\n\n const preview = this.form.querySelector(Selectors.IMAGE.elements.preview);\n preview.setAttribute('src', image.src);\n preview.style.display = 'inline';\n\n const constrain = this.form.querySelector(Selectors.IMAGE.elements.constrain);\n if (this.isPercentageValue(currentWidth) && this.isPercentageValue(currentHeight)) {\n constrain.checked = currentWidth === currentHeight;\n } else if (image.width === 0 || image.height === 0) {\n // If we don't have both dimensions of the image, we can't auto-size it, so disable control.\n constrain.disabled = 'disabled';\n } else {\n // This is the same as comparing to 3 decimal places.\n const widthRatio = Math.round(1000 * parseInt(currentWidth, 10) / image.width);\n const heightRatio = Math.round(1000 * parseInt(currentHeight, 10) / image.height);\n constrain.checked = widthRatio === heightRatio;\n }\n }\n\n loadPreviewImage(url) {\n const image = new Image();\n\n image.addEventListener('error', () => {\n const preview = this.form.querySelector(Selectors.IMAGE.elements.preview);\n preview.style.display = 'none';\n });\n image.addEventListener('load', () => {\n this.storeImageDimensions(image);\n this.autoAdjustSize();\n });\n\n image.src = url;\n }\n\n urlChanged() {\n const input = this.form.querySelector(Selectors.IMAGE.elements.url);\n\n if (input.value) {\n // Load the preview image.\n this.loadPreviewImage(input.value);\n }\n }\n\n hasErrorUrlField() {\n const url = this.form.querySelector(Selectors.IMAGE.elements.url).value;\n const urlError = url === '';\n this.toggleVisibility(Selectors.IMAGE.elements.urlWarning, urlError);\n this.toggleAriaInvalid([Selectors.IMAGE.elements.url], urlError);\n\n return urlError;\n }\n\n hasErrorAltField() {\n const alt = this.form.querySelector(Selectors.IMAGE.elements.alt).value;\n const presentation = this.form.querySelector(Selectors.IMAGE.elements.presentation).checked;\n const imageAltError = alt === '' && !presentation;\n this.toggleVisibility(Selectors.IMAGE.elements.altWarning, imageAltError);\n this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError);\n\n return imageAltError;\n }\n\n toggleVisibility(selector, predicate) {\n const elements = this.form.querySelectorAll(selector);\n elements.forEach((element) => {\n element.style.display = predicate ? 'block' : 'none';\n });\n }\n\n toggleAriaInvalid(selectors, predicate) {\n selectors.forEach((selector) => {\n const elements = this.form.querySelectorAll(selector);\n elements.forEach((element) => element.setAttribute('aria-invalid', predicate));\n });\n }\n\n getAlignmentClass(alignment) {\n return alignment;\n }\n\n updateWarning() {\n const urlError = this.hasErrorUrlField();\n const imageAltError = this.hasErrorAltField();\n\n return urlError || imageAltError;\n }\n\n getImageContext() {\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n return null;\n }\n\n const classList = [];\n\n const constrain = this.form.querySelector(Selectors.IMAGE.elements.constrain).value;\n if (constrain) {\n classList.push(Selectors.IMAGE.styles.responsive);\n }\n\n // Add the alignment class for the image.\n const alignment = this.getAlignmentClass(this.form.querySelector(Selectors.IMAGE.elements.alignment).value);\n classList.push(alignment);\n\n return {\n url: this.form.querySelector(Selectors.IMAGE.elements.url).value,\n alt: this.form.querySelector(Selectors.IMAGE.elements.alt).value,\n width: this.form.querySelector(Selectors.IMAGE.elements.width).value,\n height: this.form.querySelector(Selectors.IMAGE.elements.height).value,\n presentation: this.form.querySelector(Selectors.IMAGE.elements.presentation).checked,\n customStyle: this.form.querySelector(Selectors.IMAGE.elements.customStyle).value,\n classlist: classList.join(' '),\n };\n }\n\n setImage() {\n const pendingPromise = new Pending('tiny_media:setImage');\n const url = this.form.querySelector(Selectors.IMAGE.elements.url).value;\n if (url === '') {\n return;\n }\n\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n pendingPromise.resolve();\n return;\n }\n\n // Check for invalid width or height.\n const width = this.form.querySelector(Selectors.IMAGE.elements.width).value;\n if (!this.isPercentageValue(width) && isNaN(parseInt(width, 10))) {\n this.form.querySelector(Selectors.IMAGE.elements.width).focus();\n pendingPromise.resolve();\n return;\n }\n\n const height = this.form.querySelector(Selectors.IMAGE.elements.height).value;\n if (!this.isPercentageValue(height) && isNaN(parseInt(height, 10))) {\n this.form.querySelector(Selectors.IMAGE.elements.height).focus();\n pendingPromise.resolve();\n return;\n }\n\n Templates.render('tiny_media/image', this.getImageContext())\n .then((html) => {\n this.editor.insertContent(html);\n this.currentModal.destroy();\n pendingPromise.resolve();\n\n return html;\n })\n .catch();\n }\n\n handleKeyupCharacterCount() {\n const alt = this.form.querySelector(Selectors.IMAGE.elements.alt).value;\n const current = this.form.querySelector('#currentcount');\n current.innerHTML = alt.length;\n }\n\n autoAdjustSize(forceHeight = false) {\n // If we do not know the image size, do not do anything.\n if (!this.rawImageDimensions) {\n return;\n }\n\n const widthField = this.form.querySelector(Selectors.IMAGE.elements.width);\n const heightField = this.form.querySelector(Selectors.IMAGE.elements.height);\n const normalizeFieldData = (fieldData) => {\n fieldData.isPercentageValue = !!this.isPercentageValue(fieldData.field.value);\n if (fieldData.isPercentageValue) {\n fieldData.percentValue = parseInt(fieldData.field.value, 10);\n fieldData.pixelSize = this.rawImageDimensions[fieldData.type] / 100 * fieldData.percentValue;\n } else {\n fieldData.pixelSize = parseInt(fieldData.field.value, 10);\n fieldData.percentValue = fieldData.pixelSize / this.rawImageDimensions[fieldData.type] * 100;\n }\n\n return fieldData;\n };\n\n const getKeyField = () => {\n const getValue = () => {\n if (forceHeight) {\n return {\n field: heightField,\n type: 'height',\n };\n } else {\n return {\n field: widthField,\n type: 'width',\n };\n }\n };\n\n const currentValue = getValue();\n if (currentValue.field.value === '') {\n currentValue.field.value = this.rawImageDimensions[currentValue.type];\n }\n\n return normalizeFieldData(currentValue);\n };\n\n const getRelativeField = () => {\n if (forceHeight) {\n return normalizeFieldData({\n field: widthField,\n type: 'width',\n });\n } else {\n return normalizeFieldData({\n field: heightField,\n type: 'height',\n });\n }\n };\n\n\n const setImageDimensions = (image, keyField, relativeField, forceHeight = false) => {\n const getStyleValue = (field) => field.isPercentageValue ? `${field.percentValue}%` : `${field.pixelSize}px`;\n\n // If the values are constrained, then only update the width.\n if (forceHeight) {\n if (keyField.type === 'width') {\n image.style.width = getStyleValue(keyField);\n } else {\n image.style.width = getStyleValue(relativeField);\n }\n } else {\n image.style[keyField.type] = getStyleValue(keyField);\n image.style[relativeField.type] = getStyleValue(relativeField);\n }\n };\n\n const imagePreview = this.form.querySelector(Selectors.IMAGE.elements.preview);\n // Clear the existing preview sizes.\n imagePreview.style.width = '';\n imagePreview.style.height = '';\n\n // Now update with the new values.\n const constrainField = this.form.querySelector(Selectors.IMAGE.elements.constrain);\n const keyField = getKeyField();\n const relativeField = getRelativeField();\n if (constrainField.checked) {\n // We are keeping the image in proportion.\n // Calculate the size for the relative field.\n if (keyField.isPercentageValue) {\n // In proportion, so the percentages are the same.\n relativeField.field.value = keyField.field.value;\n relativeField.percentValue = keyField.percentValue;\n } else {\n relativeField.pixelSize = Math.round(\n keyField.pixelSize / this.rawImageDimensions[keyField.type] * this.rawImageDimensions[relativeField.type]\n );\n relativeField.field.value = relativeField.pixelSize;\n }\n }\n setImageDimensions(imagePreview, keyField, relativeField, !!constrainField.checked);\n }\n\n getSelectedImageProperties() {\n const image = this.getSelectedImage();\n if (!image) {\n this.selectedImage = null;\n return null;\n }\n\n const properties = {\n src: null,\n alt: null,\n width: null,\n height: null,\n align: '',\n presentation: false,\n };\n\n const getImageHeight = (image) => {\n if (!this.isPercentageValue(String(image.height))) {\n return parseInt(image.height, 10);\n }\n\n return image.height;\n };\n\n const getImageWidth = (image) => {\n if (!this.isPercentageValue(String(image.width))) {\n return parseInt(image.width, 10);\n }\n\n return image.width;\n };\n\n // Get the current selection.\n this.removeLegacyAlignment(image);\n this.selectedImage = image;\n\n properties.customStyle = image.style.cssText;\n\n const width = getImageWidth(image);\n if (width !== 0) {\n properties.width = width;\n }\n\n const height = getImageHeight(image);\n if (height !== 0) {\n properties.height = height;\n }\n\n const alignment = this.getAlignmentProperties(image, properties);\n if (alignment) {\n properties.align = alignment.value;\n }\n\n properties.src = image.getAttribute('src');\n properties.alt = image.getAttribute('alt') || '';\n properties.presentation = (image.getAttribute('role') === 'presentation');\n\n return properties;\n }\n\n removeLegacyAlignment(imageNode) {\n if (!imageNode.style.margin) {\n // There is no margin therefore this cannot match any known alignments.\n return imageNode;\n }\n\n Selectors.IMAGE.alignments.some(alignment => {\n if (imageNode.style[alignment.name] !== alignment.value) {\n // The name/value do not match. Skip.\n return false;\n }\n const normalisedNode = document.createElement('div');\n normalisedNode.style.margin = alignment.margin;\n if (imageNode.style.margin !== normalisedNode.style.margin) {\n // The margin does not match.\n return false;\n }\n\n imageNode.classList.add(this.getAlignmentClass(alignment.value));\n imageNode.style[alignment.name] = null;\n imageNode.style.margin = null;\n\n return true;\n });\n\n return imageNode;\n }\n\n getAlignmentProperties(image) {\n const currentAlignment = Selectors.IMAGE.alignments.find((alignment) => {\n if (image.classList.contains(this.getAlignmentClass(alignment.value))) {\n return true;\n }\n\n if (alignment.legacyValues) {\n return alignment.legacyValues.some((legacyValue) => image.classList.contains(legacyValue));\n }\n\n return false;\n });\n if (currentAlignment) {\n return currentAlignment;\n }\n\n return Selectors.IMAGE.alignments.find((alignment) => alignment.isDefault);\n }\n\n getSelectedImage() {\n const imgElm = this.editor.selection.getNode();\n const figureElm = this.editor.dom.getParent(imgElm, 'figure.image');\n if (figureElm) {\n return this.editor.dom.select('img', figureElm)[0];\n }\n\n if (imgElm && (imgElm.nodeName.toUpperCase() !== 'IMG' || this.isPlaceholderImage(imgElm))) {\n return null;\n }\n return imgElm;\n }\n\n isPlaceholderImage(imgElm) {\n if (imgElm.nodeName.toUpperCase() !== 'IMG') {\n return false;\n }\n\n return (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder'));\n }\n\n isPercentageValue(value) {\n return value.match(/\\d+%/);\n }\n\n async registerEventListeners(modal) {\n await modal.getBody();\n const root = modal.getRoot()[0];\n\n this.form = root.querySelector(Selectors.IMAGE.elements.form);\n root.addEventListener('click', (e) => {\n const submitAction = e.target.closest(Selectors.IMAGE.actions.submit);\n const imageBrowserAction = e.target.closest(Selectors.IMAGE.actions.imageBrowser);\n if (submitAction) {\n e.preventDefault();\n this.setImage();\n }\n if (imageBrowserAction && this.canShowFilePicker) {\n e.preventDefault();\n displayFilepicker(this.editor, 'image').then((params) => {\n const self = this;\n this.filePickerCallback(params, self);\n\n return;\n }).catch();\n }\n });\n\n root.addEventListener('change', (e) => {\n const urlEle = e.target.closest(Selectors.IMAGE.elements.url);\n if (urlEle) {\n this.hasErrorUrlField();\n }\n\n const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);\n if (presentationEle) {\n this.hasErrorAltField();\n }\n\n const constrainEle = e.target.closest(Selectors.IMAGE.elements.constrain);\n if (constrainEle) {\n this.autoAdjustSize(true);\n }\n });\n\n root.addEventListener('blur', (e) => {\n if (e.target.nodeType === Node.ELEMENT_NODE) {\n const urlEle = e.target.closest(Selectors.IMAGE.elements.url);\n if (urlEle) {\n this.urlChanged();\n }\n\n const altEle = e.target.closest(Selectors.IMAGE.elements.alt);\n if (altEle) {\n this.hasErrorAltField();\n }\n\n const widthEle = e.target.closest(Selectors.IMAGE.elements.width);\n if (widthEle) {\n this.autoAdjustSize();\n }\n\n const heightEle = e.target.closest(Selectors.IMAGE.elements.height);\n if (heightEle) {\n this.autoAdjustSize(true);\n }\n }\n }, true);\n\n // Character count.\n root.addEventListener('keyup', (e) => {\n const altEle = e.target.closest(Selectors.IMAGE.elements.alt);\n if (altEle) {\n this.handleKeyupCharacterCount();\n }\n });\n }\n};\n"],"names":["constructor","editor","WIDTH","HEIGHT","permissions","canShowFilePicker","filepicker","rawImageDimensions","currentImageData","this","getCurrentImageData","modal","ModalFactory","create","type","Modal","TYPE","title","templateContext","getTemplateContext","removeOnClose","large","currentModal","src","loadPreviewImage","registerEventListeners","show","alignmentTitles","top","middle","bottom","map","key","component","selected","titles","getAlignmentTitles","alignments","text","value","forEach","alignment","index","array","data","elementid","id","showfilepicker","alignoptions","getImageAlignment","selectedImageProperties","getSelectedImageProperties","properties","align","haspreview","alt","presentation","filePickerCallback","params","self","url","form","querySelector","Selectors","IMAGE","elements","width","height","storeImageDimensions","image","DEFAULTS","currentWidth","element","getCurrentWidth","currentHeight","getCurrentHeight","preview","setAttribute","style","display","constrain","isPercentageValue","checked","disabled","widthRatio","Math","round","parseInt","heightRatio","Image","addEventListener","autoAdjustSize","urlChanged","input","hasErrorUrlField","urlError","toggleVisibility","urlWarning","toggleAriaInvalid","hasErrorAltField","imageAltError","altWarning","selector","predicate","querySelectorAll","selectors","getAlignmentClass","updateWarning","getImageContext","classList","push","styles","responsive","customStyle","classlist","join","setImage","pendingPromise","Pending","resolve","isNaN","focus","render","then","html","insertContent","destroy","catch","handleKeyupCharacterCount","innerHTML","length","forceHeight","widthField","heightField","normalizeFieldData","fieldData","field","percentValue","pixelSize","imagePreview","constrainField","keyField","currentValue","getKeyField","relativeField","getStyleValue","setImageDimensions","getSelectedImage","selectedImage","removeLegacyAlignment","cssText","String","getImageWidth","getImageHeight","getAlignmentProperties","getAttribute","imageNode","margin","some","name","normalisedNode","document","createElement","add","currentAlignment","find","contains","legacyValues","legacyValue","isDefault","imgElm","selection","getNode","figureElm","dom","getParent","select","nodeName","toUpperCase","isPlaceholderImage","hasAttribute","match","getBody","root","getRoot","e","submitAction","target","closest","actions","submit","imageBrowserAction","imageBrowser","preventDefault","nodeType","Node","ELEMENT_NODE"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"image.min.js","sources":["../src/image.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 * Tiny Media plugin Image class for Moodle.\n *\n * @module tiny_media/image\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {getString, getStrings} from 'core/str';\nimport * as ModalFactory from 'core/modal_factory';\nimport Pending from 'core/pending';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport Selectors from './selectors';\nimport Modal from './imagemodal';\nimport {getImagePermissions} from './options';\nimport {component} from \"./common\";\n\nexport const MediaImage = class {\n\n DEFAULTS = {\n WIDTH: 160,\n HEIGHT: 160,\n };\n\n form = null;\n rawImageDimensions = null;\n canShowFilePicker = false;\n editor = null;\n currentModal = null;\n selectedImage = null;\n imageAlignment = null;\n\n constructor(editor) {\n const permissions = getImagePermissions(editor);\n this.canShowFilePicker = permissions.filepicker;\n this.editor = editor;\n }\n\n async displayDialogue() {\n // Reset the image dimensions.\n this.rawImageDimensions = null;\n\n const currentImageData = await this.getCurrentImageData();\n const modal = await ModalFactory.create({\n type: Modal.TYPE,\n title: getString('imageproperties', 'tiny_media'),\n templateContext: await this.getTemplateContext(currentImageData),\n removeOnClose: true,\n large: true,\n });\n\n this.currentModal = modal;\n if (currentImageData && currentImageData.src) {\n this.loadPreviewImage(currentImageData.src);\n }\n\n await this.registerEventListeners(modal);\n modal.show();\n }\n\n async getAlignmentTitles() {\n if (!this.alignmentTitles) {\n const [top, middle, bottom] = await getStrings([\n 'alignment_top',\n 'alignment_middle',\n 'alignment_bottom',\n ].map((key) => ({key, component})));\n\n this.alignmentTitles = {\n top,\n middle,\n bottom,\n };\n }\n\n return this.alignmentTitles;\n }\n\n async getImageAlignment(selected = '') {\n const titles = await this.getAlignmentTitles();\n const alignments = [\n {\n text: titles.top,\n value: 'align-top',\n },\n {\n text: titles.middle,\n value: 'align-middle',\n },\n {\n text: titles.bottom,\n value: 'align-bottom',\n },\n ];\n\n if (selected) {\n alignments.forEach((alignment, index, array) => {\n if (alignment.value === selected) {\n array[index].selected = true;\n }\n });\n }\n\n return alignments;\n }\n\n async getTemplateContext(data) {\n return {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n alignoptions: await this.getImageAlignment(),\n ...data,\n };\n }\n\n async getCurrentImageData() {\n const selectedImageProperties = this.getSelectedImageProperties();\n if (!selectedImageProperties) {\n return {};\n }\n\n const properties = {...selectedImageProperties};\n if (properties.align) {\n properties.alignoptions = await this.getImageAlignment(properties.align);\n }\n\n if (properties.src) {\n properties.haspreview = true;\n }\n\n if (!properties.alt) {\n properties.presentation = true;\n }\n\n return properties;\n }\n\n filePickerCallback(params, self) {\n if (params.url) {\n const input = self.form.querySelector(Selectors.IMAGE.elements.url);\n input.value = params.url;\n\n // Auto set the width and height.\n self.form.querySelector(Selectors.IMAGE.elements.width).value = '';\n self.form.querySelector(Selectors.IMAGE.elements.height).value = '';\n\n // Load the preview image.\n self.loadPreviewImage(params.url);\n }\n }\n\n storeImageDimensions(image) {\n // Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG).\n this.rawImageDimensions = {\n width: image.width || this.DEFAULTS.WIDTH,\n height: image.height || this.DEFAULTS.HEIGHT,\n };\n\n const getCurrentWidth = (element) => {\n if (element.value === '') {\n element.value = this.rawImageDimensions.width;\n }\n return element.value;\n };\n const getCurrentHeight = (element) => {\n if (element.value === '') {\n element.value = this.rawImageDimensions.height;\n }\n return element.value;\n };\n\n const widthInput = this.form.querySelector(Selectors.IMAGE.elements.width);\n const currentWidth = getCurrentWidth(widthInput);\n\n const heightInput = this.form.querySelector(Selectors.IMAGE.elements.height);\n const currentHeight = getCurrentHeight(heightInput);\n\n const preview = this.form.querySelector(Selectors.IMAGE.elements.preview);\n preview.setAttribute('src', image.src);\n preview.style.display = 'inline';\n\n const constrain = this.form.querySelector(Selectors.IMAGE.elements.constrain);\n if (this.isPercentageValue(currentWidth) && this.isPercentageValue(currentHeight)) {\n constrain.checked = currentWidth === currentHeight;\n } else if (image.width === 0 || image.height === 0) {\n // If we don't have both dimensions of the image, we can't auto-size it, so disable control.\n constrain.disabled = 'disabled';\n } else {\n // This is the same as comparing to 3 decimal places.\n const widthRatio = Math.round(1000 * parseInt(currentWidth, 10) / image.width);\n const heightRatio = Math.round(1000 * parseInt(currentHeight, 10) / image.height);\n constrain.checked = widthRatio === heightRatio;\n }\n }\n\n loadPreviewImage(url) {\n const image = new Image();\n\n image.addEventListener('error', () => {\n const preview = this.form.querySelector(Selectors.IMAGE.elements.preview);\n preview.style.display = 'none';\n });\n image.addEventListener('load', () => {\n this.storeImageDimensions(image);\n this.autoAdjustSize();\n });\n\n image.src = url;\n }\n\n urlChanged() {\n const input = this.form.querySelector(Selectors.IMAGE.elements.url);\n\n if (input.value) {\n // Load the preview image.\n this.loadPreviewImage(input.value);\n }\n }\n\n hasErrorUrlField() {\n const url = this.form.querySelector(Selectors.IMAGE.elements.url).value;\n const urlError = url === '';\n this.toggleVisibility(Selectors.IMAGE.elements.urlWarning, urlError);\n this.toggleAriaInvalid([Selectors.IMAGE.elements.url], urlError);\n\n return urlError;\n }\n\n hasErrorAltField() {\n const alt = this.form.querySelector(Selectors.IMAGE.elements.alt).value;\n const presentation = this.form.querySelector(Selectors.IMAGE.elements.presentation).checked;\n const imageAltError = alt === '' && !presentation;\n this.toggleVisibility(Selectors.IMAGE.elements.altWarning, imageAltError);\n this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError);\n\n return imageAltError;\n }\n\n toggleVisibility(selector, predicate) {\n const elements = this.form.querySelectorAll(selector);\n elements.forEach((element) => {\n element.style.display = predicate ? 'block' : 'none';\n });\n }\n\n toggleAriaInvalid(selectors, predicate) {\n selectors.forEach((selector) => {\n const elements = this.form.querySelectorAll(selector);\n elements.forEach((element) => element.setAttribute('aria-invalid', predicate));\n });\n }\n\n getAlignmentClass(alignment) {\n return alignment;\n }\n\n updateWarning() {\n const urlError = this.hasErrorUrlField();\n const imageAltError = this.hasErrorAltField();\n\n return urlError || imageAltError;\n }\n\n getImageContext() {\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n return null;\n }\n\n const classList = [];\n\n const constrain = this.form.querySelector(Selectors.IMAGE.elements.constrain).value;\n if (constrain) {\n classList.push(Selectors.IMAGE.styles.responsive);\n }\n\n // Add the alignment class for the image.\n const alignment = this.getAlignmentClass(this.form.querySelector(Selectors.IMAGE.elements.alignment).value);\n classList.push(alignment);\n\n return {\n url: this.form.querySelector(Selectors.IMAGE.elements.url).value,\n alt: this.form.querySelector(Selectors.IMAGE.elements.alt).value,\n width: this.form.querySelector(Selectors.IMAGE.elements.width).value,\n height: this.form.querySelector(Selectors.IMAGE.elements.height).value,\n presentation: this.form.querySelector(Selectors.IMAGE.elements.presentation).checked,\n customStyle: this.form.querySelector(Selectors.IMAGE.elements.customStyle).value,\n classlist: classList.join(' '),\n };\n }\n\n setImage() {\n const pendingPromise = new Pending('tiny_media:setImage');\n const url = this.form.querySelector(Selectors.IMAGE.elements.url).value;\n if (url === '') {\n return;\n }\n\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n pendingPromise.resolve();\n return;\n }\n\n // Check for invalid width or height.\n const width = this.form.querySelector(Selectors.IMAGE.elements.width).value;\n if (!this.isPercentageValue(width) && isNaN(parseInt(width, 10))) {\n this.form.querySelector(Selectors.IMAGE.elements.width).focus();\n pendingPromise.resolve();\n return;\n }\n\n const height = this.form.querySelector(Selectors.IMAGE.elements.height).value;\n if (!this.isPercentageValue(height) && isNaN(parseInt(height, 10))) {\n this.form.querySelector(Selectors.IMAGE.elements.height).focus();\n pendingPromise.resolve();\n return;\n }\n\n Templates.render('tiny_media/image', this.getImageContext())\n .then((html) => {\n this.editor.insertContent(html);\n this.currentModal.destroy();\n pendingPromise.resolve();\n\n return html;\n })\n .catch();\n }\n\n handleKeyupCharacterCount() {\n const alt = this.form.querySelector(Selectors.IMAGE.elements.alt).value;\n const current = this.form.querySelector('#currentcount');\n current.innerHTML = alt.length;\n }\n\n autoAdjustSize(forceHeight = false) {\n // If we do not know the image size, do not do anything.\n if (!this.rawImageDimensions) {\n return;\n }\n\n const widthField = this.form.querySelector(Selectors.IMAGE.elements.width);\n const heightField = this.form.querySelector(Selectors.IMAGE.elements.height);\n const normalizeFieldData = (fieldData) => {\n fieldData.isPercentageValue = !!this.isPercentageValue(fieldData.field.value);\n if (fieldData.isPercentageValue) {\n fieldData.percentValue = parseInt(fieldData.field.value, 10);\n fieldData.pixelSize = this.rawImageDimensions[fieldData.type] / 100 * fieldData.percentValue;\n } else {\n fieldData.pixelSize = parseInt(fieldData.field.value, 10);\n fieldData.percentValue = fieldData.pixelSize / this.rawImageDimensions[fieldData.type] * 100;\n }\n\n return fieldData;\n };\n\n const getKeyField = () => {\n const getValue = () => {\n if (forceHeight) {\n return {\n field: heightField,\n type: 'height',\n };\n } else {\n return {\n field: widthField,\n type: 'width',\n };\n }\n };\n\n const currentValue = getValue();\n if (currentValue.field.value === '') {\n currentValue.field.value = this.rawImageDimensions[currentValue.type];\n }\n\n return normalizeFieldData(currentValue);\n };\n\n const getRelativeField = () => {\n if (forceHeight) {\n return normalizeFieldData({\n field: widthField,\n type: 'width',\n });\n } else {\n return normalizeFieldData({\n field: heightField,\n type: 'height',\n });\n }\n };\n\n\n const setImageDimensions = (image, keyField, relativeField, forceHeight = false) => {\n const getStyleValue = (field) => field.isPercentageValue ? `${field.percentValue}%` : `${field.pixelSize}px`;\n\n // If the values are constrained, then only update the width.\n if (forceHeight) {\n if (keyField.type === 'width') {\n image.style.width = getStyleValue(keyField);\n } else {\n image.style.width = getStyleValue(relativeField);\n }\n } else {\n image.style[keyField.type] = getStyleValue(keyField);\n image.style[relativeField.type] = getStyleValue(relativeField);\n }\n };\n\n const imagePreview = this.form.querySelector(Selectors.IMAGE.elements.preview);\n // Clear the existing preview sizes.\n imagePreview.style.width = '';\n imagePreview.style.height = '';\n\n // Now update with the new values.\n const constrainField = this.form.querySelector(Selectors.IMAGE.elements.constrain);\n const keyField = getKeyField();\n const relativeField = getRelativeField();\n if (constrainField.checked) {\n // We are keeping the image in proportion.\n // Calculate the size for the relative field.\n if (keyField.isPercentageValue) {\n // In proportion, so the percentages are the same.\n relativeField.field.value = keyField.field.value;\n relativeField.percentValue = keyField.percentValue;\n } else {\n relativeField.pixelSize = Math.round(\n keyField.pixelSize / this.rawImageDimensions[keyField.type] * this.rawImageDimensions[relativeField.type]\n );\n relativeField.field.value = relativeField.pixelSize;\n }\n }\n setImageDimensions(imagePreview, keyField, relativeField, !!constrainField.checked);\n }\n\n getSelectedImageProperties() {\n const image = this.getSelectedImage();\n if (!image) {\n this.selectedImage = null;\n return null;\n }\n\n const properties = {\n src: null,\n alt: null,\n width: null,\n height: null,\n align: '',\n presentation: false,\n };\n\n const getImageHeight = (image) => {\n if (!this.isPercentageValue(String(image.height))) {\n return parseInt(image.height, 10);\n }\n\n return image.height;\n };\n\n const getImageWidth = (image) => {\n if (!this.isPercentageValue(String(image.width))) {\n return parseInt(image.width, 10);\n }\n\n return image.width;\n };\n\n // Get the current selection.\n this.removeLegacyAlignment(image);\n this.selectedImage = image;\n\n properties.customStyle = image.style.cssText;\n\n const width = getImageWidth(image);\n if (width !== 0) {\n properties.width = width;\n }\n\n const height = getImageHeight(image);\n if (height !== 0) {\n properties.height = height;\n }\n\n const alignment = this.getAlignmentProperties(image, properties);\n if (alignment) {\n properties.align = alignment.value;\n }\n\n properties.src = image.getAttribute('src');\n properties.alt = image.getAttribute('alt') || '';\n properties.presentation = (image.getAttribute('role') === 'presentation');\n\n return properties;\n }\n\n removeLegacyAlignment(imageNode) {\n if (!imageNode.style.margin) {\n // There is no margin therefore this cannot match any known alignments.\n return imageNode;\n }\n\n Selectors.IMAGE.alignments.some(alignment => {\n if (imageNode.style[alignment.name] !== alignment.value) {\n // The name/value do not match. Skip.\n return false;\n }\n const normalisedNode = document.createElement('div');\n normalisedNode.style.margin = alignment.margin;\n if (imageNode.style.margin !== normalisedNode.style.margin) {\n // The margin does not match.\n return false;\n }\n\n imageNode.classList.add(this.getAlignmentClass(alignment.value));\n imageNode.style[alignment.name] = null;\n imageNode.style.margin = null;\n\n return true;\n });\n\n return imageNode;\n }\n\n getAlignmentProperties(image) {\n const currentAlignment = Selectors.IMAGE.alignments.find((alignment) => {\n if (image.classList.contains(this.getAlignmentClass(alignment.value))) {\n return true;\n }\n\n if (alignment.legacyValues) {\n return alignment.legacyValues.some((legacyValue) => image.classList.contains(legacyValue));\n }\n\n return false;\n });\n if (currentAlignment) {\n return currentAlignment;\n }\n\n return Selectors.IMAGE.alignments.find((alignment) => alignment.isDefault);\n }\n\n getSelectedImage() {\n const imgElm = this.editor.selection.getNode();\n const figureElm = this.editor.dom.getParent(imgElm, 'figure.image');\n if (figureElm) {\n return this.editor.dom.select('img', figureElm)[0];\n }\n\n if (imgElm && (imgElm.nodeName.toUpperCase() !== 'IMG' || this.isPlaceholderImage(imgElm))) {\n return null;\n }\n return imgElm;\n }\n\n isPlaceholderImage(imgElm) {\n if (imgElm.nodeName.toUpperCase() !== 'IMG') {\n return false;\n }\n\n return (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder'));\n }\n\n isPercentageValue(value) {\n return value.match(/\\d+%/);\n }\n\n async registerEventListeners(modal) {\n await modal.getBody();\n const root = modal.getRoot()[0];\n\n this.form = root.querySelector(Selectors.IMAGE.elements.form);\n root.addEventListener('click', (e) => {\n const submitAction = e.target.closest(Selectors.IMAGE.actions.submit);\n const imageBrowserAction = e.target.closest(Selectors.IMAGE.actions.imageBrowser);\n if (submitAction) {\n e.preventDefault();\n this.setImage();\n }\n if (imageBrowserAction && this.canShowFilePicker) {\n e.preventDefault();\n displayFilepicker(this.editor, 'image').then((params) => {\n const self = this;\n this.filePickerCallback(params, self);\n\n return;\n }).catch();\n }\n });\n\n root.addEventListener('change', (e) => {\n const urlEle = e.target.closest(Selectors.IMAGE.elements.url);\n if (urlEle) {\n this.hasErrorUrlField();\n }\n\n const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);\n if (presentationEle) {\n this.hasErrorAltField();\n }\n\n const constrainEle = e.target.closest(Selectors.IMAGE.elements.constrain);\n if (constrainEle) {\n this.autoAdjustSize(true);\n }\n });\n\n root.addEventListener('blur', (e) => {\n if (e.target.nodeType === Node.ELEMENT_NODE) {\n const urlEle = e.target.closest(Selectors.IMAGE.elements.url);\n if (urlEle) {\n this.urlChanged();\n }\n\n const altEle = e.target.closest(Selectors.IMAGE.elements.alt);\n if (altEle) {\n this.hasErrorAltField();\n }\n\n const widthEle = e.target.closest(Selectors.IMAGE.elements.width);\n if (widthEle) {\n this.autoAdjustSize();\n }\n\n const heightEle = e.target.closest(Selectors.IMAGE.elements.height);\n if (heightEle) {\n this.autoAdjustSize(true);\n }\n }\n }, true);\n\n // Character count.\n root.addEventListener('keyup', (e) => {\n const altEle = e.target.closest(Selectors.IMAGE.elements.alt);\n if (altEle) {\n this.handleKeyupCharacterCount();\n }\n });\n }\n};\n"],"names":["constructor","editor","WIDTH","HEIGHT","permissions","canShowFilePicker","filepicker","rawImageDimensions","currentImageData","this","getCurrentImageData","modal","ModalFactory","create","type","Modal","TYPE","title","templateContext","getTemplateContext","removeOnClose","large","currentModal","src","loadPreviewImage","registerEventListeners","show","alignmentTitles","top","middle","bottom","map","key","component","selected","titles","getAlignmentTitles","alignments","text","value","forEach","alignment","index","array","data","elementid","id","showfilepicker","alignoptions","getImageAlignment","selectedImageProperties","getSelectedImageProperties","properties","align","haspreview","alt","presentation","filePickerCallback","params","self","url","form","querySelector","Selectors","IMAGE","elements","width","height","storeImageDimensions","image","DEFAULTS","currentWidth","element","getCurrentWidth","currentHeight","getCurrentHeight","preview","setAttribute","style","display","constrain","isPercentageValue","checked","disabled","widthRatio","Math","round","parseInt","heightRatio","Image","addEventListener","autoAdjustSize","urlChanged","input","hasErrorUrlField","urlError","toggleVisibility","urlWarning","toggleAriaInvalid","hasErrorAltField","imageAltError","altWarning","selector","predicate","querySelectorAll","selectors","getAlignmentClass","updateWarning","getImageContext","classList","push","styles","responsive","customStyle","classlist","join","setImage","pendingPromise","Pending","resolve","isNaN","focus","render","then","html","insertContent","destroy","catch","handleKeyupCharacterCount","innerHTML","length","forceHeight","widthField","heightField","normalizeFieldData","fieldData","field","percentValue","pixelSize","imagePreview","constrainField","keyField","currentValue","getKeyField","relativeField","getStyleValue","setImageDimensions","getSelectedImage","selectedImage","removeLegacyAlignment","cssText","String","getImageWidth","getImageHeight","getAlignmentProperties","getAttribute","imageNode","margin","some","name","normalisedNode","document","createElement","add","currentAlignment","find","contains","legacyValues","legacyValue","isDefault","imgElm","selection","getNode","figureElm","dom","getParent","select","nodeName","toUpperCase","isPlaceholderImage","hasAttribute","match","getBody","root","getRoot","e","submitAction","target","closest","actions","submit","imageBrowserAction","imageBrowser","preventDefault","nodeType","Node","ELEMENT_NODE"],"mappings":"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"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/manager.min.js b/lib/editor/tiny/plugins/media/amd/build/manager.min.js index 9e8c4cfd6ec..819c2d01959 100644 --- a/lib/editor/tiny/plugins/media/amd/build/manager.min.js +++ b/lib/editor/tiny/plugins/media/amd/build/manager.min.js @@ -1,3 +1,3 @@ -define("tiny_media/manager",["exports","core/templates","core/str","core/modal_factory","core/modal_events","./options","core/config"],(function(_exports,_templates,_str,Modal,ModalEvents,_options,_config){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.MediaManager=void 0,_templates=_interopRequireDefault(_templates),Modal=_interopRequireWildcard(Modal),ModalEvents=_interopRequireWildcard(ModalEvents),_config=_interopRequireDefault(_config);_exports.MediaManager=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"area",null),_defineProperty(this,"displayDialogue",(()=>{Modal.create({type:Modal.types.DEFAULT,large:!0,title:(0,_str.get_string)("mediamanagerproperties","tiny_media"),body:_templates.default.render("tiny_media/mm_iframe",{src:this.getIframeURL()})}).then((modal=>(modal.getRoot().on(ModalEvents.bodyRendered,(()=>{this.selectFirstElement()})),modal.getRoot().on(ModalEvents.hidden,(()=>{modal.destroy()})),modal.show(),document.querySelector(".modal-lg").style.cssText="max-width: 850px",modal))).catch()})),_defineProperty(this,"selectFirstElement",(()=>{const iframe=document.getElementById("mm-iframe");iframe.addEventListener("load",(function(){let intervalId=setInterval((function(){const iDocument=iframe.contentWindow.document;if(iDocument.querySelector(".filemanager")){const firstFocusableElement=iDocument.querySelector(".fp-navbar a:not([disabled])");firstFocusableElement&&firstFocusableElement.focus(),clearInterval(intervalId)}}),200)}))})),_defineProperty(this,"getIframeURL",(()=>{const url=new URL("".concat(_config.default.wwwroot,"/lib/editor/tiny/plugins/media/manage.php"));url.searchParams.append("elementid",this.editor.getElement().id);for(const key in this.area)url.searchParams.append(key,this.area[key]);return url.toString()})),this.editor=editor;const data=(0,_options.getData)(editor);this.area=data.params.area,this.area.itemid=data.fpoptions.image.itemid}}})); +define("tiny_media/manager",["exports","core/templates","core/str","core/modal_factory","core/modal_events","./options","core/config"],(function(_exports,_templates,_str,Modal,ModalEvents,_options,_config){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.MediaManager=void 0,_templates=_interopRequireDefault(_templates),Modal=_interopRequireWildcard(Modal),ModalEvents=_interopRequireWildcard(ModalEvents),_config=_interopRequireDefault(_config);_exports.MediaManager=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"area",null),_defineProperty(this,"displayDialogue",(()=>{Modal.create({type:Modal.types.DEFAULT,large:!0,title:(0,_str.getString)("mediamanagerproperties","tiny_media"),body:_templates.default.render("tiny_media/mm_iframe",{src:this.getIframeURL()})}).then((modal=>(modal.getRoot().on(ModalEvents.bodyRendered,(()=>{this.selectFirstElement()})),modal.getRoot().on(ModalEvents.hidden,(()=>{modal.destroy()})),modal.show(),document.querySelector(".modal-lg").style.cssText="max-width: 850px",modal))).catch()})),_defineProperty(this,"selectFirstElement",(()=>{const iframe=document.getElementById("mm-iframe");iframe.addEventListener("load",(function(){let intervalId=setInterval((function(){const iDocument=iframe.contentWindow.document;if(iDocument.querySelector(".filemanager")){const firstFocusableElement=iDocument.querySelector(".fp-navbar a:not([disabled])");firstFocusableElement&&firstFocusableElement.focus(),clearInterval(intervalId)}}),200)}))})),_defineProperty(this,"getIframeURL",(()=>{const url=new URL("".concat(_config.default.wwwroot,"/lib/editor/tiny/plugins/media/manage.php"));url.searchParams.append("elementid",this.editor.getElement().id);for(const key in this.area)url.searchParams.append(key,this.area[key]);return url.toString()})),this.editor=editor;const data=(0,_options.getData)(editor);this.area=data.params.area,this.area.itemid=data.fpoptions.image.itemid}}})); //# sourceMappingURL=manager.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/manager.min.js.map b/lib/editor/tiny/plugins/media/amd/build/manager.min.js.map index b1e4a51c09c..ed38a85c0dc 100644 --- a/lib/editor/tiny/plugins/media/amd/build/manager.min.js.map +++ b/lib/editor/tiny/plugins/media/amd/build/manager.min.js.map @@ -1 +1 @@ -{"version":3,"file":"manager.min.js","sources":["../src/manager.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 * Tiny Media Manager plugin class for Moodle.\n *\n * @module tiny_media/manager\n * @copyright 2022, Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {get_string as getString} from 'core/str';\nimport * as Modal from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {getData} from './options';\nimport Config from 'core/config';\n\nexport const MediaManager = class {\n\n editor = null;\n area = null;\n\n constructor(editor) {\n this.editor = editor;\n const data = getData(editor);\n this.area = data.params.area;\n this.area.itemid = data.fpoptions.image.itemid;\n }\n\n displayDialogue = () => {\n Modal.create({\n type: Modal.types.DEFAULT,\n large: true,\n title: getString('mediamanagerproperties', 'tiny_media'),\n body: Templates.render('tiny_media/mm_iframe', {\n src: this.getIframeURL()\n })\n }).then(modal => {\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n this.selectFirstElement();\n });\n modal.getRoot().on(ModalEvents.hidden, () => {\n modal.destroy();\n });\n modal.show();\n document.querySelector('.modal-lg').style.cssText = `max-width: 850px`;\n return modal;\n }).catch();\n };\n\n // It will select the first element in the file manager.\n selectFirstElement = () => {\n const iframe = document.getElementById('mm-iframe');\n iframe.addEventListener('load', function() {\n let intervalId = setInterval(function() {\n const iDocument = iframe.contentWindow.document;\n if (iDocument.querySelector('.filemanager')) {\n const firstFocusableElement = iDocument.querySelector('.fp-navbar a:not([disabled])');\n if (firstFocusableElement) {\n firstFocusableElement.focus();\n }\n clearInterval(intervalId);\n }\n }, 200);\n });\n };\n\n getIframeURL = () => {\n const url = new URL(`${Config.wwwroot}/lib/editor/tiny/plugins/media/manage.php`);\n url.searchParams.append('elementid', this.editor.getElement().id);\n for (const key in this.area) {\n url.searchParams.append(key, this.area[key]);\n }\n return url.toString();\n };\n};\n"],"names":["constructor","editor","Modal","create","type","types","DEFAULT","large","title","body","Templates","render","src","this","getIframeURL","then","modal","getRoot","on","ModalEvents","bodyRendered","selectFirstElement","hidden","destroy","show","document","querySelector","style","cssText","catch","iframe","getElementById","addEventListener","intervalId","setInterval","iDocument","contentWindow","firstFocusableElement","focus","clearInterval","url","URL","Config","wwwroot","searchParams","append","getElement","id","key","area","toString","data","params","itemid","fpoptions","image"],"mappings":"kpDA8B4B,MAKxBA,YAAYC,sCAHH,kCACF,8CASW,KACdC,MAAMC,OAAO,CACTC,KAAMF,MAAMG,MAAMC,QAClBC,OAAO,EACPC,OAAO,mBAAU,yBAA0B,cAC3CC,KAAMC,mBAAUC,OAAO,uBAAwB,CAC3CC,IAAKC,KAAKC,mBAEfC,MAAKC,QACJA,MAAMC,UAAUC,GAAGC,YAAYC,cAAc,UACpCC,wBAETL,MAAMC,UAAUC,GAAGC,YAAYG,QAAQ,KACnCN,MAAMO,aAEVP,MAAMQ,OACNC,SAASC,cAAc,aAAaC,MAAMC,2BACnCZ,SACRa,sDAIc,WACXC,OAASL,SAASM,eAAe,aACvCD,OAAOE,iBAAiB,QAAQ,eACxBC,WAAaC,aAAY,iBACnBC,UAAYL,OAAOM,cAAcX,YACnCU,UAAUT,cAAc,gBAAiB,OACnCW,sBAAwBF,UAAUT,cAAc,gCAClDW,uBACAA,sBAAsBC,QAE1BC,cAAcN,eAEnB,gDAII,WACLO,IAAM,IAAIC,cAAOC,gBAAOC,sDAC9BH,IAAII,aAAaC,OAAO,YAAahC,KAAKZ,OAAO6C,aAAaC,QACzD,MAAMC,OAAOnC,KAAKoC,KACnBT,IAAII,aAAaC,OAAOG,IAAKnC,KAAKoC,KAAKD,aAEpCR,IAAIU,mBAlDNjD,OAASA,aACRkD,MAAO,oBAAQlD,aAChBgD,KAAOE,KAAKC,OAAOH,UACnBA,KAAKI,OAASF,KAAKG,UAAUC,MAAMF"} \ No newline at end of file +{"version":3,"file":"manager.min.js","sources":["../src/manager.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 * Tiny Media Manager plugin class for Moodle.\n *\n * @module tiny_media/manager\n * @copyright 2022, Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport * as Modal from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {getData} from './options';\nimport Config from 'core/config';\n\nexport const MediaManager = class {\n\n editor = null;\n area = null;\n\n constructor(editor) {\n this.editor = editor;\n const data = getData(editor);\n this.area = data.params.area;\n this.area.itemid = data.fpoptions.image.itemid;\n }\n\n displayDialogue = () => {\n Modal.create({\n type: Modal.types.DEFAULT,\n large: true,\n title: getString('mediamanagerproperties', 'tiny_media'),\n body: Templates.render('tiny_media/mm_iframe', {\n src: this.getIframeURL()\n })\n }).then(modal => {\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n this.selectFirstElement();\n });\n modal.getRoot().on(ModalEvents.hidden, () => {\n modal.destroy();\n });\n modal.show();\n document.querySelector('.modal-lg').style.cssText = `max-width: 850px`;\n return modal;\n }).catch();\n };\n\n // It will select the first element in the file manager.\n selectFirstElement = () => {\n const iframe = document.getElementById('mm-iframe');\n iframe.addEventListener('load', function() {\n let intervalId = setInterval(function() {\n const iDocument = iframe.contentWindow.document;\n if (iDocument.querySelector('.filemanager')) {\n const firstFocusableElement = iDocument.querySelector('.fp-navbar a:not([disabled])');\n if (firstFocusableElement) {\n firstFocusableElement.focus();\n }\n clearInterval(intervalId);\n }\n }, 200);\n });\n };\n\n getIframeURL = () => {\n const url = new URL(`${Config.wwwroot}/lib/editor/tiny/plugins/media/manage.php`);\n url.searchParams.append('elementid', this.editor.getElement().id);\n for (const key in this.area) {\n url.searchParams.append(key, this.area[key]);\n }\n return url.toString();\n };\n};\n"],"names":["constructor","editor","Modal","create","type","types","DEFAULT","large","title","body","Templates","render","src","this","getIframeURL","then","modal","getRoot","on","ModalEvents","bodyRendered","selectFirstElement","hidden","destroy","show","document","querySelector","style","cssText","catch","iframe","getElementById","addEventListener","intervalId","setInterval","iDocument","contentWindow","firstFocusableElement","focus","clearInterval","url","URL","Config","wwwroot","searchParams","append","getElement","id","key","area","toString","data","params","itemid","fpoptions","image"],"mappings":"kpDA8B4B,MAKxBA,YAAYC,sCAHH,kCACF,8CASW,KACdC,MAAMC,OAAO,CACTC,KAAMF,MAAMG,MAAMC,QAClBC,OAAO,EACPC,OAAO,kBAAU,yBAA0B,cAC3CC,KAAMC,mBAAUC,OAAO,uBAAwB,CAC3CC,IAAKC,KAAKC,mBAEfC,MAAKC,QACJA,MAAMC,UAAUC,GAAGC,YAAYC,cAAc,UACpCC,wBAETL,MAAMC,UAAUC,GAAGC,YAAYG,QAAQ,KACnCN,MAAMO,aAEVP,MAAMQ,OACNC,SAASC,cAAc,aAAaC,MAAMC,2BACnCZ,SACRa,sDAIc,WACXC,OAASL,SAASM,eAAe,aACvCD,OAAOE,iBAAiB,QAAQ,eACxBC,WAAaC,aAAY,iBACnBC,UAAYL,OAAOM,cAAcX,YACnCU,UAAUT,cAAc,gBAAiB,OACnCW,sBAAwBF,UAAUT,cAAc,gCAClDW,uBACAA,sBAAsBC,QAE1BC,cAAcN,eAEnB,gDAII,WACLO,IAAM,IAAIC,cAAOC,gBAAOC,sDAC9BH,IAAII,aAAaC,OAAO,YAAahC,KAAKZ,OAAO6C,aAAaC,QACzD,MAAMC,OAAOnC,KAAKoC,KACnBT,IAAII,aAAaC,OAAOG,IAAKnC,KAAKoC,KAAKD,aAEpCR,IAAIU,mBAlDNjD,OAASA,aACRkD,MAAO,oBAAQlD,aAChBgD,KAAOE,KAAKC,OAAOH,UACnBA,KAAKI,OAASF,KAAKG,UAAUC,MAAMF"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/src/commands.js b/lib/editor/tiny/plugins/media/amd/src/commands.js index 4a44eb2c795..3a98d0b2d6e 100644 --- a/lib/editor/tiny/plugins/media/amd/src/commands.js +++ b/lib/editor/tiny/plugins/media/amd/src/commands.js @@ -21,7 +21,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import {get_strings as getStrings} from 'core/str'; +import {getStrings} from 'core/str'; import { component, imageButtonName, diff --git a/lib/editor/tiny/plugins/media/amd/src/embed.js b/lib/editor/tiny/plugins/media/amd/src/embed.js index ea07415056b..4f9667c5f99 100644 --- a/lib/editor/tiny/plugins/media/amd/src/embed.js +++ b/lib/editor/tiny/plugins/media/amd/src/embed.js @@ -23,8 +23,8 @@ import Templates from 'core/templates'; import { - get_string as getString, - get_strings as getStrings, + getString, + getStrings, } from 'core/str'; import * as ModalFactory from 'core/modal_factory'; import * as ModalEvents from 'core/modal_events'; diff --git a/lib/editor/tiny/plugins/media/amd/src/image.js b/lib/editor/tiny/plugins/media/amd/src/image.js index ee1a2525ba9..94800f70722 100644 --- a/lib/editor/tiny/plugins/media/amd/src/image.js +++ b/lib/editor/tiny/plugins/media/amd/src/image.js @@ -22,7 +22,7 @@ */ import Templates from 'core/templates'; -import {get_string as getString, get_strings as getStrings} from 'core/str'; +import {getString, getStrings} from 'core/str'; import * as ModalFactory from 'core/modal_factory'; import Pending from 'core/pending'; import {displayFilepicker} from 'editor_tiny/utils'; diff --git a/lib/editor/tiny/plugins/media/amd/src/manager.js b/lib/editor/tiny/plugins/media/amd/src/manager.js index a1e3abb4796..20570938121 100644 --- a/lib/editor/tiny/plugins/media/amd/src/manager.js +++ b/lib/editor/tiny/plugins/media/amd/src/manager.js @@ -22,7 +22,7 @@ */ import Templates from 'core/templates'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import * as Modal from 'core/modal_factory'; import * as ModalEvents from 'core/modal_events'; import {getData} from './options'; diff --git a/lib/editor/tiny/plugins/recordrtc/amd/build/base_recorder.min.js b/lib/editor/tiny/plugins/recordrtc/amd/build/base_recorder.min.js index 3c6b786822c..bdb888a9f23 100644 --- a/lib/editor/tiny/plugins/recordrtc/amd/build/base_recorder.min.js +++ b/lib/editor/tiny/plugins/recordrtc/amd/build/base_recorder.min.js @@ -1,3 +1,3 @@ -define("tiny_recordrtc/base_recorder",["exports","core/str","./common","core/pending","./options","editor_tiny/uploader","core/toast","core/modal_events","core/modal_factory","core/templates","core/notification","core/prefetch"],(function(_exports,_str,_common,_pending,_options,_uploader,_toast,ModalEvents,ModalFactory,Templates,_notification,_prefetch){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_pending=_interopRequireDefault(_pending),_uploader=_interopRequireDefault(_uploader),ModalEvents=_interopRequireWildcard(ModalEvents),ModalFactory=_interopRequireWildcard(ModalFactory),Templates=_interopRequireWildcard(Templates);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}return _exports.default=class{constructor(editor,modal){var obj,key,value;value=!1,(key="stopRequested")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.ready=!1,this.checkAndWarnAboutBrowserCompatibility()&&(this.editor=editor,this.config=(0,_options.getData)(editor).params,this.modal=modal,this.modalRoot=modal.getRoot()[0],this.startStopButton=this.modalRoot.querySelector('button[data-action="startstop"]'),this.uploadButton=this.modalRoot.querySelector('button[data-action="upload"]'),this.setRecordButtonState(!1),this.player=this.configurePlayer(),this.registerEventListeners(),this.ready=!0,this.captureUserMedia(),this.prefetchContent())}isReady(){return this.ready}configurePlayer(){throw new Error("configurePlayer() must be implemented in ".concat(this.constructor.name))}getSupportedTypes(){throw new Error("getSupportedTypes() must be implemented in ".concat(this.constructor.name))}getRecordingOptions(){throw new Error("getRecordingOptions() must be implemented in ".concat(this.constructor.name))}getFileName(prefix){throw new Error("getFileName() must be implemented in ".concat(this.constructor.name))}getMediaConstraints(){throw new Error("getMediaConstraints() must be implemented in ".concat(this.constructor.name))}playOnCapture(){return!1}getTimeLimit(){throw new Error("getTimeLimit() must be implemented in ".concat(this.constructor.name))}getEmbedTemplateName(){throw new Error("getEmbedTemplateName() must be implemented in ".concat(this.constructor.name))}static getModalClass(){throw new Error("getModalClass() must be implemented in ".concat(this.constructor.name))}getParsedRecordingOptions(){const compatTypes=this.getSupportedTypes().reduce(((result,type)=>(result.push(type),result.push(type.replace("=",":")),result)),[]).filter((type=>window.MediaRecorder.isTypeSupported(type))),options=this.getRecordingOptions();return 0!==compatTypes.length&&(options.mimeType=compatTypes[0]),window.console.info("Selected codec ".concat(options.mimeType," from ").concat(compatTypes.length," options."),compatTypes),options}async captureUserMedia(){try{const stream=await navigator.mediaDevices.getUserMedia(this.getMediaConstraints());this.handleCaptureSuccess(stream)}catch(error){this.handleCaptureFailure(error)}}prefetchContent(){(0,_prefetch.prefetchStrings)(_common.component,["uploading","recordagain_title","recordagain_desc","discard_title","discard_desc","confirm_yes","recordinguploaded","maxfilesizehit","maxfilesizehit_title","uploadfailed"]),(0,_prefetch.prefetchTemplates)([this.getEmbedTemplateName(),"tiny_recordrtc/timeremaining"])}async displayAlert(title,content){const pendingPromise=new _pending.default("core/confirm:alert"),ModalFactory=await("function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require(["core/modal_factory"],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require("core/modal_factory")):Promise.resolve(_systemImportTransformerGlobalIdentifier["core/modal_factory"])),modal=await ModalFactory.create({type:ModalFactory.types.ALERT,title:title,body:content,removeOnClose:!0});return modal.show(),pendingPromise.resolve(),modal}handleCaptureSuccess(stream){this.player.srcObject=stream,this.playOnCapture()&&(this.player.muted=!0,this.player.play()),this.stream=stream,this.setupPlayerSource(),this.setRecordButtonState(!0)}setupPlayerSource(){this.player.srcObject||(this.player.srcObject=this.stream,this.player.muted=!0,this.player.play())}setRecordButtonState(enabled){this.startStopButton.disabled=!enabled}setRecordButtonVisibility(visible){this.getButtonContainer("start-stop").classList.toggle("hide",!visible)}setUploadButtonState(enabled){this.uploadButton.disabled=!enabled}setUploadButtonVisibility(visible){this.getButtonContainer("upload").classList.toggle("hide",!visible)}handleCaptureFailure(error){var subject="gum".concat(error.name.replace("Error","").toLowerCase());this.displayAlert((0,_str.get_string)("".concat(subject,"_title"),_common.component),(0,_str.get_string)(subject,_common.component))}close(){this.modal.hide()}registerEventListeners(){this.modalRoot.addEventListener("click",this.handleModalClick.bind(this)),this.modal.getRoot().on(ModalEvents.outsideClick,this.outsideClickHandler.bind(this)),this.modal.getRoot().on(ModalEvents.hidden,(()=>{this.cleanupStream(),this.requestRecordingStop()}))}async outsideClickHandler(event){if(this.isRecording())event.preventDefault();else if(this.hasData()){event.preventDefault();try{await(0,_notification.saveCancelPromise)(await(0,_str.get_string)("discard_title",_common.component),await(0,_str.get_string)("discard_desc",_common.component),await(0,_str.get_string)("confirm_yes",_common.component)),this.modal.hide()}catch(error){}}}handleModalClick(event){const button=event.target.closest("button");if(button&&button.dataset.action){const action=button.dataset.action;"startstop"===action&&this.handleRecordingStartStopRequested(),"upload"===action&&this.uploadRecording()}}handleRecordingStartStopRequested(){var _this$mediaRecorder;"recording"===(null===(_this$mediaRecorder=this.mediaRecorder)||void 0===_this$mediaRecorder?void 0:_this$mediaRecorder.state)?this.requestRecordingStop():this.startRecording()}async onMediaStopped(){var _this$getButtonContai;this.blob=new Blob(this.data.chunks,{type:this.mediaRecorder.mimeType}),this.player.srcObject=null,this.player.src=URL.createObjectURL(this.blob),this.setRecordButtonTextFromString("recordagain"),this.player.muted=!1,this.player.controls=!0,null===(_this$getButtonContai=this.getButtonContainer("player"))||void 0===_this$getButtonContai||_this$getButtonContai.classList.toggle("hide",!1),this.setUploadButtonVisibility(!0),this.setUploadButtonState(!0)}async uploadRecording(){if(0===this.data.chunks.length)return void this.displayAlert("norecordingfound");const fileName=this.getFileName((1e3*Math.random()).toString().replace(".",""));try{this.setRecordButtonVisibility(!1),this.setUploadButtonState(!1);const fileURL=await(0,_uploader.default)(this.editor,"media",this.blob,fileName,(progress=>{this.setUploadButtonTextProgress(progress)}));this.insertMedia(fileURL),this.close(),(0,_toast.add)(await(0,_str.get_string)("recordinguploaded",_common.component))}catch(error){this.setUploadButtonState(!0),(0,_toast.add)(await(0,_str.get_string)("uploadfailed",_common.component,{error:error}),{type:"error"})}}getButtonContainer(purpose){return this.modalRoot.querySelector('[data-purpose="'.concat(purpose,'-container"]'))}static isBrowserCompatible(){return this.checkSecure()&&this.hasUserMedia()}static async display(editor){const ModalClass=this.getModalClass(),modal=await ModalFactory.create({type:ModalClass.TYPE,templateContext:{},large:!0});return new this(editor,modal).isReady()&&modal.show(),modal}checkAndWarnAboutBrowserCompatibility(){return this.constructor.checkSecure()?!!this.constructor.hasUserMedia||((0,_str.get_strings)(["nowebrtc_title","nowebrtc"].map((key=>({key:key,component:_common.component})))).then((_ref2=>{let[title,message]=_ref2;return(0,_toast.add)(message,{title:title,type:"error"})})).catch(),!1):((0,_str.get_strings)(["insecurealert_title","insecurealert"].map((key=>({key:key,component:_common.component})))).then((_ref=>{let[title,message]=_ref;return(0,_toast.add)(message,{title:title,type:"error"})})).catch(),!1)}static hasUserMedia(){return navigator.mediaDevices&&window.MediaRecorder}static checkSecure(){return window.isSecureContext}async setStopRecordingButton(){const{html:html,js:js}=await Templates.renderForPromise("tiny_recordrtc/timeremaining",this.getTimeRemaining());Templates.replaceNodeContents(this.startStopButton,html,js),this.buttonTimer=setInterval(this.updateRecordButtonTime.bind(this),500)}updateRecordButtonTime(){const{remaining:remaining,minutes:minutes,seconds:seconds}=this.getTimeRemaining();remaining<0?this.requestRecordingStop():(this.startStopButton.querySelector('[data-type="minutes"]').textContent=minutes,this.startStopButton.querySelector('[data-type="seconds"]').textContent=seconds)}async setRecordButtonTextFromString(string){this.startStopButton.textContent=await(0,_str.get_string)(string,_common.component)}async setUploadButtonTextProgress(progress){this.uploadButton.textContent=await(0,_str.get_string)("uploading",_common.component,{progress:Math.round(100*progress)/100})}async resetUploadButtonText(){this.uploadButton.textContent=await(0,_str.get_string)("upload",_common.component)}clearButtonTimer(){this.buttonTimer&&clearInterval(this.buttonTimer),this.buttonTimer=null}getTimeRemaining(){const now=(new Date).getTime(),remaining=Math.floor(this.getTimeLimit()-(now-this.startTime)/1e3),formatter=new Intl.NumberFormat(navigator.language,{minimumIntegerDigits:2}),seconds=formatter.format(remaining%60);return{remaining:remaining,minutes:formatter.format(Math.floor((remaining-seconds)/60)),seconds:seconds}}getMaxUploadSize(){return this.config.maxrecsize}requestRecordingStop(){this.mediaRecorder&&"inactive"!==this.mediaRecorder.state?this.stopRequested=!0:this.cleanupStream()}stopRecorder(){this.mediaRecorder.stop(),this.player.muted=!1}cleanupStream(){this.stream&&this.stream.getTracks().filter((track=>"ended"!==track.readyState)).forEach((track=>track.stop()))}handleStopped(){this.onMediaStopped(),this.clearButtonTimer()}handleStarted(){this.startTime=(new Date).getTime(),this.setStopRecordingButton()}handleDataAvailable(event){if(this.isRecording()){const newSize=this.data.blobSize+event.data.size;newSize>=this.getMaxUploadSize()?(this.stopRecorder(),this.displayFileLimitHitMessage()):(this.data.chunks.push(event.data),this.data.blobSize=newSize,this.stopRequested&&this.stopRecorder())}}async displayFileLimitHitMessage(){(0,_toast.add)(await(0,_str.get_string)("maxfilesizehit",_common.component),{title:await(0,_str.get_string)("maxfilesizehit_title",_common.component),type:"error"})}isRecording(){var _this$mediaRecorder2;return"recording"===(null===(_this$mediaRecorder2=this.mediaRecorder)||void 0===_this$mediaRecorder2?void 0:_this$mediaRecorder2.state)}hasData(){var _this$data;return!(null===(_this$data=this.data)||void 0===_this$data||!_this$data.blobSize)}async startRecording(){if(this.mediaRecorder){if(this.isRecording()&&this.mediaRecorder.stop(),this.hasData()){if(!await this.recordAgainConfirmation())return;this.setUploadButtonVisibility(!1)}this.mediaRecorder=null}this.mediaRecorder=new MediaRecorder(this.stream,this.getParsedRecordingOptions()),this.mediaRecorder.addEventListener("dataavailable",this.handleDataAvailable.bind(this)),this.mediaRecorder.addEventListener("stop",this.handleStopped.bind(this)),this.mediaRecorder.addEventListener("start",this.handleStarted.bind(this)),this.data={chunks:[],blobSize:0},this.setupPlayerSource(),this.stopRequested=!1,this.mediaRecorder.start(50)}async recordAgainConfirmation(){try{return await(0,_notification.saveCancelPromise)(await(0,_str.get_string)("recordagain_title",_common.component),await(0,_str.get_string)("recordagain_desc",_common.component),await(0,_str.get_string)("confirm_yes",_common.component)),!0}catch{return!1}}async insertMedia(source){const{html:html}=await Templates.renderForPromise(this.getEmbedTemplateName(),this.getEmbedTemplateContext({source:source}));this.editor.insertContent(html)}getEmbedTemplateContext(templateContext){return templateContext}},_exports.default})); +define("tiny_recordrtc/base_recorder",["exports","core/str","./common","core/pending","./options","editor_tiny/uploader","core/toast","core/modal_events","core/modal_factory","core/templates","core/notification","core/prefetch"],(function(_exports,_str,_common,_pending,_options,_uploader,_toast,ModalEvents,ModalFactory,Templates,_notification,_prefetch){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_pending=_interopRequireDefault(_pending),_uploader=_interopRequireDefault(_uploader),ModalEvents=_interopRequireWildcard(ModalEvents),ModalFactory=_interopRequireWildcard(ModalFactory),Templates=_interopRequireWildcard(Templates);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}return _exports.default=class{constructor(editor,modal){var obj,key,value;value=!1,(key="stopRequested")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.ready=!1,this.checkAndWarnAboutBrowserCompatibility()&&(this.editor=editor,this.config=(0,_options.getData)(editor).params,this.modal=modal,this.modalRoot=modal.getRoot()[0],this.startStopButton=this.modalRoot.querySelector('button[data-action="startstop"]'),this.uploadButton=this.modalRoot.querySelector('button[data-action="upload"]'),this.setRecordButtonState(!1),this.player=this.configurePlayer(),this.registerEventListeners(),this.ready=!0,this.captureUserMedia(),this.prefetchContent())}isReady(){return this.ready}configurePlayer(){throw new Error("configurePlayer() must be implemented in ".concat(this.constructor.name))}getSupportedTypes(){throw new Error("getSupportedTypes() must be implemented in ".concat(this.constructor.name))}getRecordingOptions(){throw new Error("getRecordingOptions() must be implemented in ".concat(this.constructor.name))}getFileName(prefix){throw new Error("getFileName() must be implemented in ".concat(this.constructor.name))}getMediaConstraints(){throw new Error("getMediaConstraints() must be implemented in ".concat(this.constructor.name))}playOnCapture(){return!1}getTimeLimit(){throw new Error("getTimeLimit() must be implemented in ".concat(this.constructor.name))}getEmbedTemplateName(){throw new Error("getEmbedTemplateName() must be implemented in ".concat(this.constructor.name))}static getModalClass(){throw new Error("getModalClass() must be implemented in ".concat(this.constructor.name))}getParsedRecordingOptions(){const compatTypes=this.getSupportedTypes().reduce(((result,type)=>(result.push(type),result.push(type.replace("=",":")),result)),[]).filter((type=>window.MediaRecorder.isTypeSupported(type))),options=this.getRecordingOptions();return 0!==compatTypes.length&&(options.mimeType=compatTypes[0]),window.console.info("Selected codec ".concat(options.mimeType," from ").concat(compatTypes.length," options."),compatTypes),options}async captureUserMedia(){try{const stream=await navigator.mediaDevices.getUserMedia(this.getMediaConstraints());this.handleCaptureSuccess(stream)}catch(error){this.handleCaptureFailure(error)}}prefetchContent(){(0,_prefetch.prefetchStrings)(_common.component,["uploading","recordagain_title","recordagain_desc","discard_title","discard_desc","confirm_yes","recordinguploaded","maxfilesizehit","maxfilesizehit_title","uploadfailed"]),(0,_prefetch.prefetchTemplates)([this.getEmbedTemplateName(),"tiny_recordrtc/timeremaining"])}async displayAlert(title,content){const pendingPromise=new _pending.default("core/confirm:alert"),ModalFactory=await("function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require(["core/modal_factory"],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require("core/modal_factory")):Promise.resolve(_systemImportTransformerGlobalIdentifier["core/modal_factory"])),modal=await ModalFactory.create({type:ModalFactory.types.ALERT,title:title,body:content,removeOnClose:!0});return modal.show(),pendingPromise.resolve(),modal}handleCaptureSuccess(stream){this.player.srcObject=stream,this.playOnCapture()&&(this.player.muted=!0,this.player.play()),this.stream=stream,this.setupPlayerSource(),this.setRecordButtonState(!0)}setupPlayerSource(){this.player.srcObject||(this.player.srcObject=this.stream,this.player.muted=!0,this.player.play())}setRecordButtonState(enabled){this.startStopButton.disabled=!enabled}setRecordButtonVisibility(visible){this.getButtonContainer("start-stop").classList.toggle("hide",!visible)}setUploadButtonState(enabled){this.uploadButton.disabled=!enabled}setUploadButtonVisibility(visible){this.getButtonContainer("upload").classList.toggle("hide",!visible)}handleCaptureFailure(error){var subject="gum".concat(error.name.replace("Error","").toLowerCase());this.displayAlert((0,_str.getString)("".concat(subject,"_title"),_common.component),(0,_str.getString)(subject,_common.component))}close(){this.modal.hide()}registerEventListeners(){this.modalRoot.addEventListener("click",this.handleModalClick.bind(this)),this.modal.getRoot().on(ModalEvents.outsideClick,this.outsideClickHandler.bind(this)),this.modal.getRoot().on(ModalEvents.hidden,(()=>{this.cleanupStream(),this.requestRecordingStop()}))}async outsideClickHandler(event){if(this.isRecording())event.preventDefault();else if(this.hasData()){event.preventDefault();try{await(0,_notification.saveCancelPromise)(await(0,_str.getString)("discard_title",_common.component),await(0,_str.getString)("discard_desc",_common.component),await(0,_str.getString)("confirm_yes",_common.component)),this.modal.hide()}catch(error){}}}handleModalClick(event){const button=event.target.closest("button");if(button&&button.dataset.action){const action=button.dataset.action;"startstop"===action&&this.handleRecordingStartStopRequested(),"upload"===action&&this.uploadRecording()}}handleRecordingStartStopRequested(){var _this$mediaRecorder;"recording"===(null===(_this$mediaRecorder=this.mediaRecorder)||void 0===_this$mediaRecorder?void 0:_this$mediaRecorder.state)?this.requestRecordingStop():this.startRecording()}async onMediaStopped(){var _this$getButtonContai;this.blob=new Blob(this.data.chunks,{type:this.mediaRecorder.mimeType}),this.player.srcObject=null,this.player.src=URL.createObjectURL(this.blob),this.setRecordButtonTextFromString("recordagain"),this.player.muted=!1,this.player.controls=!0,null===(_this$getButtonContai=this.getButtonContainer("player"))||void 0===_this$getButtonContai||_this$getButtonContai.classList.toggle("hide",!1),this.setUploadButtonVisibility(!0),this.setUploadButtonState(!0)}async uploadRecording(){if(0===this.data.chunks.length)return void this.displayAlert("norecordingfound");const fileName=this.getFileName((1e3*Math.random()).toString().replace(".",""));try{this.setRecordButtonVisibility(!1),this.setUploadButtonState(!1);const fileURL=await(0,_uploader.default)(this.editor,"media",this.blob,fileName,(progress=>{this.setUploadButtonTextProgress(progress)}));this.insertMedia(fileURL),this.close(),(0,_toast.add)(await(0,_str.getString)("recordinguploaded",_common.component))}catch(error){this.setUploadButtonState(!0),(0,_toast.add)(await(0,_str.getString)("uploadfailed",_common.component,{error:error}),{type:"error"})}}getButtonContainer(purpose){return this.modalRoot.querySelector('[data-purpose="'.concat(purpose,'-container"]'))}static isBrowserCompatible(){return this.checkSecure()&&this.hasUserMedia()}static async display(editor){const ModalClass=this.getModalClass(),modal=await ModalFactory.create({type:ModalClass.TYPE,templateContext:{},large:!0});return new this(editor,modal).isReady()&&modal.show(),modal}checkAndWarnAboutBrowserCompatibility(){return this.constructor.checkSecure()?!!this.constructor.hasUserMedia||((0,_str.getStrings)(["nowebrtc_title","nowebrtc"].map((key=>({key:key,component:_common.component})))).then((_ref2=>{let[title,message]=_ref2;return(0,_toast.add)(message,{title:title,type:"error"})})).catch(),!1):((0,_str.getStrings)(["insecurealert_title","insecurealert"].map((key=>({key:key,component:_common.component})))).then((_ref=>{let[title,message]=_ref;return(0,_toast.add)(message,{title:title,type:"error"})})).catch(),!1)}static hasUserMedia(){return navigator.mediaDevices&&window.MediaRecorder}static checkSecure(){return window.isSecureContext}async setStopRecordingButton(){const{html:html,js:js}=await Templates.renderForPromise("tiny_recordrtc/timeremaining",this.getTimeRemaining());Templates.replaceNodeContents(this.startStopButton,html,js),this.buttonTimer=setInterval(this.updateRecordButtonTime.bind(this),500)}updateRecordButtonTime(){const{remaining:remaining,minutes:minutes,seconds:seconds}=this.getTimeRemaining();remaining<0?this.requestRecordingStop():(this.startStopButton.querySelector('[data-type="minutes"]').textContent=minutes,this.startStopButton.querySelector('[data-type="seconds"]').textContent=seconds)}async setRecordButtonTextFromString(string){this.startStopButton.textContent=await(0,_str.getString)(string,_common.component)}async setUploadButtonTextProgress(progress){this.uploadButton.textContent=await(0,_str.getString)("uploading",_common.component,{progress:Math.round(100*progress)/100})}async resetUploadButtonText(){this.uploadButton.textContent=await(0,_str.getString)("upload",_common.component)}clearButtonTimer(){this.buttonTimer&&clearInterval(this.buttonTimer),this.buttonTimer=null}getTimeRemaining(){const now=(new Date).getTime(),remaining=Math.floor(this.getTimeLimit()-(now-this.startTime)/1e3),formatter=new Intl.NumberFormat(navigator.language,{minimumIntegerDigits:2}),seconds=formatter.format(remaining%60);return{remaining:remaining,minutes:formatter.format(Math.floor((remaining-seconds)/60)),seconds:seconds}}getMaxUploadSize(){return this.config.maxrecsize}requestRecordingStop(){this.mediaRecorder&&"inactive"!==this.mediaRecorder.state?this.stopRequested=!0:this.cleanupStream()}stopRecorder(){this.mediaRecorder.stop(),this.player.muted=!1}cleanupStream(){this.stream&&this.stream.getTracks().filter((track=>"ended"!==track.readyState)).forEach((track=>track.stop()))}handleStopped(){this.onMediaStopped(),this.clearButtonTimer()}handleStarted(){this.startTime=(new Date).getTime(),this.setStopRecordingButton()}handleDataAvailable(event){if(this.isRecording()){const newSize=this.data.blobSize+event.data.size;newSize>=this.getMaxUploadSize()?(this.stopRecorder(),this.displayFileLimitHitMessage()):(this.data.chunks.push(event.data),this.data.blobSize=newSize,this.stopRequested&&this.stopRecorder())}}async displayFileLimitHitMessage(){(0,_toast.add)(await(0,_str.getString)("maxfilesizehit",_common.component),{title:await(0,_str.getString)("maxfilesizehit_title",_common.component),type:"error"})}isRecording(){var _this$mediaRecorder2;return"recording"===(null===(_this$mediaRecorder2=this.mediaRecorder)||void 0===_this$mediaRecorder2?void 0:_this$mediaRecorder2.state)}hasData(){var _this$data;return!(null===(_this$data=this.data)||void 0===_this$data||!_this$data.blobSize)}async startRecording(){if(this.mediaRecorder){if(this.isRecording()&&this.mediaRecorder.stop(),this.hasData()){if(!await this.recordAgainConfirmation())return;this.setUploadButtonVisibility(!1)}this.mediaRecorder=null}this.mediaRecorder=new MediaRecorder(this.stream,this.getParsedRecordingOptions()),this.mediaRecorder.addEventListener("dataavailable",this.handleDataAvailable.bind(this)),this.mediaRecorder.addEventListener("stop",this.handleStopped.bind(this)),this.mediaRecorder.addEventListener("start",this.handleStarted.bind(this)),this.data={chunks:[],blobSize:0},this.setupPlayerSource(),this.stopRequested=!1,this.mediaRecorder.start(50)}async recordAgainConfirmation(){try{return await(0,_notification.saveCancelPromise)(await(0,_str.getString)("recordagain_title",_common.component),await(0,_str.getString)("recordagain_desc",_common.component),await(0,_str.getString)("confirm_yes",_common.component)),!0}catch{return!1}}async insertMedia(source){const{html:html}=await Templates.renderForPromise(this.getEmbedTemplateName(),this.getEmbedTemplateContext({source:source}));this.editor.insertContent(html)}getEmbedTemplateContext(templateContext){return templateContext}},_exports.default})); //# sourceMappingURL=base_recorder.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/recordrtc/amd/build/base_recorder.min.js.map b/lib/editor/tiny/plugins/recordrtc/amd/build/base_recorder.min.js.map index f018a94b00f..a2fbe20c649 100644 --- a/lib/editor/tiny/plugins/recordrtc/amd/build/base_recorder.min.js.map +++ b/lib/editor/tiny/plugins/recordrtc/amd/build/base_recorder.min.js.map @@ -1 +1 @@ -{"version":3,"file":"base_recorder.min.js","sources":["../src/base_recorder.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/**\n * Tiny Record RTC type.\n *\n * @module tiny_recordrtc/base_recorder\n * @copyright 2022 Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString, get_strings as getStrings} from 'core/str';\nimport {component} from './common';\nimport Pending from 'core/pending';\nimport {getData} from './options';\nimport uploadFile from 'editor_tiny/uploader';\nimport {add as addToast} from 'core/toast';\nimport * as ModalEvents from 'core/modal_events';\nimport * as ModalFactory from 'core/modal_factory';\nimport * as Templates from 'core/templates';\nimport {saveCancelPromise} from 'core/notification';\nimport {prefetchStrings, prefetchTemplates} from 'core/prefetch';\n\n/**\n * The RecordRTC base class for audio, video, and any other future types\n */\nexport default class {\n\n stopRequested = false;\n\n /**\n * Constructor for the RecordRTC class\n *\n * @param {TinyMCE} editor The Editor to which the content will be inserted\n * @param {Modal} modal The Moodle Modal that contains the interface used for recording\n */\n constructor(editor, modal) {\n this.ready = false;\n\n if (!this.checkAndWarnAboutBrowserCompatibility()) {\n return;\n }\n\n this.editor = editor;\n this.config = getData(editor).params;\n this.modal = modal;\n this.modalRoot = modal.getRoot()[0];\n this.startStopButton = this.modalRoot.querySelector('button[data-action=\"startstop\"]');\n this.uploadButton = this.modalRoot.querySelector('button[data-action=\"upload\"]');\n\n // Disable the record button untilt he stream is acquired.\n this.setRecordButtonState(false);\n\n this.player = this.configurePlayer();\n this.registerEventListeners();\n this.ready = true;\n\n this.captureUserMedia();\n this.prefetchContent();\n }\n\n /**\n * Check whether the browser is compatible.\n *\n * @returns {boolean}\n */\n isReady() {\n return this.ready;\n }\n\n // Disable eslint's valid-jsdoc rule as the following methods are abstract and mnust be overridden by the child class.\n\n /* eslint-disable valid-jsdoc, no-unused-vars */\n\n /**\n * Get the Player element for this type.\n *\n * @returns {HTMLElement} The player element, typically an audio or video tag.\n */\n configurePlayer() {\n throw new Error(`configurePlayer() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Get the list of supported mimetypes for this recorder.\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/isTypeSupported}\n *\n * @returns {string[]} The list of supported mimetypes.\n */\n getSupportedTypes() {\n throw new Error(`getSupportedTypes() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Get any recording options passed into the MediaRecorder.\n * Please note that the mimeType will be fetched from {@link getSupportedTypes()}.\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder#options}\n * @returns {Object}\n */\n getRecordingOptions() {\n throw new Error(`getRecordingOptions() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Get a filename for the generated file.\n *\n * Typically this function will take a prefix and add a type-specific suffix such as the extension to it.\n *\n * @param {string} prefix The prefix for the filename generated by the recorder.\n * @returns {string}\n */\n getFileName(prefix) {\n throw new Error(`getFileName() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Get a list of constraints as required by the getUserMedia() function.\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#constraints}\n *\n * @returns {Object}\n */\n getMediaConstraints() {\n throw new Error(`getMediaConstraints() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Whether to start playing the recording as it is captured.\n * @returns {boolean} Whether to start playing the recording as it is captured.\n */\n playOnCapture() {\n return false;\n }\n\n /**\n * Get the time limit for this recording type.\n *\n * @returns {number} The time limit in seconds.\n */\n getTimeLimit() {\n throw new Error(`getTimeLimit() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Get the name of the template used when embedding the URL in the editor content.\n *\n * @returns {string}\n */\n getEmbedTemplateName() {\n throw new Error(`getEmbedTemplateName() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Fetch the Class of the Modal to be displayed.\n *\n * @returns {Modal}\n */\n static getModalClass() {\n throw new Error(`getModalClass() must be implemented in ${this.constructor.name}`);\n }\n\n /* eslint-enable valid-jsdoc, no-unused-vars */\n\n /**\n * Get the options for the MediaRecorder.\n *\n * @returns {object} The options for the MediaRecorder instance.\n */\n getParsedRecordingOptions() {\n const requestedTypes = this.getSupportedTypes();\n const possibleTypes = requestedTypes.reduce((result, type) => {\n result.push(type);\n // Safari seems to use codecs: instead of codecs=.\n // It is safe to add both, so we do, but we want them to remain in order.\n result.push(type.replace('=', ':'));\n return result;\n }, []);\n\n const compatTypes = possibleTypes.filter((type) => window.MediaRecorder.isTypeSupported(type));\n\n const options = this.getRecordingOptions();\n if (compatTypes.length !== 0) {\n options.mimeType = compatTypes[0];\n }\n window.console.info(\n `Selected codec ${options.mimeType} from ${compatTypes.length} options.`,\n compatTypes,\n );\n\n return options;\n }\n\n /**\n * Start capturing the User Media and handle success or failure of the capture.\n */\n async captureUserMedia() {\n try {\n const stream = await navigator.mediaDevices.getUserMedia(this.getMediaConstraints());\n this.handleCaptureSuccess(stream);\n } catch (error) {\n this.handleCaptureFailure(error);\n }\n }\n\n /**\n * Prefetch some of the content that will be used in the UI.\n *\n * Note: not all of the strings used are pre-fetched.\n * Some of the strings will be fetched because their template is used.\n */\n prefetchContent() {\n prefetchStrings(component, [\n 'uploading',\n 'recordagain_title',\n 'recordagain_desc',\n 'discard_title',\n 'discard_desc',\n 'confirm_yes',\n 'recordinguploaded',\n 'maxfilesizehit',\n 'maxfilesizehit_title',\n 'uploadfailed',\n ]);\n\n prefetchTemplates([\n this.getEmbedTemplateName(),\n 'tiny_recordrtc/timeremaining',\n ]);\n }\n\n /**\n * Display an error message to the user.\n *\n * @param {Promise} title The error title\n * @param {Promise} content The error message\n * @returns {Promise}\n */\n async displayAlert(title, content) {\n const pendingPromise = new Pending('core/confirm:alert');\n const ModalFactory = await import('core/modal_factory');\n const modal = await ModalFactory.create({\n type: ModalFactory.types.ALERT,\n title: title,\n body: content,\n removeOnClose: true,\n });\n\n modal.show();\n pendingPromise.resolve();\n\n return modal;\n }\n\n /**\n * Handle successful capture of the User Media.\n *\n * @param {MediaStream} stream The stream as captured by the User Media.\n */\n handleCaptureSuccess(stream) {\n // Set audio player source to microphone stream.\n this.player.srcObject = stream;\n\n if (this.playOnCapture()) {\n // Mute audio, distracting while recording.\n this.player.muted = true;\n\n this.player.play();\n }\n\n this.stream = stream;\n this.setupPlayerSource();\n this.setRecordButtonState(true);\n }\n\n /**\n * Setup the player to use the stream as a source.\n */\n setupPlayerSource() {\n if (!this.player.srcObject) {\n this.player.srcObject = this.stream;\n\n // Mute audio, distracting while recording.\n this.player.muted = true;\n\n this.player.play();\n }\n }\n\n /**\n * Enable the record button.\n *\n * @param {boolean|null} enabled Set the button state\n */\n setRecordButtonState(enabled) {\n this.startStopButton.disabled = !enabled;\n }\n\n /**\n * Configure button visibility for the record button.\n *\n * @param {boolean} visible Set the visibility of the button.\n */\n setRecordButtonVisibility(visible) {\n const container = this.getButtonContainer('start-stop');\n container.classList.toggle('hide', !visible);\n }\n\n /**\n * Enable the upload button.\n *\n * @param {boolean|null} enabled Set the button state\n */\n setUploadButtonState(enabled) {\n this.uploadButton.disabled = !enabled;\n }\n\n /**\n * Configure button visibility for the upload button.\n *\n * @param {boolean} visible Set the visibility of the button.\n */\n setUploadButtonVisibility(visible) {\n const container = this.getButtonContainer('upload');\n container.classList.toggle('hide', !visible);\n }\n /**\n * Handle failure to capture the User Media.\n *\n * @param {Error} error\n */\n handleCaptureFailure(error) {\n // Changes 'CertainError' -> 'gumcertain' to match language string names.\n var subject = `gum${error.name.replace('Error', '').toLowerCase()}`;\n this.displayAlert(\n getString(`${subject}_title`, component),\n getString(subject, component)\n );\n }\n\n /**\n * Close the modal and stop recording.\n */\n close() {\n // Closing the modal will destroy it and remove it from the DOM.\n // It will also stop the recording via the hidden Modal Event.\n this.modal.hide();\n }\n\n /**\n * Register event listeners for the modal.\n */\n registerEventListeners() {\n this.modalRoot.addEventListener('click', this.handleModalClick.bind(this));\n this.modal.getRoot().on(ModalEvents.outsideClick, this.outsideClickHandler.bind(this));\n this.modal.getRoot().on(ModalEvents.hidden, () => {\n this.cleanupStream();\n this.requestRecordingStop();\n });\n }\n\n /**\n * Prevent the Modal from closing when recording is on process.\n *\n * @param {MouseEvent} event The click event\n */\n async outsideClickHandler(event) {\n if (this.isRecording()) {\n // The user is recording.\n // Do not distract with a confirmation, just prevent closing.\n event.preventDefault();\n } else if (this.hasData()) {\n // If there is a blobsize then there is data that may be lost.\n // Ask the user to confirm they want to close the modal.\n // We prevent default here, and then close the modal if they confirm.\n event.preventDefault();\n\n try {\n await saveCancelPromise(\n await getString(\"discard_title\", component),\n await getString(\"discard_desc\", component),\n await getString(\"confirm_yes\", component),\n );\n this.modal.hide();\n } catch (error) {\n // Do nothing, the modal will not close.\n }\n }\n }\n\n /**\n * Handle a click within the Modal.\n *\n * @param {MouseEvent} event The click event\n */\n handleModalClick(event) {\n const button = event.target.closest('button');\n if (button && button.dataset.action) {\n const action = button.dataset.action;\n if (action === 'startstop') {\n this.handleRecordingStartStopRequested();\n }\n\n if (action === 'upload') {\n this.uploadRecording();\n }\n }\n }\n\n /**\n * Handle the click event for the recording start/stop button.\n */\n handleRecordingStartStopRequested() {\n if (this.mediaRecorder?.state === 'recording') {\n this.requestRecordingStop();\n } else {\n this.startRecording();\n }\n }\n\n /**\n * Handle the media stream after it has finished.\n */\n async onMediaStopped() {\n // Set source of audio player.\n this.blob = new Blob(this.data.chunks, {\n type: this.mediaRecorder.mimeType\n });\n this.player.srcObject = null;\n this.player.src = URL.createObjectURL(this.blob);\n\n // Change the label to \"Record again\".\n this.setRecordButtonTextFromString('recordagain');\n\n // Show audio player with controls enabled, and unmute.\n this.player.muted = false;\n this.player.controls = true;\n this.getButtonContainer('player')?.classList.toggle('hide', false);\n\n // Show upload button.\n this.setUploadButtonVisibility(true);\n this.setUploadButtonState(true);\n }\n\n /**\n * Upload the recording and insert it into the editor content.\n */\n async uploadRecording() {\n // Trigger error if no recording has been made.\n if (this.data.chunks.length === 0) {\n this.displayAlert('norecordingfound');\n return;\n }\n\n const fileName = this.getFileName((Math.random() * 1000).toString().replace('.', ''));\n\n // Upload recording to server.\n try {\n // Once uploading starts, do not allow any further changes to the recording.\n this.setRecordButtonVisibility(false);\n\n // Disable the upload button.\n this.setUploadButtonState(false);\n\n // Upload the recording.\n const fileURL = await uploadFile(this.editor, 'media', this.blob, fileName, (progress) => {\n this.setUploadButtonTextProgress(progress);\n });\n this.insertMedia(fileURL);\n this.close();\n addToast(await getString('recordinguploaded', component));\n } catch (error) {\n // Show a toast and unhide the button.\n this.setUploadButtonState(true);\n\n addToast(await getString('uploadfailed', component, {error}), {\n type: 'error',\n });\n\n }\n }\n\n /**\n * Helper to get the container that a button is in.\n *\n * @param {string} purpose The button purpose\n * @returns {HTMLElement}\n */\n getButtonContainer(purpose) {\n return this.modalRoot.querySelector(`[data-purpose=\"${purpose}-container\"]`);\n }\n\n /**\n * Check whether the browser is compatible with capturing media.\n *\n * @returns {boolean}\n */\n static isBrowserCompatible() {\n return this.checkSecure() && this.hasUserMedia();\n }\n\n static async display(editor) {\n const ModalClass = this.getModalClass();\n const modal = await ModalFactory.create({\n type: ModalClass.TYPE,\n templateContext: {},\n large: true,\n });\n\n // Set up the VideoRecorder.\n const recorder = new this(editor, modal);\n if (recorder.isReady()) {\n modal.show();\n }\n return modal;\n }\n\n /**\n * Check whether the browser is compatible with capturing media, and display a warning if not.\n *\n * @returns {boolean}\n */\n checkAndWarnAboutBrowserCompatibility() {\n if (!this.constructor.checkSecure()) {\n getStrings(['insecurealert_title', 'insecurealert'].map((key) => ({key, component})))\n .then(([title, message]) => addToast(message, {title, type: 'error'}))\n .catch();\n return false;\n }\n\n if (!this.constructor.hasUserMedia) {\n getStrings(['nowebrtc_title', 'nowebrtc'].map((key) => ({key, component})))\n .then(([title, message]) => addToast(message, {title, type: 'error'}))\n .catch();\n return false;\n }\n\n return true;\n }\n\n /**\n * Check whether the browser supports WebRTC.\n *\n * @returns {boolean}\n */\n static hasUserMedia() {\n return (navigator.mediaDevices && window.MediaRecorder);\n }\n\n /**\n * Check whether the hostname is either hosted over SSL, or from a valid localhost hostname.\n *\n * The UserMedia API can only be used in secure contexts as noted.\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#privacy_and_security}\n *\n * @returns {boolean} Whether the plugin can be loaded.\n */\n static checkSecure() {\n // Note: We can now use window.isSecureContext.\n // https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#feature_detection\n // https://developer.mozilla.org/en-US/docs/Web/API/isSecureContext\n return window.isSecureContext;\n }\n\n /**\n * Update the content of the stop recording button timer.\n */\n async setStopRecordingButton() {\n const {html, js} = await Templates.renderForPromise('tiny_recordrtc/timeremaining', this.getTimeRemaining());\n Templates.replaceNodeContents(this.startStopButton, html, js);\n this.buttonTimer = setInterval(this.updateRecordButtonTime.bind(this), 500);\n }\n\n /**\n * Update the time on the stop recording button.\n */\n updateRecordButtonTime() {\n const {remaining, minutes, seconds} = this.getTimeRemaining();\n if (remaining < 0) {\n this.requestRecordingStop();\n } else {\n this.startStopButton.querySelector('[data-type=\"minutes\"]').textContent = minutes;\n this.startStopButton.querySelector('[data-type=\"seconds\"]').textContent = seconds;\n }\n }\n\n /**\n * Set the text of the record button using a language string.\n *\n * @param {string} string The string identifier\n */\n async setRecordButtonTextFromString(string) {\n this.startStopButton.textContent = await getString(string, component);\n }\n\n /**\n * Set the upload button text progress.\n *\n * @param {number} progress The progress\n */\n async setUploadButtonTextProgress(progress) {\n this.uploadButton.textContent = await getString('uploading', component, {\n progress: Math.round(progress * 100) / 100,\n });\n }\n\n async resetUploadButtonText() {\n this.uploadButton.textContent = await getString('upload', component);\n }\n\n /**\n * Clear the timer for the stop recording button.\n */\n clearButtonTimer() {\n if (this.buttonTimer) {\n clearInterval(this.buttonTimer);\n }\n this.buttonTimer = null;\n }\n\n /**\n * Get the time remaining for the recording.\n *\n * @returns {Object} The minutes and seconds remaining.\n */\n getTimeRemaining() {\n // All times are in milliseconds\n const now = new Date().getTime();\n const remaining = Math.floor(this.getTimeLimit() - ((now - this.startTime) / 1000));\n\n const formatter = new Intl.NumberFormat(navigator.language, {minimumIntegerDigits: 2});\n const seconds = formatter.format(remaining % 60);\n const minutes = formatter.format(Math.floor((remaining - seconds) / 60));\n return {\n remaining,\n minutes,\n seconds,\n };\n }\n\n /**\n * Get the maximum file size that can be uploaded.\n *\n * @returns {number} The max byte size\n */\n getMaxUploadSize() {\n return this.config.maxrecsize;\n }\n\n /**\n * Stop the recording.\n * Please note that this should only stop the recording.\n * Anything related to processing the recording should be handled by the\n * mediaRecorder's stopped event handler which is processed after it has stopped.\n */\n requestRecordingStop() {\n if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {\n this.stopRequested = true;\n } else {\n // There is no recording to stop, but the stream must still be cleaned up.\n this.cleanupStream();\n }\n }\n\n stopRecorder() {\n this.mediaRecorder.stop();\n\n // Unmute the player so that the audio is heard during playback.\n this.player.muted = false;\n }\n\n /**\n * Clean up the stream.\n *\n * This involves stopping any track which is still active.\n */\n cleanupStream() {\n if (this.stream) {\n this.stream.getTracks()\n .filter((track) => track.readyState !== 'ended')\n .forEach((track) => track.stop());\n }\n }\n\n /**\n * Handle the mediaRecorder `stop` event.\n */\n handleStopped() {\n // Handle the stream data.\n this.onMediaStopped();\n\n // Clear the button timer.\n this.clearButtonTimer();\n }\n\n /**\n * Handle the mediaRecorder `start` event.\n *\n * This event is called when the recording starts.\n */\n handleStarted() {\n this.startTime = new Date().getTime();\n this.setStopRecordingButton();\n }\n\n /**\n * Handle the mediaRecorder `dataavailable` event.\n *\n * @param {Event} event\n */\n handleDataAvailable(event) {\n if (this.isRecording()) {\n const newSize = this.data.blobSize + event.data.size;\n // Recording stops when either the maximum upload size is reached, or the time limit expires.\n // The time limit is checked in the `updateButtonTime` function.\n if (newSize >= this.getMaxUploadSize()) {\n this.stopRecorder();\n this.displayFileLimitHitMessage();\n } else {\n // Push recording slice to array.\n this.data.chunks.push(event.data);\n\n // Size of all recorded data so far.\n this.data.blobSize = newSize;\n\n if (this.stopRequested) {\n this.stopRecorder();\n }\n }\n }\n }\n\n async displayFileLimitHitMessage() {\n addToast(await getString('maxfilesizehit', component), {\n title: await getString('maxfilesizehit_title', component),\n type: 'error',\n });\n }\n\n /**\n * Check whether the recording is in progress.\n *\n * @returns {boolean}\n */\n isRecording() {\n return this.mediaRecorder?.state === 'recording';\n }\n\n /**\n * Whether any data has been recorded.\n *\n * @returns {boolean}\n */\n hasData() {\n return !!this.data?.blobSize;\n }\n\n /**\n * Start the recording\n */\n async startRecording() {\n if (this.mediaRecorder) {\n // Stop the existing recorder if it exists.\n if (this.isRecording()) {\n this.mediaRecorder.stop();\n }\n\n if (this.hasData()) {\n const resetRecording = await this.recordAgainConfirmation();\n if (!resetRecording) {\n // User cancelled at the confirmation to reset the data, so exit early.\n return;\n }\n this.setUploadButtonVisibility(false);\n }\n\n this.mediaRecorder = null;\n }\n\n // The options for the recording codecs and bitrates.\n this.mediaRecorder = new MediaRecorder(this.stream, this.getParsedRecordingOptions());\n\n this.mediaRecorder.addEventListener('dataavailable', this.handleDataAvailable.bind(this));\n this.mediaRecorder.addEventListener('stop', this.handleStopped.bind(this));\n this.mediaRecorder.addEventListener('start', this.handleStarted.bind(this));\n\n this.data = {\n chunks: [],\n blobSize: 0\n };\n this.setupPlayerSource();\n this.stopRequested = false;\n\n // Capture in 50ms chunks.\n this.mediaRecorder.start(50);\n }\n\n /**\n * Confirm whether the user wants to reset the existing recoring.\n *\n * @returns {Promise} Whether the user confirmed the reset.\n */\n async recordAgainConfirmation() {\n try {\n await saveCancelPromise(\n await getString(\"recordagain_title\", component),\n await getString(\"recordagain_desc\", component),\n await getString(\"confirm_yes\", component)\n );\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Insert the HTML to embed the recording into the editor content.\n *\n * @param {string} source The URL to view the media.\n */\n async insertMedia(source) {\n const {html} = await Templates.renderForPromise(\n this.getEmbedTemplateName(),\n this.getEmbedTemplateContext({\n source,\n })\n );\n this.editor.insertContent(html);\n }\n\n /**\n * Add or modify the template parameters for the specified type.\n *\n * @param {Object} templateContext The Tempalte context to use\n * @returns {Object} The finalised template context\n */\n getEmbedTemplateContext(templateContext) {\n return templateContext;\n }\n}\n"],"names":["constructor","editor","modal","ready","this","checkAndWarnAboutBrowserCompatibility","config","params","modalRoot","getRoot","startStopButton","querySelector","uploadButton","setRecordButtonState","player","configurePlayer","registerEventListeners","captureUserMedia","prefetchContent","isReady","Error","name","getSupportedTypes","getRecordingOptions","getFileName","prefix","getMediaConstraints","playOnCapture","getTimeLimit","getEmbedTemplateName","getParsedRecordingOptions","compatTypes","reduce","result","type","push","replace","filter","window","MediaRecorder","isTypeSupported","options","length","mimeType","console","info","stream","navigator","mediaDevices","getUserMedia","handleCaptureSuccess","error","handleCaptureFailure","component","title","content","pendingPromise","Pending","ModalFactory","create","types","ALERT","body","removeOnClose","show","resolve","srcObject","muted","play","setupPlayerSource","enabled","disabled","setRecordButtonVisibility","visible","getButtonContainer","classList","toggle","setUploadButtonState","setUploadButtonVisibility","subject","toLowerCase","displayAlert","close","hide","addEventListener","handleModalClick","bind","on","ModalEvents","outsideClick","outsideClickHandler","hidden","cleanupStream","requestRecordingStop","event","isRecording","preventDefault","hasData","button","target","closest","dataset","action","handleRecordingStartStopRequested","uploadRecording","mediaRecorder","state","startRecording","blob","Blob","data","chunks","src","URL","createObjectURL","setRecordButtonTextFromString","controls","fileName","Math","random","toString","fileURL","progress","setUploadButtonTextProgress","insertMedia","purpose","checkSecure","hasUserMedia","ModalClass","getModalClass","TYPE","templateContext","large","map","key","then","_ref2","message","catch","_ref","isSecureContext","html","js","Templates","renderForPromise","getTimeRemaining","replaceNodeContents","buttonTimer","setInterval","updateRecordButtonTime","remaining","minutes","seconds","textContent","string","round","clearButtonTimer","clearInterval","now","Date","getTime","floor","startTime","formatter","Intl","NumberFormat","language","minimumIntegerDigits","format","getMaxUploadSize","maxrecsize","stopRequested","stopRecorder","stop","getTracks","track","readyState","forEach","handleStopped","onMediaStopped","handleStarted","setStopRecordingButton","handleDataAvailable","newSize","blobSize","size","displayFileLimitHitMessage","_this$data","recordAgainConfirmation","start","source","getEmbedTemplateContext","insertContent"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"base_recorder.min.js","sources":["../src/base_recorder.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/**\n * Tiny Record RTC type.\n *\n * @module tiny_recordrtc/base_recorder\n * @copyright 2022 Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getString, getStrings} from 'core/str';\nimport {component} from './common';\nimport Pending from 'core/pending';\nimport {getData} from './options';\nimport uploadFile from 'editor_tiny/uploader';\nimport {add as addToast} from 'core/toast';\nimport * as ModalEvents from 'core/modal_events';\nimport * as ModalFactory from 'core/modal_factory';\nimport * as Templates from 'core/templates';\nimport {saveCancelPromise} from 'core/notification';\nimport {prefetchStrings, prefetchTemplates} from 'core/prefetch';\n\n/**\n * The RecordRTC base class for audio, video, and any other future types\n */\nexport default class {\n\n stopRequested = false;\n\n /**\n * Constructor for the RecordRTC class\n *\n * @param {TinyMCE} editor The Editor to which the content will be inserted\n * @param {Modal} modal The Moodle Modal that contains the interface used for recording\n */\n constructor(editor, modal) {\n this.ready = false;\n\n if (!this.checkAndWarnAboutBrowserCompatibility()) {\n return;\n }\n\n this.editor = editor;\n this.config = getData(editor).params;\n this.modal = modal;\n this.modalRoot = modal.getRoot()[0];\n this.startStopButton = this.modalRoot.querySelector('button[data-action=\"startstop\"]');\n this.uploadButton = this.modalRoot.querySelector('button[data-action=\"upload\"]');\n\n // Disable the record button untilt he stream is acquired.\n this.setRecordButtonState(false);\n\n this.player = this.configurePlayer();\n this.registerEventListeners();\n this.ready = true;\n\n this.captureUserMedia();\n this.prefetchContent();\n }\n\n /**\n * Check whether the browser is compatible.\n *\n * @returns {boolean}\n */\n isReady() {\n return this.ready;\n }\n\n // Disable eslint's valid-jsdoc rule as the following methods are abstract and mnust be overridden by the child class.\n\n /* eslint-disable valid-jsdoc, no-unused-vars */\n\n /**\n * Get the Player element for this type.\n *\n * @returns {HTMLElement} The player element, typically an audio or video tag.\n */\n configurePlayer() {\n throw new Error(`configurePlayer() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Get the list of supported mimetypes for this recorder.\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/isTypeSupported}\n *\n * @returns {string[]} The list of supported mimetypes.\n */\n getSupportedTypes() {\n throw new Error(`getSupportedTypes() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Get any recording options passed into the MediaRecorder.\n * Please note that the mimeType will be fetched from {@link getSupportedTypes()}.\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder#options}\n * @returns {Object}\n */\n getRecordingOptions() {\n throw new Error(`getRecordingOptions() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Get a filename for the generated file.\n *\n * Typically this function will take a prefix and add a type-specific suffix such as the extension to it.\n *\n * @param {string} prefix The prefix for the filename generated by the recorder.\n * @returns {string}\n */\n getFileName(prefix) {\n throw new Error(`getFileName() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Get a list of constraints as required by the getUserMedia() function.\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#constraints}\n *\n * @returns {Object}\n */\n getMediaConstraints() {\n throw new Error(`getMediaConstraints() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Whether to start playing the recording as it is captured.\n * @returns {boolean} Whether to start playing the recording as it is captured.\n */\n playOnCapture() {\n return false;\n }\n\n /**\n * Get the time limit for this recording type.\n *\n * @returns {number} The time limit in seconds.\n */\n getTimeLimit() {\n throw new Error(`getTimeLimit() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Get the name of the template used when embedding the URL in the editor content.\n *\n * @returns {string}\n */\n getEmbedTemplateName() {\n throw new Error(`getEmbedTemplateName() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Fetch the Class of the Modal to be displayed.\n *\n * @returns {Modal}\n */\n static getModalClass() {\n throw new Error(`getModalClass() must be implemented in ${this.constructor.name}`);\n }\n\n /* eslint-enable valid-jsdoc, no-unused-vars */\n\n /**\n * Get the options for the MediaRecorder.\n *\n * @returns {object} The options for the MediaRecorder instance.\n */\n getParsedRecordingOptions() {\n const requestedTypes = this.getSupportedTypes();\n const possibleTypes = requestedTypes.reduce((result, type) => {\n result.push(type);\n // Safari seems to use codecs: instead of codecs=.\n // It is safe to add both, so we do, but we want them to remain in order.\n result.push(type.replace('=', ':'));\n return result;\n }, []);\n\n const compatTypes = possibleTypes.filter((type) => window.MediaRecorder.isTypeSupported(type));\n\n const options = this.getRecordingOptions();\n if (compatTypes.length !== 0) {\n options.mimeType = compatTypes[0];\n }\n window.console.info(\n `Selected codec ${options.mimeType} from ${compatTypes.length} options.`,\n compatTypes,\n );\n\n return options;\n }\n\n /**\n * Start capturing the User Media and handle success or failure of the capture.\n */\n async captureUserMedia() {\n try {\n const stream = await navigator.mediaDevices.getUserMedia(this.getMediaConstraints());\n this.handleCaptureSuccess(stream);\n } catch (error) {\n this.handleCaptureFailure(error);\n }\n }\n\n /**\n * Prefetch some of the content that will be used in the UI.\n *\n * Note: not all of the strings used are pre-fetched.\n * Some of the strings will be fetched because their template is used.\n */\n prefetchContent() {\n prefetchStrings(component, [\n 'uploading',\n 'recordagain_title',\n 'recordagain_desc',\n 'discard_title',\n 'discard_desc',\n 'confirm_yes',\n 'recordinguploaded',\n 'maxfilesizehit',\n 'maxfilesizehit_title',\n 'uploadfailed',\n ]);\n\n prefetchTemplates([\n this.getEmbedTemplateName(),\n 'tiny_recordrtc/timeremaining',\n ]);\n }\n\n /**\n * Display an error message to the user.\n *\n * @param {Promise} title The error title\n * @param {Promise} content The error message\n * @returns {Promise}\n */\n async displayAlert(title, content) {\n const pendingPromise = new Pending('core/confirm:alert');\n const ModalFactory = await import('core/modal_factory');\n const modal = await ModalFactory.create({\n type: ModalFactory.types.ALERT,\n title: title,\n body: content,\n removeOnClose: true,\n });\n\n modal.show();\n pendingPromise.resolve();\n\n return modal;\n }\n\n /**\n * Handle successful capture of the User Media.\n *\n * @param {MediaStream} stream The stream as captured by the User Media.\n */\n handleCaptureSuccess(stream) {\n // Set audio player source to microphone stream.\n this.player.srcObject = stream;\n\n if (this.playOnCapture()) {\n // Mute audio, distracting while recording.\n this.player.muted = true;\n\n this.player.play();\n }\n\n this.stream = stream;\n this.setupPlayerSource();\n this.setRecordButtonState(true);\n }\n\n /**\n * Setup the player to use the stream as a source.\n */\n setupPlayerSource() {\n if (!this.player.srcObject) {\n this.player.srcObject = this.stream;\n\n // Mute audio, distracting while recording.\n this.player.muted = true;\n\n this.player.play();\n }\n }\n\n /**\n * Enable the record button.\n *\n * @param {boolean|null} enabled Set the button state\n */\n setRecordButtonState(enabled) {\n this.startStopButton.disabled = !enabled;\n }\n\n /**\n * Configure button visibility for the record button.\n *\n * @param {boolean} visible Set the visibility of the button.\n */\n setRecordButtonVisibility(visible) {\n const container = this.getButtonContainer('start-stop');\n container.classList.toggle('hide', !visible);\n }\n\n /**\n * Enable the upload button.\n *\n * @param {boolean|null} enabled Set the button state\n */\n setUploadButtonState(enabled) {\n this.uploadButton.disabled = !enabled;\n }\n\n /**\n * Configure button visibility for the upload button.\n *\n * @param {boolean} visible Set the visibility of the button.\n */\n setUploadButtonVisibility(visible) {\n const container = this.getButtonContainer('upload');\n container.classList.toggle('hide', !visible);\n }\n /**\n * Handle failure to capture the User Media.\n *\n * @param {Error} error\n */\n handleCaptureFailure(error) {\n // Changes 'CertainError' -> 'gumcertain' to match language string names.\n var subject = `gum${error.name.replace('Error', '').toLowerCase()}`;\n this.displayAlert(\n getString(`${subject}_title`, component),\n getString(subject, component)\n );\n }\n\n /**\n * Close the modal and stop recording.\n */\n close() {\n // Closing the modal will destroy it and remove it from the DOM.\n // It will also stop the recording via the hidden Modal Event.\n this.modal.hide();\n }\n\n /**\n * Register event listeners for the modal.\n */\n registerEventListeners() {\n this.modalRoot.addEventListener('click', this.handleModalClick.bind(this));\n this.modal.getRoot().on(ModalEvents.outsideClick, this.outsideClickHandler.bind(this));\n this.modal.getRoot().on(ModalEvents.hidden, () => {\n this.cleanupStream();\n this.requestRecordingStop();\n });\n }\n\n /**\n * Prevent the Modal from closing when recording is on process.\n *\n * @param {MouseEvent} event The click event\n */\n async outsideClickHandler(event) {\n if (this.isRecording()) {\n // The user is recording.\n // Do not distract with a confirmation, just prevent closing.\n event.preventDefault();\n } else if (this.hasData()) {\n // If there is a blobsize then there is data that may be lost.\n // Ask the user to confirm they want to close the modal.\n // We prevent default here, and then close the modal if they confirm.\n event.preventDefault();\n\n try {\n await saveCancelPromise(\n await getString(\"discard_title\", component),\n await getString(\"discard_desc\", component),\n await getString(\"confirm_yes\", component),\n );\n this.modal.hide();\n } catch (error) {\n // Do nothing, the modal will not close.\n }\n }\n }\n\n /**\n * Handle a click within the Modal.\n *\n * @param {MouseEvent} event The click event\n */\n handleModalClick(event) {\n const button = event.target.closest('button');\n if (button && button.dataset.action) {\n const action = button.dataset.action;\n if (action === 'startstop') {\n this.handleRecordingStartStopRequested();\n }\n\n if (action === 'upload') {\n this.uploadRecording();\n }\n }\n }\n\n /**\n * Handle the click event for the recording start/stop button.\n */\n handleRecordingStartStopRequested() {\n if (this.mediaRecorder?.state === 'recording') {\n this.requestRecordingStop();\n } else {\n this.startRecording();\n }\n }\n\n /**\n * Handle the media stream after it has finished.\n */\n async onMediaStopped() {\n // Set source of audio player.\n this.blob = new Blob(this.data.chunks, {\n type: this.mediaRecorder.mimeType\n });\n this.player.srcObject = null;\n this.player.src = URL.createObjectURL(this.blob);\n\n // Change the label to \"Record again\".\n this.setRecordButtonTextFromString('recordagain');\n\n // Show audio player with controls enabled, and unmute.\n this.player.muted = false;\n this.player.controls = true;\n this.getButtonContainer('player')?.classList.toggle('hide', false);\n\n // Show upload button.\n this.setUploadButtonVisibility(true);\n this.setUploadButtonState(true);\n }\n\n /**\n * Upload the recording and insert it into the editor content.\n */\n async uploadRecording() {\n // Trigger error if no recording has been made.\n if (this.data.chunks.length === 0) {\n this.displayAlert('norecordingfound');\n return;\n }\n\n const fileName = this.getFileName((Math.random() * 1000).toString().replace('.', ''));\n\n // Upload recording to server.\n try {\n // Once uploading starts, do not allow any further changes to the recording.\n this.setRecordButtonVisibility(false);\n\n // Disable the upload button.\n this.setUploadButtonState(false);\n\n // Upload the recording.\n const fileURL = await uploadFile(this.editor, 'media', this.blob, fileName, (progress) => {\n this.setUploadButtonTextProgress(progress);\n });\n this.insertMedia(fileURL);\n this.close();\n addToast(await getString('recordinguploaded', component));\n } catch (error) {\n // Show a toast and unhide the button.\n this.setUploadButtonState(true);\n\n addToast(await getString('uploadfailed', component, {error}), {\n type: 'error',\n });\n\n }\n }\n\n /**\n * Helper to get the container that a button is in.\n *\n * @param {string} purpose The button purpose\n * @returns {HTMLElement}\n */\n getButtonContainer(purpose) {\n return this.modalRoot.querySelector(`[data-purpose=\"${purpose}-container\"]`);\n }\n\n /**\n * Check whether the browser is compatible with capturing media.\n *\n * @returns {boolean}\n */\n static isBrowserCompatible() {\n return this.checkSecure() && this.hasUserMedia();\n }\n\n static async display(editor) {\n const ModalClass = this.getModalClass();\n const modal = await ModalFactory.create({\n type: ModalClass.TYPE,\n templateContext: {},\n large: true,\n });\n\n // Set up the VideoRecorder.\n const recorder = new this(editor, modal);\n if (recorder.isReady()) {\n modal.show();\n }\n return modal;\n }\n\n /**\n * Check whether the browser is compatible with capturing media, and display a warning if not.\n *\n * @returns {boolean}\n */\n checkAndWarnAboutBrowserCompatibility() {\n if (!this.constructor.checkSecure()) {\n getStrings(['insecurealert_title', 'insecurealert'].map((key) => ({key, component})))\n .then(([title, message]) => addToast(message, {title, type: 'error'}))\n .catch();\n return false;\n }\n\n if (!this.constructor.hasUserMedia) {\n getStrings(['nowebrtc_title', 'nowebrtc'].map((key) => ({key, component})))\n .then(([title, message]) => addToast(message, {title, type: 'error'}))\n .catch();\n return false;\n }\n\n return true;\n }\n\n /**\n * Check whether the browser supports WebRTC.\n *\n * @returns {boolean}\n */\n static hasUserMedia() {\n return (navigator.mediaDevices && window.MediaRecorder);\n }\n\n /**\n * Check whether the hostname is either hosted over SSL, or from a valid localhost hostname.\n *\n * The UserMedia API can only be used in secure contexts as noted.\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#privacy_and_security}\n *\n * @returns {boolean} Whether the plugin can be loaded.\n */\n static checkSecure() {\n // Note: We can now use window.isSecureContext.\n // https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#feature_detection\n // https://developer.mozilla.org/en-US/docs/Web/API/isSecureContext\n return window.isSecureContext;\n }\n\n /**\n * Update the content of the stop recording button timer.\n */\n async setStopRecordingButton() {\n const {html, js} = await Templates.renderForPromise('tiny_recordrtc/timeremaining', this.getTimeRemaining());\n Templates.replaceNodeContents(this.startStopButton, html, js);\n this.buttonTimer = setInterval(this.updateRecordButtonTime.bind(this), 500);\n }\n\n /**\n * Update the time on the stop recording button.\n */\n updateRecordButtonTime() {\n const {remaining, minutes, seconds} = this.getTimeRemaining();\n if (remaining < 0) {\n this.requestRecordingStop();\n } else {\n this.startStopButton.querySelector('[data-type=\"minutes\"]').textContent = minutes;\n this.startStopButton.querySelector('[data-type=\"seconds\"]').textContent = seconds;\n }\n }\n\n /**\n * Set the text of the record button using a language string.\n *\n * @param {string} string The string identifier\n */\n async setRecordButtonTextFromString(string) {\n this.startStopButton.textContent = await getString(string, component);\n }\n\n /**\n * Set the upload button text progress.\n *\n * @param {number} progress The progress\n */\n async setUploadButtonTextProgress(progress) {\n this.uploadButton.textContent = await getString('uploading', component, {\n progress: Math.round(progress * 100) / 100,\n });\n }\n\n async resetUploadButtonText() {\n this.uploadButton.textContent = await getString('upload', component);\n }\n\n /**\n * Clear the timer for the stop recording button.\n */\n clearButtonTimer() {\n if (this.buttonTimer) {\n clearInterval(this.buttonTimer);\n }\n this.buttonTimer = null;\n }\n\n /**\n * Get the time remaining for the recording.\n *\n * @returns {Object} The minutes and seconds remaining.\n */\n getTimeRemaining() {\n // All times are in milliseconds\n const now = new Date().getTime();\n const remaining = Math.floor(this.getTimeLimit() - ((now - this.startTime) / 1000));\n\n const formatter = new Intl.NumberFormat(navigator.language, {minimumIntegerDigits: 2});\n const seconds = formatter.format(remaining % 60);\n const minutes = formatter.format(Math.floor((remaining - seconds) / 60));\n return {\n remaining,\n minutes,\n seconds,\n };\n }\n\n /**\n * Get the maximum file size that can be uploaded.\n *\n * @returns {number} The max byte size\n */\n getMaxUploadSize() {\n return this.config.maxrecsize;\n }\n\n /**\n * Stop the recording.\n * Please note that this should only stop the recording.\n * Anything related to processing the recording should be handled by the\n * mediaRecorder's stopped event handler which is processed after it has stopped.\n */\n requestRecordingStop() {\n if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {\n this.stopRequested = true;\n } else {\n // There is no recording to stop, but the stream must still be cleaned up.\n this.cleanupStream();\n }\n }\n\n stopRecorder() {\n this.mediaRecorder.stop();\n\n // Unmute the player so that the audio is heard during playback.\n this.player.muted = false;\n }\n\n /**\n * Clean up the stream.\n *\n * This involves stopping any track which is still active.\n */\n cleanupStream() {\n if (this.stream) {\n this.stream.getTracks()\n .filter((track) => track.readyState !== 'ended')\n .forEach((track) => track.stop());\n }\n }\n\n /**\n * Handle the mediaRecorder `stop` event.\n */\n handleStopped() {\n // Handle the stream data.\n this.onMediaStopped();\n\n // Clear the button timer.\n this.clearButtonTimer();\n }\n\n /**\n * Handle the mediaRecorder `start` event.\n *\n * This event is called when the recording starts.\n */\n handleStarted() {\n this.startTime = new Date().getTime();\n this.setStopRecordingButton();\n }\n\n /**\n * Handle the mediaRecorder `dataavailable` event.\n *\n * @param {Event} event\n */\n handleDataAvailable(event) {\n if (this.isRecording()) {\n const newSize = this.data.blobSize + event.data.size;\n // Recording stops when either the maximum upload size is reached, or the time limit expires.\n // The time limit is checked in the `updateButtonTime` function.\n if (newSize >= this.getMaxUploadSize()) {\n this.stopRecorder();\n this.displayFileLimitHitMessage();\n } else {\n // Push recording slice to array.\n this.data.chunks.push(event.data);\n\n // Size of all recorded data so far.\n this.data.blobSize = newSize;\n\n if (this.stopRequested) {\n this.stopRecorder();\n }\n }\n }\n }\n\n async displayFileLimitHitMessage() {\n addToast(await getString('maxfilesizehit', component), {\n title: await getString('maxfilesizehit_title', component),\n type: 'error',\n });\n }\n\n /**\n * Check whether the recording is in progress.\n *\n * @returns {boolean}\n */\n isRecording() {\n return this.mediaRecorder?.state === 'recording';\n }\n\n /**\n * Whether any data has been recorded.\n *\n * @returns {boolean}\n */\n hasData() {\n return !!this.data?.blobSize;\n }\n\n /**\n * Start the recording\n */\n async startRecording() {\n if (this.mediaRecorder) {\n // Stop the existing recorder if it exists.\n if (this.isRecording()) {\n this.mediaRecorder.stop();\n }\n\n if (this.hasData()) {\n const resetRecording = await this.recordAgainConfirmation();\n if (!resetRecording) {\n // User cancelled at the confirmation to reset the data, so exit early.\n return;\n }\n this.setUploadButtonVisibility(false);\n }\n\n this.mediaRecorder = null;\n }\n\n // The options for the recording codecs and bitrates.\n this.mediaRecorder = new MediaRecorder(this.stream, this.getParsedRecordingOptions());\n\n this.mediaRecorder.addEventListener('dataavailable', this.handleDataAvailable.bind(this));\n this.mediaRecorder.addEventListener('stop', this.handleStopped.bind(this));\n this.mediaRecorder.addEventListener('start', this.handleStarted.bind(this));\n\n this.data = {\n chunks: [],\n blobSize: 0\n };\n this.setupPlayerSource();\n this.stopRequested = false;\n\n // Capture in 50ms chunks.\n this.mediaRecorder.start(50);\n }\n\n /**\n * Confirm whether the user wants to reset the existing recoring.\n *\n * @returns {Promise} Whether the user confirmed the reset.\n */\n async recordAgainConfirmation() {\n try {\n await saveCancelPromise(\n await getString(\"recordagain_title\", component),\n await getString(\"recordagain_desc\", component),\n await getString(\"confirm_yes\", component)\n );\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Insert the HTML to embed the recording into the editor content.\n *\n * @param {string} source The URL to view the media.\n */\n async insertMedia(source) {\n const {html} = await Templates.renderForPromise(\n this.getEmbedTemplateName(),\n this.getEmbedTemplateContext({\n source,\n })\n );\n this.editor.insertContent(html);\n }\n\n /**\n * Add or modify the template parameters for the specified type.\n *\n * @param {Object} templateContext The Tempalte context to use\n * @returns {Object} The finalised template context\n */\n getEmbedTemplateContext(templateContext) {\n return templateContext;\n }\n}\n"],"names":["constructor","editor","modal","ready","this","checkAndWarnAboutBrowserCompatibility","config","params","modalRoot","getRoot","startStopButton","querySelector","uploadButton","setRecordButtonState","player","configurePlayer","registerEventListeners","captureUserMedia","prefetchContent","isReady","Error","name","getSupportedTypes","getRecordingOptions","getFileName","prefix","getMediaConstraints","playOnCapture","getTimeLimit","getEmbedTemplateName","getParsedRecordingOptions","compatTypes","reduce","result","type","push","replace","filter","window","MediaRecorder","isTypeSupported","options","length","mimeType","console","info","stream","navigator","mediaDevices","getUserMedia","handleCaptureSuccess","error","handleCaptureFailure","component","title","content","pendingPromise","Pending","ModalFactory","create","types","ALERT","body","removeOnClose","show","resolve","srcObject","muted","play","setupPlayerSource","enabled","disabled","setRecordButtonVisibility","visible","getButtonContainer","classList","toggle","setUploadButtonState","setUploadButtonVisibility","subject","toLowerCase","displayAlert","close","hide","addEventListener","handleModalClick","bind","on","ModalEvents","outsideClick","outsideClickHandler","hidden","cleanupStream","requestRecordingStop","event","isRecording","preventDefault","hasData","button","target","closest","dataset","action","handleRecordingStartStopRequested","uploadRecording","mediaRecorder","state","startRecording","blob","Blob","data","chunks","src","URL","createObjectURL","setRecordButtonTextFromString","controls","fileName","Math","random","toString","fileURL","progress","setUploadButtonTextProgress","insertMedia","purpose","checkSecure","hasUserMedia","ModalClass","getModalClass","TYPE","templateContext","large","map","key","then","_ref2","message","catch","_ref","isSecureContext","html","js","Templates","renderForPromise","getTimeRemaining","replaceNodeContents","buttonTimer","setInterval","updateRecordButtonTime","remaining","minutes","seconds","textContent","string","round","clearButtonTimer","clearInterval","now","Date","getTime","floor","startTime","formatter","Intl","NumberFormat","language","minimumIntegerDigits","format","getMaxUploadSize","maxrecsize","stopRequested","stopRecorder","stop","getTracks","track","readyState","forEach","handleStopped","onMediaStopped","handleStarted","setStopRecordingButton","handleDataAvailable","newSize","blobSize","size","displayFileLimitHitMessage","_this$data","recordAgainConfirmation","start","source","getEmbedTemplateContext","insertContent"],"mappings":"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"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/recordrtc/amd/build/commands_audio.min.js b/lib/editor/tiny/plugins/recordrtc/amd/build/commands_audio.min.js index bc6773dec6f..8ee2ead3dce 100644 --- a/lib/editor/tiny/plugins/recordrtc/amd/build/commands_audio.min.js +++ b/lib/editor/tiny/plugins/recordrtc/amd/build/commands_audio.min.js @@ -5,6 +5,6 @@ define("tiny_recordrtc/commands_audio",["exports","core/str","editor_tiny/utils" * @module tiny_recordrtc/commands_audio * @copyright 2022, Stevani Andolo * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_audio_recorder=(obj=_audio_recorder)&&obj.__esModule?obj:{default:obj};return _exports.default=async()=>{if(!_audio_recorder.default.isBrowserCompatible())return()=>!1;const[audioButtonTitle,audio]=await Promise.all([(0,_str.get_string)("audiobuttontitle",_common.component),(0,_utils.getButtonImage)("audio",_common.component)]);return editor=>{if(!(0,_options.isAudioAllowed)(editor))return;editor.ui.registry.addIcon("audio",audio.html),editor.ui.registry.addButton(_common.audioButtonName,{icon:"audio",tooltip:audioButtonTitle,onAction:()=>_audio_recorder.default.display(editor)}),editor.ui.registry.addMenuItem(_common.audioButtonName,{icon:"audio",text:audioButtonTitle,onAction:()=>_audio_recorder.default.display(editor)})}},_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_audio_recorder=(obj=_audio_recorder)&&obj.__esModule?obj:{default:obj};return _exports.default=async()=>{if(!_audio_recorder.default.isBrowserCompatible())return()=>!1;const[audioButtonTitle,audio]=await Promise.all([(0,_str.getString)("audiobuttontitle",_common.component),(0,_utils.getButtonImage)("audio",_common.component)]);return editor=>{if(!(0,_options.isAudioAllowed)(editor))return;editor.ui.registry.addIcon("audio",audio.html),editor.ui.registry.addButton(_common.audioButtonName,{icon:"audio",tooltip:audioButtonTitle,onAction:()=>_audio_recorder.default.display(editor)}),editor.ui.registry.addMenuItem(_common.audioButtonName,{icon:"audio",text:audioButtonTitle,onAction:()=>_audio_recorder.default.display(editor)})}},_exports.default})); //# sourceMappingURL=commands_audio.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/recordrtc/amd/build/commands_audio.min.js.map b/lib/editor/tiny/plugins/recordrtc/amd/build/commands_audio.min.js.map index 7c23d1a7ba0..8dd9df7218d 100644 --- a/lib/editor/tiny/plugins/recordrtc/amd/build/commands_audio.min.js.map +++ b/lib/editor/tiny/plugins/recordrtc/amd/build/commands_audio.min.js.map @@ -1 +1 @@ -{"version":3,"file":"commands_audio.min.js","sources":["../src/commands_audio.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 * Tiny Record RTC - record audio command.\n *\n * @module tiny_recordrtc/commands_audio\n * @copyright 2022, Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString} from 'core/str';\nimport {getButtonImage} from 'editor_tiny/utils';\nimport {\n audioButtonName,\n component\n} from './common';\nimport Recorder from './audio_recorder';\nimport {isAudioAllowed} from './options';\n\nexport default async() => {\n if (!Recorder.isBrowserCompatible()) {\n // The browser doesn't support the plugin, so just don't show it.\n return () => false;\n }\n\n const [\n audioButtonTitle,\n audio,\n ] = await Promise.all([\n getString('audiobuttontitle', component),\n getButtonImage('audio', component),\n ]);\n\n return (editor) => {\n if (!isAudioAllowed(editor)) {\n return;\n }\n\n const icon = 'audio';\n editor.ui.registry.addIcon(icon, audio.html);\n\n editor.ui.registry.addButton(audioButtonName, {\n icon,\n tooltip: audioButtonTitle,\n onAction: () => Recorder.display(editor),\n });\n\n editor.ui.registry.addMenuItem(audioButtonName, {\n icon,\n text: audioButtonTitle,\n onAction: () => Recorder.display(editor),\n });\n };\n};\n"],"names":["async","Recorder","isBrowserCompatible","audioButtonTitle","audio","Promise","all","component","editor","ui","registry","addIcon","html","addButton","audioButtonName","icon","tooltip","onAction","display","addMenuItem","text"],"mappings":";;;;;;;qLAgCeA,cACNC,wBAASC,4BAEH,KAAM,QAIbC,iBACAC,aACMC,QAAQC,IAAI,EAClB,mBAAU,mBAAoBC,oBAC9B,yBAAe,QAASA,4BAGpBC,cACC,2BAAeA,eAKpBA,OAAOC,GAAGC,SAASC,QADN,QACoBP,MAAMQ,MAEvCJ,OAAOC,GAAGC,SAASG,UAAUC,wBAAiB,CAC1CC,KAJS,QAKTC,QAASb,iBACTc,SAAU,IAAMhB,wBAASiB,QAAQV,UAGrCA,OAAOC,GAAGC,SAASS,YAAYL,wBAAiB,CAC5CC,KAVS,QAWTK,KAAMjB,iBACNc,SAAU,IAAMhB,wBAASiB,QAAQV"} \ No newline at end of file +{"version":3,"file":"commands_audio.min.js","sources":["../src/commands_audio.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 * Tiny Record RTC - record audio command.\n *\n * @module tiny_recordrtc/commands_audio\n * @copyright 2022, Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getString} from 'core/str';\nimport {getButtonImage} from 'editor_tiny/utils';\nimport {\n audioButtonName,\n component\n} from './common';\nimport Recorder from './audio_recorder';\nimport {isAudioAllowed} from './options';\n\nexport default async() => {\n if (!Recorder.isBrowserCompatible()) {\n // The browser doesn't support the plugin, so just don't show it.\n return () => false;\n }\n\n const [\n audioButtonTitle,\n audio,\n ] = await Promise.all([\n getString('audiobuttontitle', component),\n getButtonImage('audio', component),\n ]);\n\n return (editor) => {\n if (!isAudioAllowed(editor)) {\n return;\n }\n\n const icon = 'audio';\n editor.ui.registry.addIcon(icon, audio.html);\n\n editor.ui.registry.addButton(audioButtonName, {\n icon,\n tooltip: audioButtonTitle,\n onAction: () => Recorder.display(editor),\n });\n\n editor.ui.registry.addMenuItem(audioButtonName, {\n icon,\n text: audioButtonTitle,\n onAction: () => Recorder.display(editor),\n });\n };\n};\n"],"names":["async","Recorder","isBrowserCompatible","audioButtonTitle","audio","Promise","all","component","editor","ui","registry","addIcon","html","addButton","audioButtonName","icon","tooltip","onAction","display","addMenuItem","text"],"mappings":";;;;;;;qLAgCeA,cACNC,wBAASC,4BAEH,KAAM,QAIbC,iBACAC,aACMC,QAAQC,IAAI,EAClB,kBAAU,mBAAoBC,oBAC9B,yBAAe,QAASA,4BAGpBC,cACC,2BAAeA,eAKpBA,OAAOC,GAAGC,SAASC,QADN,QACoBP,MAAMQ,MAEvCJ,OAAOC,GAAGC,SAASG,UAAUC,wBAAiB,CAC1CC,KAJS,QAKTC,QAASb,iBACTc,SAAU,IAAMhB,wBAASiB,QAAQV,UAGrCA,OAAOC,GAAGC,SAASS,YAAYL,wBAAiB,CAC5CC,KAVS,QAWTK,KAAMjB,iBACNc,SAAU,IAAMhB,wBAASiB,QAAQV"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/recordrtc/amd/build/commands_video.min.js b/lib/editor/tiny/plugins/recordrtc/amd/build/commands_video.min.js index 84142088dcd..15162a8e0cb 100644 --- a/lib/editor/tiny/plugins/recordrtc/amd/build/commands_video.min.js +++ b/lib/editor/tiny/plugins/recordrtc/amd/build/commands_video.min.js @@ -5,6 +5,6 @@ define("tiny_recordrtc/commands_video",["exports","core/str","editor_tiny/utils" * @module tiny_recordrtc/commands_video * @copyright 2022, Stevani Andolo * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_video_recorder=(obj=_video_recorder)&&obj.__esModule?obj:{default:obj};return _exports.default=async()=>{if(!_video_recorder.default.isBrowserCompatible())return()=>!1;const[videoButtonTitle,buttonImage]=await Promise.all([(0,_str.get_string)("videobuttontitle",_common.component),(0,_utils.getButtonImage)("video",_common.component)]);return editor=>{if(!(0,_options.isVideoAllowed)(editor))return;editor.ui.registry.addIcon("video",buttonImage.html),editor.ui.registry.addButton(_common.videoButtonName,{icon:"video",tooltip:videoButtonTitle,onAction:()=>_video_recorder.default.display(editor)}),editor.ui.registry.addMenuItem(_common.videoButtonName,{icon:"video",text:videoButtonTitle,onAction:()=>_video_recorder.default.display(editor)})}},_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_video_recorder=(obj=_video_recorder)&&obj.__esModule?obj:{default:obj};return _exports.default=async()=>{if(!_video_recorder.default.isBrowserCompatible())return()=>!1;const[videoButtonTitle,buttonImage]=await Promise.all([(0,_str.getString)("videobuttontitle",_common.component),(0,_utils.getButtonImage)("video",_common.component)]);return editor=>{if(!(0,_options.isVideoAllowed)(editor))return;editor.ui.registry.addIcon("video",buttonImage.html),editor.ui.registry.addButton(_common.videoButtonName,{icon:"video",tooltip:videoButtonTitle,onAction:()=>_video_recorder.default.display(editor)}),editor.ui.registry.addMenuItem(_common.videoButtonName,{icon:"video",text:videoButtonTitle,onAction:()=>_video_recorder.default.display(editor)})}},_exports.default})); //# sourceMappingURL=commands_video.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/recordrtc/amd/build/commands_video.min.js.map b/lib/editor/tiny/plugins/recordrtc/amd/build/commands_video.min.js.map index cdfd70dda16..e1944c58fb9 100644 --- a/lib/editor/tiny/plugins/recordrtc/amd/build/commands_video.min.js.map +++ b/lib/editor/tiny/plugins/recordrtc/amd/build/commands_video.min.js.map @@ -1 +1 @@ -{"version":3,"file":"commands_video.min.js","sources":["../src/commands_video.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 * Tiny Record RTC - record video command.\n *\n * @module tiny_recordrtc/commands_video\n * @copyright 2022, Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString} from 'core/str';\nimport {getButtonImage as getVideoIcon} from 'editor_tiny/utils';\nimport {\n videoButtonName,\n component\n} from './common';\nimport {isVideoAllowed} from './options';\nimport Recorder from './video_recorder';\n\nexport default async() => {\n if (!Recorder.isBrowserCompatible()) {\n // The browser doesn't support the plugin, so just don't show it.\n return () => false;\n }\n\n const [\n videoButtonTitle,\n buttonImage,\n ] = await Promise.all([\n getString('videobuttontitle', component),\n getVideoIcon('video', component),\n ]);\n\n return (editor) => {\n if (!isVideoAllowed(editor)) {\n return;\n }\n\n const icon = 'video';\n editor.ui.registry.addIcon(icon, buttonImage.html);\n\n editor.ui.registry.addButton(videoButtonName, {\n icon,\n tooltip: videoButtonTitle,\n onAction: () => Recorder.display(editor),\n });\n\n editor.ui.registry.addMenuItem(videoButtonName, {\n icon,\n text: videoButtonTitle,\n onAction: () => Recorder.display(editor),\n });\n };\n};\n"],"names":["async","Recorder","isBrowserCompatible","videoButtonTitle","buttonImage","Promise","all","component","editor","ui","registry","addIcon","html","addButton","videoButtonName","icon","tooltip","onAction","display","addMenuItem","text"],"mappings":";;;;;;;qLAgCeA,cACNC,wBAASC,4BAEH,KAAM,QAIbC,iBACAC,mBACMC,QAAQC,IAAI,EAClB,mBAAU,mBAAoBC,oBAC9B,yBAAa,QAASA,4BAGlBC,cACC,2BAAeA,eAKpBA,OAAOC,GAAGC,SAASC,QADN,QACoBP,YAAYQ,MAE7CJ,OAAOC,GAAGC,SAASG,UAAUC,wBAAiB,CAC1CC,KAJS,QAKTC,QAASb,iBACTc,SAAU,IAAMhB,wBAASiB,QAAQV,UAGrCA,OAAOC,GAAGC,SAASS,YAAYL,wBAAiB,CAC5CC,KAVS,QAWTK,KAAMjB,iBACNc,SAAU,IAAMhB,wBAASiB,QAAQV"} \ No newline at end of file +{"version":3,"file":"commands_video.min.js","sources":["../src/commands_video.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 * Tiny Record RTC - record video command.\n *\n * @module tiny_recordrtc/commands_video\n * @copyright 2022, Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getString} from 'core/str';\nimport {getButtonImage as getVideoIcon} from 'editor_tiny/utils';\nimport {\n videoButtonName,\n component\n} from './common';\nimport {isVideoAllowed} from './options';\nimport Recorder from './video_recorder';\n\nexport default async() => {\n if (!Recorder.isBrowserCompatible()) {\n // The browser doesn't support the plugin, so just don't show it.\n return () => false;\n }\n\n const [\n videoButtonTitle,\n buttonImage,\n ] = await Promise.all([\n getString('videobuttontitle', component),\n getVideoIcon('video', component),\n ]);\n\n return (editor) => {\n if (!isVideoAllowed(editor)) {\n return;\n }\n\n const icon = 'video';\n editor.ui.registry.addIcon(icon, buttonImage.html);\n\n editor.ui.registry.addButton(videoButtonName, {\n icon,\n tooltip: videoButtonTitle,\n onAction: () => Recorder.display(editor),\n });\n\n editor.ui.registry.addMenuItem(videoButtonName, {\n icon,\n text: videoButtonTitle,\n onAction: () => Recorder.display(editor),\n });\n };\n};\n"],"names":["async","Recorder","isBrowserCompatible","videoButtonTitle","buttonImage","Promise","all","component","editor","ui","registry","addIcon","html","addButton","videoButtonName","icon","tooltip","onAction","display","addMenuItem","text"],"mappings":";;;;;;;qLAgCeA,cACNC,wBAASC,4BAEH,KAAM,QAIbC,iBACAC,mBACMC,QAAQC,IAAI,EAClB,kBAAU,mBAAoBC,oBAC9B,yBAAa,QAASA,4BAGlBC,cACC,2BAAeA,eAKpBA,OAAOC,GAAGC,SAASC,QADN,QACoBP,YAAYQ,MAE7CJ,OAAOC,GAAGC,SAASG,UAAUC,wBAAiB,CAC1CC,KAJS,QAKTC,QAASb,iBACTc,SAAU,IAAMhB,wBAASiB,QAAQV,UAGrCA,OAAOC,GAAGC,SAASS,YAAYL,wBAAiB,CAC5CC,KAVS,QAWTK,KAAMjB,iBACNc,SAAU,IAAMhB,wBAASiB,QAAQV"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/recordrtc/amd/src/base_recorder.js b/lib/editor/tiny/plugins/recordrtc/amd/src/base_recorder.js index adfc9c4ded6..6e45a88296d 100644 --- a/lib/editor/tiny/plugins/recordrtc/amd/src/base_recorder.js +++ b/lib/editor/tiny/plugins/recordrtc/amd/src/base_recorder.js @@ -22,7 +22,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import {get_string as getString, get_strings as getStrings} from 'core/str'; +import {getString, getStrings} from 'core/str'; import {component} from './common'; import Pending from 'core/pending'; import {getData} from './options'; diff --git a/lib/editor/tiny/plugins/recordrtc/amd/src/commands_audio.js b/lib/editor/tiny/plugins/recordrtc/amd/src/commands_audio.js index 249f9fd922b..165ab909c5f 100644 --- a/lib/editor/tiny/plugins/recordrtc/amd/src/commands_audio.js +++ b/lib/editor/tiny/plugins/recordrtc/amd/src/commands_audio.js @@ -21,7 +21,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {getButtonImage} from 'editor_tiny/utils'; import { audioButtonName, diff --git a/lib/editor/tiny/plugins/recordrtc/amd/src/commands_video.js b/lib/editor/tiny/plugins/recordrtc/amd/src/commands_video.js index a16a0a23a54..e76e9522877 100644 --- a/lib/editor/tiny/plugins/recordrtc/amd/src/commands_video.js +++ b/lib/editor/tiny/plugins/recordrtc/amd/src/commands_video.js @@ -21,7 +21,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {getButtonImage as getVideoIcon} from 'editor_tiny/utils'; import { videoButtonName, diff --git a/lib/form/amd/build/changechecker.min.js b/lib/form/amd/build/changechecker.min.js index b0d6e09bfd8..eed0e56e234 100644 --- a/lib/form/amd/build/changechecker.min.js +++ b/lib/form/amd/build/changechecker.min.js @@ -48,6 +48,6 @@ define("core_form/changechecker",["exports","core_editor/events","core/str"],(fu * }); * {{/js}} */ -let warningString,watchedForms=[],formChangeCheckerDisabled=!1;const getFormFromChild=formChild=>formChild.closest("form"),watchForm=formNode=>{(formNode=getFormFromChild(formNode))&&(isWatchingForm(formNode)||watchedForms.push(formNode))};_exports.watchForm=watchForm;_exports.unWatchForm=formNode=>{watchedForms=watchedForms.filter((watchedForm=>!!watchedForm.contains(formNode)))};const resetAllFormDirtyStates=()=>{watchedForms.forEach((watchedForm=>{watchedForm.dataset.formSubmitted="false",watchedForm.dataset.formDirty="false"}))};_exports.resetAllFormDirtyStates=resetAllFormDirtyStates;const resetFormDirtyState=formNode=>{(formNode=getFormFromChild(formNode))&&(formNode.dataset.formSubmitted="false",formNode.dataset.formDirty="false")};_exports.resetFormDirtyState=resetFormDirtyState;const markAllFormsAsDirty=()=>{watchedForms.forEach((watchedForm=>{watchedForm.dataset.formDirty="true"}))};_exports.markAllFormsAsDirty=markAllFormsAsDirty;const markFormAsDirty=formNode=>{(formNode=getFormFromChild(formNode))&&(formNode.dataset.formDirty="true")};_exports.markFormAsDirty=markFormAsDirty;const disableAllChecks=()=>{formChangeCheckerDisabled=!0};_exports.disableAllChecks=disableAllChecks;const isAnyWatchedFormDirty=()=>{if(formChangeCheckerDisabled)return!1;if(watchedForms.some((watchedForm=>"true"===watchedForm.dataset.formSubmitted)))return!1;return!!watchedForms.some((watchedForm=>{if(!watchedForm.isConnected)return!1;if("true"===watchedForm.dataset.formDirty)return!0;if(document.activeElement&&document.activeElement.dataset.propertyIsEnumerable("initialValue")){const isActiveElementWatched=isWatchingForm(document.activeElement)&&!shouldIgnoreChangesForNode(document.activeElement),hasValueChanged=document.activeElement.dataset.initialValue!==document.activeElement.value;if(isActiveElementWatched&&hasValueChanged)return!0}return!1}))||!(void 0===window.tinyMCE||!window.tinyMCE.editors||!window.tinyMCE.editors.some((editor=>editor.isDirty())))};_exports.isAnyWatchedFormDirty=isAnyWatchedFormDirty;const isWatchingForm=target=>watchedForms.some((watchedForm=>watchedForm.contains(target))),shouldIgnoreChangesForNode=target=>!!target.closest(".ignoredirty"),markFormChangedFromNode=changedNode=>{if(changedNode.dataset.formChangeCheckerOverride)return void disableAllChecks();if(!isWatchingForm(changedNode))return;if(shouldIgnoreChangesForNode(changedNode))return;var target;(target=changedNode,watchedForms.find((watchedForm=>watchedForm.contains(target)))).dataset.formDirty="true"};_exports.markFormChangedFromNode=markFormChangedFromNode;const markFormSubmitted=formNode=>{(formNode=getFormFromChild(formNode))&&(formNode.dataset.formSubmitted="true")};_exports.markFormSubmitted=markFormSubmitted;const markAllFormsSubmitted=()=>{watchedForms.forEach((watchedForm=>markFormSubmitted(watchedForm)))};_exports.markAllFormsSubmitted=markAllFormsSubmitted;const beforeUnloadHandler=e=>isAnyWatchedFormDirty()&&!M.cfg.behatsiterunning?(e.preventDefault(),e.returnValue=warningString,e.returnValue):(window.removeEventListener("beforeunload",beforeUnloadHandler),null),startWatching=()=>{addLegacyFunctions(),document.addEventListener("change",(e=>{isWatchingForm(e.target)&&markFormChangedFromNode(e.target)})),document.addEventListener("click",(e=>{if(!e.target.closest("[data-formchangechecker-ignore-submit]"))return;const ownerForm=getFormFromChild(e.target);ownerForm&&(ownerForm.dataset.ignoreSubmission="true")})),document.addEventListener("focusin",(e=>{if(e.target.matches("input, textarea, select")){if(e.target.dataset.propertyIsEnumerable("initialValue"))return;e.target.dataset.initialValue=e.target.value}})),document.addEventListener("submit",(e=>{const formNode=getFormFromChild(e.target);formNode&&(formNode.dataset.ignoreSubmission?formNode.dataset.ignoreSubmission="false":markFormSubmitted(formNode))})),document.addEventListener(_events.eventTypes.editorContentRestored,(e=>{e.target!=document?resetFormDirtyState(e.target):resetAllFormDirtyStates()})),(0,_str.get_string)("changesmadereallygoaway","moodle").then((changesMadeString=>{warningString=changesMadeString})).catch(),window.addEventListener("beforeunload",beforeUnloadHandler)};_exports.startWatching=startWatching;const addLegacyFunctions=()=>{const getLoggedLegacyFallback=(oldFunctionName,newFunctionName,newFunction)=>function(){window.console.warn("The moodle-core-formchangechecker has been deprecated and replaced with core_form/changechecker. "+"The ".concat(oldFunctionName," function has been replaced with ").concat(newFunctionName,".")),newFunction(...arguments)};window.M.core_formchangechecker={init:getLoggedLegacyFallback("init","watchFormById",watchFormById),reset_form_dirty_state:getLoggedLegacyFallback("reset_form_dirty_state","resetFormDirtyState",resetAllFormDirtyStates),set_form_changed:getLoggedLegacyFallback("set_form_changed","markFormAsDirty",markAllFormsAsDirty),set_form_submitted:getLoggedLegacyFallback("set_form_submitted","markFormSubmitted",markAllFormsSubmitted)}},watchFormById=formId=>{watchForm(document.getElementById(formId))};_exports.watchFormById=watchFormById;_exports.resetFormDirtyStateById=formId=>{resetFormDirtyState(document.getElementById(formId))};_exports.markFormAsDirtyById=formId=>{markFormAsDirty(document.getElementById(formId))},startWatching()})); +let warningString,watchedForms=[],formChangeCheckerDisabled=!1;const getFormFromChild=formChild=>formChild.closest("form"),watchForm=formNode=>{(formNode=getFormFromChild(formNode))&&(isWatchingForm(formNode)||watchedForms.push(formNode))};_exports.watchForm=watchForm;_exports.unWatchForm=formNode=>{watchedForms=watchedForms.filter((watchedForm=>!!watchedForm.contains(formNode)))};const resetAllFormDirtyStates=()=>{watchedForms.forEach((watchedForm=>{watchedForm.dataset.formSubmitted="false",watchedForm.dataset.formDirty="false"}))};_exports.resetAllFormDirtyStates=resetAllFormDirtyStates;const resetFormDirtyState=formNode=>{(formNode=getFormFromChild(formNode))&&(formNode.dataset.formSubmitted="false",formNode.dataset.formDirty="false")};_exports.resetFormDirtyState=resetFormDirtyState;const markAllFormsAsDirty=()=>{watchedForms.forEach((watchedForm=>{watchedForm.dataset.formDirty="true"}))};_exports.markAllFormsAsDirty=markAllFormsAsDirty;const markFormAsDirty=formNode=>{(formNode=getFormFromChild(formNode))&&(formNode.dataset.formDirty="true")};_exports.markFormAsDirty=markFormAsDirty;const disableAllChecks=()=>{formChangeCheckerDisabled=!0};_exports.disableAllChecks=disableAllChecks;const isAnyWatchedFormDirty=()=>{if(formChangeCheckerDisabled)return!1;if(watchedForms.some((watchedForm=>"true"===watchedForm.dataset.formSubmitted)))return!1;return!!watchedForms.some((watchedForm=>{if(!watchedForm.isConnected)return!1;if("true"===watchedForm.dataset.formDirty)return!0;if(document.activeElement&&document.activeElement.dataset.propertyIsEnumerable("initialValue")){const isActiveElementWatched=isWatchingForm(document.activeElement)&&!shouldIgnoreChangesForNode(document.activeElement),hasValueChanged=document.activeElement.dataset.initialValue!==document.activeElement.value;if(isActiveElementWatched&&hasValueChanged)return!0}return!1}))||!(void 0===window.tinyMCE||!window.tinyMCE.editors||!window.tinyMCE.editors.some((editor=>editor.isDirty())))};_exports.isAnyWatchedFormDirty=isAnyWatchedFormDirty;const isWatchingForm=target=>watchedForms.some((watchedForm=>watchedForm.contains(target))),shouldIgnoreChangesForNode=target=>!!target.closest(".ignoredirty"),markFormChangedFromNode=changedNode=>{if(changedNode.dataset.formChangeCheckerOverride)return void disableAllChecks();if(!isWatchingForm(changedNode))return;if(shouldIgnoreChangesForNode(changedNode))return;var target;(target=changedNode,watchedForms.find((watchedForm=>watchedForm.contains(target)))).dataset.formDirty="true"};_exports.markFormChangedFromNode=markFormChangedFromNode;const markFormSubmitted=formNode=>{(formNode=getFormFromChild(formNode))&&(formNode.dataset.formSubmitted="true")};_exports.markFormSubmitted=markFormSubmitted;const markAllFormsSubmitted=()=>{watchedForms.forEach((watchedForm=>markFormSubmitted(watchedForm)))};_exports.markAllFormsSubmitted=markAllFormsSubmitted;const beforeUnloadHandler=e=>isAnyWatchedFormDirty()&&!M.cfg.behatsiterunning?(e.preventDefault(),e.returnValue=warningString,e.returnValue):(window.removeEventListener("beforeunload",beforeUnloadHandler),null),startWatching=()=>{addLegacyFunctions(),document.addEventListener("change",(e=>{isWatchingForm(e.target)&&markFormChangedFromNode(e.target)})),document.addEventListener("click",(e=>{if(!e.target.closest("[data-formchangechecker-ignore-submit]"))return;const ownerForm=getFormFromChild(e.target);ownerForm&&(ownerForm.dataset.ignoreSubmission="true")})),document.addEventListener("focusin",(e=>{if(e.target.matches("input, textarea, select")){if(e.target.dataset.propertyIsEnumerable("initialValue"))return;e.target.dataset.initialValue=e.target.value}})),document.addEventListener("submit",(e=>{const formNode=getFormFromChild(e.target);formNode&&(formNode.dataset.ignoreSubmission?formNode.dataset.ignoreSubmission="false":markFormSubmitted(formNode))})),document.addEventListener(_events.eventTypes.editorContentRestored,(e=>{e.target!=document?resetFormDirtyState(e.target):resetAllFormDirtyStates()})),(0,_str.getString)("changesmadereallygoaway","moodle").then((changesMadeString=>{warningString=changesMadeString})).catch(),window.addEventListener("beforeunload",beforeUnloadHandler)};_exports.startWatching=startWatching;const addLegacyFunctions=()=>{const getLoggedLegacyFallback=(oldFunctionName,newFunctionName,newFunction)=>function(){window.console.warn("The moodle-core-formchangechecker has been deprecated and replaced with core_form/changechecker. "+"The ".concat(oldFunctionName," function has been replaced with ").concat(newFunctionName,".")),newFunction(...arguments)};window.M.core_formchangechecker={init:getLoggedLegacyFallback("init","watchFormById",watchFormById),reset_form_dirty_state:getLoggedLegacyFallback("reset_form_dirty_state","resetFormDirtyState",resetAllFormDirtyStates),set_form_changed:getLoggedLegacyFallback("set_form_changed","markFormAsDirty",markAllFormsAsDirty),set_form_submitted:getLoggedLegacyFallback("set_form_submitted","markFormSubmitted",markAllFormsSubmitted)}},watchFormById=formId=>{watchForm(document.getElementById(formId))};_exports.watchFormById=watchFormById;_exports.resetFormDirtyStateById=formId=>{resetFormDirtyState(document.getElementById(formId))};_exports.markFormAsDirtyById=formId=>{markFormAsDirty(document.getElementById(formId))},startWatching()})); //# sourceMappingURL=changechecker.min.js.map \ No newline at end of file diff --git a/lib/form/amd/build/changechecker.min.js.map b/lib/form/amd/build/changechecker.min.js.map index 1dca216d469..346588b4143 100644 --- a/lib/form/amd/build/changechecker.min.js.map +++ b/lib/form/amd/build/changechecker.min.js.map @@ -1 +1 @@ -{"version":3,"file":"changechecker.min.js","sources":["../src/changechecker.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module provides change detection to forms, allowing a browser to warn the user before navigating away if changes\n * have been made.\n *\n * Two flags are stored for each form:\n * * a 'dirty' flag; and\n * * a 'submitted' flag.\n *\n * When the page is unloaded each watched form is checked. If the 'dirty' flag is set for any form, and the 'submitted'\n * flag is not set for any form, then a warning is shown.\n *\n * The 'dirty' flag is set when any form element is modified within a watched form.\n * The flag can also be set programatically. This may be required for custom form elements.\n *\n * It is not possible to customise the warning message in any modern browser.\n *\n * Please note that some browsers have controls on when these alerts may or may not be shown.\n * See {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload} for browser-specific\n * notes and references.\n *\n * @module core_form/changechecker\n * @copyright 2021 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @example Usage where the FormElement is already held\n *\n * import {watchForm} from 'core_form/changechecker';\n *\n * // Fetch the form element somehow.\n * watchForm(formElement);\n *\n * @example Usage from the child of a form - i.e. an input, button, div, etc.\n *\n * import {watchForm} from 'core_form/changechecker';\n *\n * // Watch the form by using a child of it.\n * watchForm(document.querySelector('input[data-foo=\"bar\"]'););\n *\n * @example Usage from within a template\n *
\n * \n *
\n * {{#js}}\n * require(['core_form/changechecker'], function(changeChecker) {\n * watchFormById('mod_example-entry-{{uniqid}}');\n * });\n * {{/js}}\n */\n\nimport {eventTypes} from 'core_editor/events';\nimport {get_string as getString} from 'core/str';\n\n/**\n * @property {Bool} initialised Whether the change checker has been initialised\n * @private\n */\nlet initialised = false;\n\n/**\n * @property {String} warningString The warning string to show on form change failure\n * @private\n */\nlet warningString;\n\n/**\n * @property {Array} watchedForms The list of watched forms\n * @private\n */\nlet watchedForms = [];\n\n/**\n * @property {Bool} formChangeCheckerDisabled Whether the form change checker has been actively disabled\n * @private\n */\nlet formChangeCheckerDisabled = false;\n\n/**\n * Get the nearest form element from a child element.\n *\n * @param {HTMLElement} formChild\n * @returns {HTMLFormElement|null}\n * @private\n */\nconst getFormFromChild = formChild => formChild.closest('form');\n\n/**\n * Watch the specified form for changes.\n *\n * @method\n * @param {HTMLElement} formNode\n */\nexport const watchForm = formNode => {\n // Normalise the formNode.\n formNode = getFormFromChild(formNode);\n\n if (!formNode) {\n // No form found.\n return;\n }\n\n if (isWatchingForm(formNode)) {\n // This form is already watched.\n return;\n }\n\n watchedForms.push(formNode);\n};\n\n/**\n * Stop watching the specified form for changes.\n *\n * If the form was not watched, then no change is made.\n *\n * A child of the form may be passed instead.\n *\n * @method\n * @param {HTMLElement} formNode\n * @example Stop watching a form for changes\n * import {unWatchForm} from 'core_form/changechecker';\n *\n * // ...\n * document.addEventListener('click', e => {\n * if (e.target.closest('[data-action=\"changePage\"]')) {\n * unWatchForm(e.target);\n * }\n * });\n */\nexport const unWatchForm = formNode => {\n watchedForms = watchedForms.filter(watchedForm => !!watchedForm.contains(formNode));\n};\n\n/**\n * Reset the 'dirty' flag for all watched forms.\n *\n * If a form was previously marked as 'dirty', then this flag will be cleared and when the page is unloaded no warning\n * will be shown.\n *\n * @method\n */\nexport const resetAllFormDirtyStates = () => {\n watchedForms.forEach(watchedForm => {\n watchedForm.dataset.formSubmitted = \"false\";\n watchedForm.dataset.formDirty = \"false\";\n });\n};\n\n/**\n * Reset the 'dirty' flag of the specified form.\n *\n * @method\n * @param {HTMLElement} formNode\n */\nexport const resetFormDirtyState = formNode => {\n formNode = getFormFromChild(formNode);\n\n if (!formNode) {\n return;\n }\n\n formNode.dataset.formSubmitted = \"false\";\n formNode.dataset.formDirty = \"false\";\n};\n\n/**\n * Mark all forms as dirty.\n *\n * This function is only for backwards-compliance with the old YUI module and should not be used in any other situation.\n * It will be removed in Moodle 4.4.\n *\n * @method\n */\nexport const markAllFormsAsDirty = () => {\n watchedForms.forEach(watchedForm => {\n watchedForm.dataset.formDirty = \"true\";\n });\n};\n\n/**\n * Mark a specific form as dirty.\n *\n * This behaviour may be required for custom form elements which are not caught by the standard change listeners.\n *\n * @method\n * @param {HTMLElement} formNode\n */\nexport const markFormAsDirty = formNode => {\n formNode = getFormFromChild(formNode);\n\n if (!formNode) {\n return;\n }\n\n // Mark it as dirty.\n formNode.dataset.formDirty = \"true\";\n};\n\n/**\n * Actively disable the form change checker.\n *\n * Please note that it cannot be re-enabled once disabled.\n *\n * @method\n */\nexport const disableAllChecks = () => {\n formChangeCheckerDisabled = true;\n};\n\n/**\n * Check whether any watched from is dirty.\n *\n * @method\n * @returns {Bool}\n */\nexport const isAnyWatchedFormDirty = () => {\n if (formChangeCheckerDisabled) {\n // The form change checker is disabled.\n return false;\n }\n\n const hasSubmittedForm = watchedForms.some(watchedForm => watchedForm.dataset.formSubmitted === \"true\");\n if (hasSubmittedForm) {\n // Do not warn about submitted forms, ever.\n return false;\n }\n\n const hasDirtyForm = watchedForms.some(watchedForm => {\n if (!watchedForm.isConnected) {\n // The watched form is not connected to the DOM.\n return false;\n }\n\n if (watchedForm.dataset.formDirty === \"true\") {\n // The form has been marked as dirty.\n return true;\n }\n\n // Elements currently holding focus will not have triggered change detection.\n // Check whether the value matches the original value upon form load.\n if (document.activeElement && document.activeElement.dataset.propertyIsEnumerable('initialValue')) {\n const isActiveElementWatched = isWatchingForm(document.activeElement)\n && !shouldIgnoreChangesForNode(document.activeElement);\n const hasValueChanged = document.activeElement.dataset.initialValue !== document.activeElement.value;\n\n if (isActiveElementWatched && hasValueChanged) {\n return true;\n }\n }\n\n return false;\n });\n\n if (hasDirtyForm) {\n // At least one form is dirty.\n return true;\n }\n\n // Handle TinyMCE editor instances.\n // TinyMCE forms may not have been initialised at the time that startWatching is called.\n // Check whether any tinyMCE editor is dirty.\n if (typeof window.tinyMCE !== 'undefined' && window.tinyMCE.editors) {\n if (window.tinyMCE.editors.some(editor => editor.isDirty())) {\n return true;\n }\n }\n\n // No dirty forms detected.\n return false;\n};\n\n/**\n * Get the watched form for the specified target.\n *\n * @method\n * @param {HTMLNode} target\n * @returns {HTMLFormElement}\n * @private\n */\nconst getFormForNode = target => watchedForms.find(watchedForm => watchedForm.contains(target));\n\n/**\n * Whether the specified target is a watched form.\n *\n * @method\n * @param {HTMLNode} target\n * @returns {Bool}\n * @private\n */\nconst isWatchingForm = target => watchedForms.some(watchedForm => watchedForm.contains(target));\n\n/**\n * Whether the specified target should ignore changes or not.\n *\n * @method\n * @param {HTMLNode} target\n * @returns {Bool}\n * @private\n */\nconst shouldIgnoreChangesForNode = target => !!target.closest('.ignoredirty');\n\n/**\n * Mark a form as changed.\n *\n * @method\n * @param {HTMLElement} changedNode An element in the form which was changed\n */\nexport const markFormChangedFromNode = changedNode => {\n if (changedNode.dataset.formChangeCheckerOverride) {\n // Changes to this form node disable the form change checker entirely.\n // This is intended for select fields which cause an immediate redirect.\n disableAllChecks();\n return;\n }\n\n if (!isWatchingForm(changedNode)) {\n return;\n }\n\n if (shouldIgnoreChangesForNode(changedNode)) {\n return;\n }\n\n // Mark the form as dirty.\n const formNode = getFormForNode(changedNode);\n formNode.dataset.formDirty = \"true\";\n};\n\n/**\n * Mark a form as submitted.\n *\n * @method\n * @param {HTMLElement} formNode An element in the form to mark as submitted\n */\nexport const markFormSubmitted = formNode => {\n formNode = getFormFromChild(formNode);\n\n if (!formNode) {\n return;\n }\n\n formNode.dataset.formSubmitted = \"true\";\n};\n\n/**\n * Mark all forms as submitted.\n *\n * This function is only for backwards-compliance with the old YUI module and should not be used in any other situation.\n * It will be removed in Moodle 4.4.\n *\n * @method\n */\nexport const markAllFormsSubmitted = () => {\n watchedForms.forEach(watchedForm => markFormSubmitted(watchedForm));\n};\n\n/**\n * Handle the beforeunload event.\n *\n * @method\n * @param {Event} e\n * @returns {string|null}\n * @private\n */\nconst beforeUnloadHandler = e => {\n // Please note: The use of Promises in this function is forbidden.\n // This is an event handler and _cannot_ be asynchronous.\n let warnBeforeUnload = isAnyWatchedFormDirty() && !M.cfg.behatsiterunning;\n if (warnBeforeUnload) {\n // According to the specification, to show the confirmation dialog an event handler should call preventDefault()\n // on the event.\n e.preventDefault();\n\n // However note that not all browsers support this method, and some instead require the event handler to\n // implement one of two legacy methods:\n // * assigning a string to the event's returnValue property; and\n // * returning a string from the event handler.\n\n // Assigning a string to the event's returnValue property.\n e.returnValue = warningString;\n\n // Returning a string from the event handler.\n return e.returnValue;\n }\n\n // Attaching an event handler/listener to window or document's beforeunload event prevents browsers from using\n // in-memory page navigation caches, like Firefox's Back-Forward cache or WebKit's Page Cache.\n // Remove the handler.\n window.removeEventListener('beforeunload', beforeUnloadHandler);\n\n return null;\n};\n\n/**\n * Start watching for form changes.\n *\n * This function is called on module load, and should not normally be called.\n *\n * @method\n * @protected\n */\nexport const startWatching = () => {\n if (initialised) {\n return;\n }\n\n // Add legacy support to provide b/c for the old YUI version.\n addLegacyFunctions();\n\n document.addEventListener('change', e => {\n if (!isWatchingForm(e.target)) {\n return;\n }\n\n markFormChangedFromNode(e.target);\n });\n\n document.addEventListener('click', e => {\n const ignoredButton = e.target.closest('[data-formchangechecker-ignore-submit]');\n if (!ignoredButton) {\n return;\n }\n\n const ownerForm = getFormFromChild(e.target);\n if (ownerForm) {\n ownerForm.dataset.ignoreSubmission = \"true\";\n }\n });\n\n document.addEventListener('focusin', e => {\n if (e.target.matches('input, textarea, select')) {\n if (e.target.dataset.propertyIsEnumerable('initialValue')) {\n // The initial value has already been set.\n return;\n }\n e.target.dataset.initialValue = e.target.value;\n }\n });\n\n document.addEventListener('submit', e => {\n const formNode = getFormFromChild(e.target);\n if (!formNode) {\n // Weird, but watch for this anyway.\n return;\n }\n\n if (formNode.dataset.ignoreSubmission) {\n // This form was submitted by a button which requested that the form checked should not mark it as submitted.\n formNode.dataset.ignoreSubmission = \"false\";\n return;\n }\n\n markFormSubmitted(formNode);\n });\n\n document.addEventListener(eventTypes.editorContentRestored, e => {\n if (e.target != document) {\n resetFormDirtyState(e.target);\n } else {\n resetAllFormDirtyStates();\n }\n });\n\n getString('changesmadereallygoaway', 'moodle')\n .then(changesMadeString => {\n warningString = changesMadeString;\n return;\n })\n .catch();\n\n window.addEventListener('beforeunload', beforeUnloadHandler);\n};\n\n/**\n * Add legacy functions for backwards compatability.\n *\n * @method\n * @private\n */\nconst addLegacyFunctions = () => {\n // Create a curried function to log use of the old function and provide detail on its replacement.\n const getLoggedLegacyFallback = (oldFunctionName, newFunctionName, newFunction) => (...args) => {\n window.console.warn(\n `The moodle-core-formchangechecker has been deprecated ` +\n `and replaced with core_form/changechecker. ` +\n `The ${oldFunctionName} function has been replaced with ${newFunctionName}.`\n );\n newFunction(...args);\n };\n\n /* eslint-disable */\n window.M.core_formchangechecker = {\n init: getLoggedLegacyFallback('init', 'watchFormById', watchFormById),\n reset_form_dirty_state: getLoggedLegacyFallback('reset_form_dirty_state', 'resetFormDirtyState', resetAllFormDirtyStates),\n set_form_changed: getLoggedLegacyFallback('set_form_changed', 'markFormAsDirty', markAllFormsAsDirty),\n set_form_submitted: getLoggedLegacyFallback('set_form_submitted', 'markFormSubmitted', markAllFormsSubmitted),\n };\n /* eslint-enable */\n};\n\n/**\n * Watch the form matching the specified ID for changes.\n *\n * @method\n * @param {String} formId\n */\nexport const watchFormById = formId => {\n watchForm(document.getElementById(formId));\n};\n\n/**\n * Reset the dirty state of the form matching the specified ID..\n *\n * @method\n * @param {String} formId\n */\nexport const resetFormDirtyStateById = formId => {\n resetFormDirtyState(document.getElementById(formId));\n};\n\n/**\n * Mark the form matching the specified ID as dirty.\n *\n * @method\n * @param {String} formId\n */\nexport const markFormAsDirtyById = formId => {\n markFormAsDirty(document.getElementById(formId));\n};\n\n// Configure all event listeners.\nstartWatching();\n"],"names":["warningString","watchedForms","formChangeCheckerDisabled","getFormFromChild","formChild","closest","watchForm","formNode","isWatchingForm","push","filter","watchedForm","contains","resetAllFormDirtyStates","forEach","dataset","formSubmitted","formDirty","resetFormDirtyState","markAllFormsAsDirty","markFormAsDirty","disableAllChecks","isAnyWatchedFormDirty","some","isConnected","document","activeElement","propertyIsEnumerable","isActiveElementWatched","shouldIgnoreChangesForNode","hasValueChanged","initialValue","value","window","tinyMCE","editors","editor","isDirty","target","markFormChangedFromNode","changedNode","formChangeCheckerOverride","find","markFormSubmitted","markAllFormsSubmitted","beforeUnloadHandler","e","M","cfg","behatsiterunning","preventDefault","returnValue","removeEventListener","startWatching","addLegacyFunctions","addEventListener","ownerForm","ignoreSubmission","matches","eventTypes","editorContentRestored","then","changesMadeString","catch","getLoggedLegacyFallback","oldFunctionName","newFunctionName","newFunction","console","warn","core_formchangechecker","init","watchFormById","reset_form_dirty_state","set_form_changed","set_form_submitted","formId","getElementById"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"changechecker.min.js","sources":["../src/changechecker.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module provides change detection to forms, allowing a browser to warn the user before navigating away if changes\n * have been made.\n *\n * Two flags are stored for each form:\n * * a 'dirty' flag; and\n * * a 'submitted' flag.\n *\n * When the page is unloaded each watched form is checked. If the 'dirty' flag is set for any form, and the 'submitted'\n * flag is not set for any form, then a warning is shown.\n *\n * The 'dirty' flag is set when any form element is modified within a watched form.\n * The flag can also be set programatically. This may be required for custom form elements.\n *\n * It is not possible to customise the warning message in any modern browser.\n *\n * Please note that some browsers have controls on when these alerts may or may not be shown.\n * See {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload} for browser-specific\n * notes and references.\n *\n * @module core_form/changechecker\n * @copyright 2021 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @example Usage where the FormElement is already held\n *\n * import {watchForm} from 'core_form/changechecker';\n *\n * // Fetch the form element somehow.\n * watchForm(formElement);\n *\n * @example Usage from the child of a form - i.e. an input, button, div, etc.\n *\n * import {watchForm} from 'core_form/changechecker';\n *\n * // Watch the form by using a child of it.\n * watchForm(document.querySelector('input[data-foo=\"bar\"]'););\n *\n * @example Usage from within a template\n *
\n * \n *
\n * {{#js}}\n * require(['core_form/changechecker'], function(changeChecker) {\n * watchFormById('mod_example-entry-{{uniqid}}');\n * });\n * {{/js}}\n */\n\nimport {eventTypes} from 'core_editor/events';\nimport {getString} from 'core/str';\n\n/**\n * @property {Bool} initialised Whether the change checker has been initialised\n * @private\n */\nlet initialised = false;\n\n/**\n * @property {String} warningString The warning string to show on form change failure\n * @private\n */\nlet warningString;\n\n/**\n * @property {Array} watchedForms The list of watched forms\n * @private\n */\nlet watchedForms = [];\n\n/**\n * @property {Bool} formChangeCheckerDisabled Whether the form change checker has been actively disabled\n * @private\n */\nlet formChangeCheckerDisabled = false;\n\n/**\n * Get the nearest form element from a child element.\n *\n * @param {HTMLElement} formChild\n * @returns {HTMLFormElement|null}\n * @private\n */\nconst getFormFromChild = formChild => formChild.closest('form');\n\n/**\n * Watch the specified form for changes.\n *\n * @method\n * @param {HTMLElement} formNode\n */\nexport const watchForm = formNode => {\n // Normalise the formNode.\n formNode = getFormFromChild(formNode);\n\n if (!formNode) {\n // No form found.\n return;\n }\n\n if (isWatchingForm(formNode)) {\n // This form is already watched.\n return;\n }\n\n watchedForms.push(formNode);\n};\n\n/**\n * Stop watching the specified form for changes.\n *\n * If the form was not watched, then no change is made.\n *\n * A child of the form may be passed instead.\n *\n * @method\n * @param {HTMLElement} formNode\n * @example Stop watching a form for changes\n * import {unWatchForm} from 'core_form/changechecker';\n *\n * // ...\n * document.addEventListener('click', e => {\n * if (e.target.closest('[data-action=\"changePage\"]')) {\n * unWatchForm(e.target);\n * }\n * });\n */\nexport const unWatchForm = formNode => {\n watchedForms = watchedForms.filter(watchedForm => !!watchedForm.contains(formNode));\n};\n\n/**\n * Reset the 'dirty' flag for all watched forms.\n *\n * If a form was previously marked as 'dirty', then this flag will be cleared and when the page is unloaded no warning\n * will be shown.\n *\n * @method\n */\nexport const resetAllFormDirtyStates = () => {\n watchedForms.forEach(watchedForm => {\n watchedForm.dataset.formSubmitted = \"false\";\n watchedForm.dataset.formDirty = \"false\";\n });\n};\n\n/**\n * Reset the 'dirty' flag of the specified form.\n *\n * @method\n * @param {HTMLElement} formNode\n */\nexport const resetFormDirtyState = formNode => {\n formNode = getFormFromChild(formNode);\n\n if (!formNode) {\n return;\n }\n\n formNode.dataset.formSubmitted = \"false\";\n formNode.dataset.formDirty = \"false\";\n};\n\n/**\n * Mark all forms as dirty.\n *\n * This function is only for backwards-compliance with the old YUI module and should not be used in any other situation.\n * It will be removed in Moodle 4.4.\n *\n * @method\n */\nexport const markAllFormsAsDirty = () => {\n watchedForms.forEach(watchedForm => {\n watchedForm.dataset.formDirty = \"true\";\n });\n};\n\n/**\n * Mark a specific form as dirty.\n *\n * This behaviour may be required for custom form elements which are not caught by the standard change listeners.\n *\n * @method\n * @param {HTMLElement} formNode\n */\nexport const markFormAsDirty = formNode => {\n formNode = getFormFromChild(formNode);\n\n if (!formNode) {\n return;\n }\n\n // Mark it as dirty.\n formNode.dataset.formDirty = \"true\";\n};\n\n/**\n * Actively disable the form change checker.\n *\n * Please note that it cannot be re-enabled once disabled.\n *\n * @method\n */\nexport const disableAllChecks = () => {\n formChangeCheckerDisabled = true;\n};\n\n/**\n * Check whether any watched from is dirty.\n *\n * @method\n * @returns {Bool}\n */\nexport const isAnyWatchedFormDirty = () => {\n if (formChangeCheckerDisabled) {\n // The form change checker is disabled.\n return false;\n }\n\n const hasSubmittedForm = watchedForms.some(watchedForm => watchedForm.dataset.formSubmitted === \"true\");\n if (hasSubmittedForm) {\n // Do not warn about submitted forms, ever.\n return false;\n }\n\n const hasDirtyForm = watchedForms.some(watchedForm => {\n if (!watchedForm.isConnected) {\n // The watched form is not connected to the DOM.\n return false;\n }\n\n if (watchedForm.dataset.formDirty === \"true\") {\n // The form has been marked as dirty.\n return true;\n }\n\n // Elements currently holding focus will not have triggered change detection.\n // Check whether the value matches the original value upon form load.\n if (document.activeElement && document.activeElement.dataset.propertyIsEnumerable('initialValue')) {\n const isActiveElementWatched = isWatchingForm(document.activeElement)\n && !shouldIgnoreChangesForNode(document.activeElement);\n const hasValueChanged = document.activeElement.dataset.initialValue !== document.activeElement.value;\n\n if (isActiveElementWatched && hasValueChanged) {\n return true;\n }\n }\n\n return false;\n });\n\n if (hasDirtyForm) {\n // At least one form is dirty.\n return true;\n }\n\n // Handle TinyMCE editor instances.\n // TinyMCE forms may not have been initialised at the time that startWatching is called.\n // Check whether any tinyMCE editor is dirty.\n if (typeof window.tinyMCE !== 'undefined' && window.tinyMCE.editors) {\n if (window.tinyMCE.editors.some(editor => editor.isDirty())) {\n return true;\n }\n }\n\n // No dirty forms detected.\n return false;\n};\n\n/**\n * Get the watched form for the specified target.\n *\n * @method\n * @param {HTMLNode} target\n * @returns {HTMLFormElement}\n * @private\n */\nconst getFormForNode = target => watchedForms.find(watchedForm => watchedForm.contains(target));\n\n/**\n * Whether the specified target is a watched form.\n *\n * @method\n * @param {HTMLNode} target\n * @returns {Bool}\n * @private\n */\nconst isWatchingForm = target => watchedForms.some(watchedForm => watchedForm.contains(target));\n\n/**\n * Whether the specified target should ignore changes or not.\n *\n * @method\n * @param {HTMLNode} target\n * @returns {Bool}\n * @private\n */\nconst shouldIgnoreChangesForNode = target => !!target.closest('.ignoredirty');\n\n/**\n * Mark a form as changed.\n *\n * @method\n * @param {HTMLElement} changedNode An element in the form which was changed\n */\nexport const markFormChangedFromNode = changedNode => {\n if (changedNode.dataset.formChangeCheckerOverride) {\n // Changes to this form node disable the form change checker entirely.\n // This is intended for select fields which cause an immediate redirect.\n disableAllChecks();\n return;\n }\n\n if (!isWatchingForm(changedNode)) {\n return;\n }\n\n if (shouldIgnoreChangesForNode(changedNode)) {\n return;\n }\n\n // Mark the form as dirty.\n const formNode = getFormForNode(changedNode);\n formNode.dataset.formDirty = \"true\";\n};\n\n/**\n * Mark a form as submitted.\n *\n * @method\n * @param {HTMLElement} formNode An element in the form to mark as submitted\n */\nexport const markFormSubmitted = formNode => {\n formNode = getFormFromChild(formNode);\n\n if (!formNode) {\n return;\n }\n\n formNode.dataset.formSubmitted = \"true\";\n};\n\n/**\n * Mark all forms as submitted.\n *\n * This function is only for backwards-compliance with the old YUI module and should not be used in any other situation.\n * It will be removed in Moodle 4.4.\n *\n * @method\n */\nexport const markAllFormsSubmitted = () => {\n watchedForms.forEach(watchedForm => markFormSubmitted(watchedForm));\n};\n\n/**\n * Handle the beforeunload event.\n *\n * @method\n * @param {Event} e\n * @returns {string|null}\n * @private\n */\nconst beforeUnloadHandler = e => {\n // Please note: The use of Promises in this function is forbidden.\n // This is an event handler and _cannot_ be asynchronous.\n let warnBeforeUnload = isAnyWatchedFormDirty() && !M.cfg.behatsiterunning;\n if (warnBeforeUnload) {\n // According to the specification, to show the confirmation dialog an event handler should call preventDefault()\n // on the event.\n e.preventDefault();\n\n // However note that not all browsers support this method, and some instead require the event handler to\n // implement one of two legacy methods:\n // * assigning a string to the event's returnValue property; and\n // * returning a string from the event handler.\n\n // Assigning a string to the event's returnValue property.\n e.returnValue = warningString;\n\n // Returning a string from the event handler.\n return e.returnValue;\n }\n\n // Attaching an event handler/listener to window or document's beforeunload event prevents browsers from using\n // in-memory page navigation caches, like Firefox's Back-Forward cache or WebKit's Page Cache.\n // Remove the handler.\n window.removeEventListener('beforeunload', beforeUnloadHandler);\n\n return null;\n};\n\n/**\n * Start watching for form changes.\n *\n * This function is called on module load, and should not normally be called.\n *\n * @method\n * @protected\n */\nexport const startWatching = () => {\n if (initialised) {\n return;\n }\n\n // Add legacy support to provide b/c for the old YUI version.\n addLegacyFunctions();\n\n document.addEventListener('change', e => {\n if (!isWatchingForm(e.target)) {\n return;\n }\n\n markFormChangedFromNode(e.target);\n });\n\n document.addEventListener('click', e => {\n const ignoredButton = e.target.closest('[data-formchangechecker-ignore-submit]');\n if (!ignoredButton) {\n return;\n }\n\n const ownerForm = getFormFromChild(e.target);\n if (ownerForm) {\n ownerForm.dataset.ignoreSubmission = \"true\";\n }\n });\n\n document.addEventListener('focusin', e => {\n if (e.target.matches('input, textarea, select')) {\n if (e.target.dataset.propertyIsEnumerable('initialValue')) {\n // The initial value has already been set.\n return;\n }\n e.target.dataset.initialValue = e.target.value;\n }\n });\n\n document.addEventListener('submit', e => {\n const formNode = getFormFromChild(e.target);\n if (!formNode) {\n // Weird, but watch for this anyway.\n return;\n }\n\n if (formNode.dataset.ignoreSubmission) {\n // This form was submitted by a button which requested that the form checked should not mark it as submitted.\n formNode.dataset.ignoreSubmission = \"false\";\n return;\n }\n\n markFormSubmitted(formNode);\n });\n\n document.addEventListener(eventTypes.editorContentRestored, e => {\n if (e.target != document) {\n resetFormDirtyState(e.target);\n } else {\n resetAllFormDirtyStates();\n }\n });\n\n getString('changesmadereallygoaway', 'moodle')\n .then(changesMadeString => {\n warningString = changesMadeString;\n return;\n })\n .catch();\n\n window.addEventListener('beforeunload', beforeUnloadHandler);\n};\n\n/**\n * Add legacy functions for backwards compatability.\n *\n * @method\n * @private\n */\nconst addLegacyFunctions = () => {\n // Create a curried function to log use of the old function and provide detail on its replacement.\n const getLoggedLegacyFallback = (oldFunctionName, newFunctionName, newFunction) => (...args) => {\n window.console.warn(\n `The moodle-core-formchangechecker has been deprecated ` +\n `and replaced with core_form/changechecker. ` +\n `The ${oldFunctionName} function has been replaced with ${newFunctionName}.`\n );\n newFunction(...args);\n };\n\n /* eslint-disable */\n window.M.core_formchangechecker = {\n init: getLoggedLegacyFallback('init', 'watchFormById', watchFormById),\n reset_form_dirty_state: getLoggedLegacyFallback('reset_form_dirty_state', 'resetFormDirtyState', resetAllFormDirtyStates),\n set_form_changed: getLoggedLegacyFallback('set_form_changed', 'markFormAsDirty', markAllFormsAsDirty),\n set_form_submitted: getLoggedLegacyFallback('set_form_submitted', 'markFormSubmitted', markAllFormsSubmitted),\n };\n /* eslint-enable */\n};\n\n/**\n * Watch the form matching the specified ID for changes.\n *\n * @method\n * @param {String} formId\n */\nexport const watchFormById = formId => {\n watchForm(document.getElementById(formId));\n};\n\n/**\n * Reset the dirty state of the form matching the specified ID..\n *\n * @method\n * @param {String} formId\n */\nexport const resetFormDirtyStateById = formId => {\n resetFormDirtyState(document.getElementById(formId));\n};\n\n/**\n * Mark the form matching the specified ID as dirty.\n *\n * @method\n * @param {String} formId\n */\nexport const markFormAsDirtyById = formId => {\n markFormAsDirty(document.getElementById(formId));\n};\n\n// Configure all event listeners.\nstartWatching();\n"],"names":["warningString","watchedForms","formChangeCheckerDisabled","getFormFromChild","formChild","closest","watchForm","formNode","isWatchingForm","push","filter","watchedForm","contains","resetAllFormDirtyStates","forEach","dataset","formSubmitted","formDirty","resetFormDirtyState","markAllFormsAsDirty","markFormAsDirty","disableAllChecks","isAnyWatchedFormDirty","some","isConnected","document","activeElement","propertyIsEnumerable","isActiveElementWatched","shouldIgnoreChangesForNode","hasValueChanged","initialValue","value","window","tinyMCE","editors","editor","isDirty","target","markFormChangedFromNode","changedNode","formChangeCheckerOverride","find","markFormSubmitted","markAllFormsSubmitted","beforeUnloadHandler","e","M","cfg","behatsiterunning","preventDefault","returnValue","removeEventListener","startWatching","addLegacyFunctions","addEventListener","ownerForm","ignoreSubmission","matches","eventTypes","editorContentRestored","then","changesMadeString","catch","getLoggedLegacyFallback","oldFunctionName","newFunctionName","newFunction","console","warn","core_formchangechecker","init","watchFormById","reset_form_dirty_state","set_form_changed","set_form_submitted","formId","getElementById"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA8EIA,cAMAC,aAAe,GAMfC,2BAA4B,QAS1BC,iBAAmBC,WAAaA,UAAUC,QAAQ,QAQ3CC,UAAYC,YAErBA,SAAWJ,iBAAiBI,aAOxBC,eAAeD,WAKnBN,aAAaQ,KAAKF,8DAsBKA,WACvBN,aAAeA,aAAaS,QAAOC,eAAiBA,YAAYC,SAASL,mBAWhEM,wBAA0B,KACnCZ,aAAaa,SAAQH,cACjBA,YAAYI,QAAQC,cAAgB,QACpCL,YAAYI,QAAQE,UAAY,2EAU3BC,oBAAsBX,YAC/BA,SAAWJ,iBAAiBI,aAM5BA,SAASQ,QAAQC,cAAgB,QACjCT,SAASQ,QAAQE,UAAY,iEAWpBE,oBAAsB,KAC/BlB,aAAaa,SAAQH,cACjBA,YAAYI,QAAQE,UAAY,kEAY3BG,gBAAkBb,YAC3BA,SAAWJ,iBAAiBI,aAO5BA,SAASQ,QAAQE,UAAY,wDAUpBI,iBAAmB,KAC5BnB,2BAA4B,oDASnBoB,sBAAwB,QAC7BpB,iCAEO,KAGcD,aAAasB,MAAKZ,aAAqD,SAAtCA,YAAYI,QAAQC,uBAGnE,UAGUf,aAAasB,MAAKZ,kBAC9BA,YAAYa,mBAEN,KAG2B,SAAlCb,YAAYI,QAAQE,iBAEb,KAKPQ,SAASC,eAAiBD,SAASC,cAAcX,QAAQY,qBAAqB,gBAAiB,OACzFC,uBAAyBpB,eAAeiB,SAASC,iBAC/CG,2BAA2BJ,SAASC,eACtCI,gBAAkBL,SAASC,cAAcX,QAAQgB,eAAiBN,SAASC,cAAcM,SAE3FJ,wBAA0BE,uBACnB,SAIR,aAWmB,IAAnBG,OAAOC,UAA2BD,OAAOC,QAAQC,UACpDF,OAAOC,QAAQC,QAAQZ,MAAKa,QAAUA,OAAOC,yEA2BnD7B,eAAiB8B,QAAUrC,aAAasB,MAAKZ,aAAeA,YAAYC,SAAS0B,UAUjFT,2BAA6BS,UAAYA,OAAOjC,QAAQ,gBAQjDkC,wBAA0BC,iBAC/BA,YAAYzB,QAAQ0B,sCAGpBpB,uBAICb,eAAegC,uBAIhBX,2BAA2BW,oBAxCZF,IAAAA,QAAAA,OA6CaE,YA7CHvC,aAAayC,MAAK/B,aAAeA,YAAYC,SAAS0B,WA8C1EvB,QAAQE,UAAY,uEASpB0B,kBAAoBpC,YAC7BA,SAAWJ,iBAAiBI,aAM5BA,SAASQ,QAAQC,cAAgB,4DAWxB4B,sBAAwB,KACjC3C,aAAaa,SAAQH,aAAegC,kBAAkBhC,2EAWpDkC,oBAAsBC,GAGDxB,0BAA4ByB,EAAEC,IAAIC,kBAIrDH,EAAEI,iBAQFJ,EAAEK,YAAcnD,cAGT8C,EAAEK,cAMblB,OAAOmB,oBAAoB,eAAgBP,qBAEpC,MAWEQ,cAAgB,KAMzBC,qBAEA7B,SAAS8B,iBAAiB,UAAUT,IAC3BtC,eAAesC,EAAER,SAItBC,wBAAwBO,EAAER,WAG9Bb,SAAS8B,iBAAiB,SAAST,QACTA,EAAER,OAAOjC,QAAQ,uDAKjCmD,UAAYrD,iBAAiB2C,EAAER,QACjCkB,YACAA,UAAUzC,QAAQ0C,iBAAmB,WAI7ChC,SAAS8B,iBAAiB,WAAWT,OAC7BA,EAAER,OAAOoB,QAAQ,2BAA4B,IACzCZ,EAAER,OAAOvB,QAAQY,qBAAqB,uBAI1CmB,EAAER,OAAOvB,QAAQgB,aAAee,EAAER,OAAON,UAIjDP,SAAS8B,iBAAiB,UAAUT,UAC1BvC,SAAWJ,iBAAiB2C,EAAER,QAC/B/B,WAKDA,SAASQ,QAAQ0C,iBAEjBlD,SAASQ,QAAQ0C,iBAAmB,QAIxCd,kBAAkBpC,cAGtBkB,SAAS8B,iBAAiBI,mBAAWC,uBAAuBd,IACpDA,EAAER,QAAUb,SACZP,oBAAoB4B,EAAER,QAEtBzB,gDAIE,0BAA2B,UACpCgD,MAAKC,oBACF9D,cAAgB8D,qBAGnBC,QAED9B,OAAOsB,iBAAiB,eAAgBV,iEAStCS,mBAAqB,WAEjBU,wBAA0B,CAACC,gBAAiBC,gBAAiBC,cAAgB,WAC/ElC,OAAOmC,QAAQC,KACX,kHAEOJ,4DAAmDC,sBAE9DC,2BAIJlC,OAAOc,EAAEuB,uBAAyB,CAC9BC,KAAMP,wBAAwB,OAAQ,gBAAiBQ,eACvDC,uBAAwBT,wBAAwB,yBAA0B,sBAAuBnD,yBACjG6D,iBAAkBV,wBAAwB,mBAAoB,kBAAmB7C,qBACjFwD,mBAAoBX,wBAAwB,qBAAsB,oBAAqBpB,yBAWlF4B,cAAgBI,SACzBtE,UAAUmB,SAASoD,eAAeD,gFASCA,SACnC1D,oBAAoBO,SAASoD,eAAeD,uCASbA,SAC/BxD,gBAAgBK,SAASoD,eAAeD,UAI5CvB"} \ No newline at end of file diff --git a/lib/form/amd/build/dynamicform.min.js b/lib/form/amd/build/dynamicform.min.js index 7962ea6a28f..76c23372a89 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_form/changechecker","core_form/events","core/ajax","core/fragment","core/notification","core/pending","core/templates","core/str","./util"],(function(_exports,FormChangeChecker,FormEvents,_ajax,_fragment,_notification,_pending,_templates,_str,_util){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,FormChangeChecker=_interopRequireWildcard(FormChangeChecker),FormEvents=_interopRequireWildcard(FormEvents),_ajax=_interopRequireDefault(_ajax),_fragment=_interopRequireDefault(_fragment),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_templates=_interopRequireDefault(_templates);return _exports.default=class{constructor(container,formClass){var obj,key,value;value={FORM_SUBMITTED:"core_form_dynamicform_formsubmitted",FORM_CANCELLED:"core_form_dynamicform_formcancelled",CLIENT_VALIDATION_ERROR:"core_form_dynamicform_clientvalidationerror",SERVER_VALIDATION_ERROR:"core_form_dynamicform_validationerror",ERROR:"core_form_dynamicform_error",NOSUBMIT_BUTTON_PRESSED:"core_form_dynamicform_nosubmitbutton",SUBMIT_BUTTON_PRESSED:"core_form_dynamicform_submitbutton",CANCEL_BUTTON_PRESSED:"core_form_dynamicform_cancelbutton"},(key="events")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.formClass=formClass,this.container=container,(0,_str.get_strings)([{key:"collapseall",component:"moodle"},{key:"expandall",component:"moodle"}]).catch(_notification.default.exception),this.container.addEventListener("click",(e=>{if(e.target.matches("form input[type=submit][data-cancel]")){e.preventDefault();this.trigger(this.events.CANCEL_BUTTON_PRESSED,e.target).defaultPrevented||this.processCancelButton()}else if(e.target.matches('form input[type=submit][data-no-submit="1"]')){e.preventDefault();this.trigger(this.events.NOSUBMIT_BUTTON_PRESSED,e.target).defaultPrevented||this.processNoSubmitButton(e.target)}})),this.container.addEventListener("submit",(e=>{if(e.target.matches("form")){e.preventDefault();this.trigger(this.events.SUBMIT_BUTTON_PRESSED).defaultPrevented||this.submitFormAjax()}}))}load(){let args=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;const formData=(0,_util.serialize)(args||{}),pendingPromise=new _pending.default("core_form/dynamicform:load");return this.getBody(formData).then((resp=>this.updateForm(resp))).then(pendingPromise.resolve)}trigger(eventName){const e=new CustomEvent(eventName,{detail:arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,cancelable:!(arguments.length>2&&void 0!==arguments[2])||arguments[2]});return this.container.dispatchEvent(e),e}addEventListener(){this.container.addEventListener(...arguments)}getBody(formDataString){return _ajax.default.call([{methodname:"core_form_dynamic_form",args:{formdata:formDataString,form:this.formClass}}])[0].then((response=>({html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)})))}onSubmitSuccess(response){this.trigger(this.events.FORM_SUBMITTED,response).defaultPrevented||(this.container.innerHTML="")}onSubmitError(exception){this.trigger(this.events.ERROR,exception).defaultPrevented||_notification.default.exception(exception)}processNoSubmitButton(button){const pendingPromise=new _pending.default("core_form/dynamicform:nosubmit"),form=this.getFormNode(),formData=new URLSearchParams([...new FormData(form).entries()]);formData.append(button.getAttribute("name"),button.getAttribute("value")),FormEvents.notifyFormSubmittedByJavascript(form,!0),this.disableButtons(),this.getBody(formData.toString()).then((resp=>this.updateForm(resp))).then(pendingPromise.resolve).catch((exception=>this.onSubmitError(exception)))}getFormNode(){return this.container.querySelector("form")}notifyResetFormChanges(){FormEvents.notifyFormSubmittedByJavascript(this.getFormNode(),!0),FormChangeChecker.resetFormDirtyState(this.getFormNode())}processCancelButton(){this.notifyResetFormChanges();this.trigger(this.events.FORM_CANCELLED).defaultPrevented||(this.container.innerHTML="")}updateForm(_ref){let{html:html,js:js}=_ref;return _templates.default.replaceNodeContents(this.container,html,js)}validateElements(){FormEvents.notifyFormSubmittedByJavascript(this.getFormNode());const invalid=[...this.container.querySelectorAll('[aria-invalid="true"], .error')];return!invalid.length||(invalid[0].focus(),!1)}disableButtons(){this.container.querySelectorAll('form input[type="submit"]').forEach((el=>el.setAttribute("disabled",!0)))}enableButtons(){this.container.querySelectorAll('form input[type="submit"]').forEach((el=>el.removeAttribute("disabled")))}async submitFormAjax(){if(!await this.validateElements())return void this.trigger(this.events.CLIENT_VALIDATION_ERROR,null,!1);this.disableButtons();const form=this.container.querySelector("form"),formData=new URLSearchParams([...new FormData(form).entries()]);_ajax.default.call([{methodname:"core_form_dynamic_form",args:{formdata:formData.toString(),form:this.formClass}}])[0].then((response=>{if(response.submitted){const data=JSON.parse(response.data);this.enableButtons(),this.notifyResetFormChanges(),this.onSubmitSuccess(data)}else this.updateForm({html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)}),this.enableButtons(),this.trigger(this.events.SERVER_VALIDATION_ERROR,null,!1);return null})).catch((exception=>this.onSubmitError(exception)))}},_exports.default})); +define("core_form/dynamicform",["exports","core_form/changechecker","core_form/events","core/ajax","core/fragment","core/notification","core/pending","core/templates","core/str","./util"],(function(_exports,FormChangeChecker,FormEvents,_ajax,_fragment,_notification,_pending,_templates,_str,_util){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,FormChangeChecker=_interopRequireWildcard(FormChangeChecker),FormEvents=_interopRequireWildcard(FormEvents),_ajax=_interopRequireDefault(_ajax),_fragment=_interopRequireDefault(_fragment),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_templates=_interopRequireDefault(_templates);return _exports.default=class{constructor(container,formClass){var obj,key,value;value={FORM_SUBMITTED:"core_form_dynamicform_formsubmitted",FORM_CANCELLED:"core_form_dynamicform_formcancelled",CLIENT_VALIDATION_ERROR:"core_form_dynamicform_clientvalidationerror",SERVER_VALIDATION_ERROR:"core_form_dynamicform_validationerror",ERROR:"core_form_dynamicform_error",NOSUBMIT_BUTTON_PRESSED:"core_form_dynamicform_nosubmitbutton",SUBMIT_BUTTON_PRESSED:"core_form_dynamicform_submitbutton",CANCEL_BUTTON_PRESSED:"core_form_dynamicform_cancelbutton"},(key="events")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.formClass=formClass,this.container=container,(0,_str.getStrings)([{key:"collapseall",component:"moodle"},{key:"expandall",component:"moodle"}]).catch(_notification.default.exception),this.container.addEventListener("click",(e=>{if(e.target.matches("form input[type=submit][data-cancel]")){e.preventDefault();this.trigger(this.events.CANCEL_BUTTON_PRESSED,e.target).defaultPrevented||this.processCancelButton()}else if(e.target.matches('form input[type=submit][data-no-submit="1"]')){e.preventDefault();this.trigger(this.events.NOSUBMIT_BUTTON_PRESSED,e.target).defaultPrevented||this.processNoSubmitButton(e.target)}})),this.container.addEventListener("submit",(e=>{if(e.target.matches("form")){e.preventDefault();this.trigger(this.events.SUBMIT_BUTTON_PRESSED).defaultPrevented||this.submitFormAjax()}}))}load(){let args=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;const formData=(0,_util.serialize)(args||{}),pendingPromise=new _pending.default("core_form/dynamicform:load");return this.getBody(formData).then((resp=>this.updateForm(resp))).then(pendingPromise.resolve)}trigger(eventName){const e=new CustomEvent(eventName,{detail:arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,cancelable:!(arguments.length>2&&void 0!==arguments[2])||arguments[2]});return this.container.dispatchEvent(e),e}addEventListener(){this.container.addEventListener(...arguments)}getBody(formDataString){return _ajax.default.call([{methodname:"core_form_dynamic_form",args:{formdata:formDataString,form:this.formClass}}])[0].then((response=>({html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)})))}onSubmitSuccess(response){this.trigger(this.events.FORM_SUBMITTED,response).defaultPrevented||(this.container.innerHTML="")}onSubmitError(exception){this.trigger(this.events.ERROR,exception).defaultPrevented||_notification.default.exception(exception)}processNoSubmitButton(button){const pendingPromise=new _pending.default("core_form/dynamicform:nosubmit"),form=this.getFormNode(),formData=new URLSearchParams([...new FormData(form).entries()]);formData.append(button.getAttribute("name"),button.getAttribute("value")),FormEvents.notifyFormSubmittedByJavascript(form,!0),this.disableButtons(),this.getBody(formData.toString()).then((resp=>this.updateForm(resp))).then(pendingPromise.resolve).catch((exception=>this.onSubmitError(exception)))}getFormNode(){return this.container.querySelector("form")}notifyResetFormChanges(){FormEvents.notifyFormSubmittedByJavascript(this.getFormNode(),!0),FormChangeChecker.resetFormDirtyState(this.getFormNode())}processCancelButton(){this.notifyResetFormChanges();this.trigger(this.events.FORM_CANCELLED).defaultPrevented||(this.container.innerHTML="")}updateForm(_ref){let{html:html,js:js}=_ref;return _templates.default.replaceNodeContents(this.container,html,js)}validateElements(){FormEvents.notifyFormSubmittedByJavascript(this.getFormNode());const invalid=[...this.container.querySelectorAll('[aria-invalid="true"], .error')];return!invalid.length||(invalid[0].focus(),!1)}disableButtons(){this.container.querySelectorAll('form input[type="submit"]').forEach((el=>el.setAttribute("disabled",!0)))}enableButtons(){this.container.querySelectorAll('form input[type="submit"]').forEach((el=>el.removeAttribute("disabled")))}async submitFormAjax(){if(!await this.validateElements())return void this.trigger(this.events.CLIENT_VALIDATION_ERROR,null,!1);this.disableButtons();const form=this.container.querySelector("form"),formData=new URLSearchParams([...new FormData(form).entries()]);_ajax.default.call([{methodname:"core_form_dynamic_form",args:{formdata:formData.toString(),form:this.formClass}}])[0].then((response=>{if(response.submitted){const data=JSON.parse(response.data);this.enableButtons(),this.notifyResetFormChanges(),this.onSubmitSuccess(data)}else this.updateForm({html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)}),this.enableButtons(),this.trigger(this.events.SERVER_VALIDATION_ERROR,null,!1);return null})).catch((exception=>this.onSubmitError(exception)))}},_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 77d481917ee..ba41e9b5de5 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 * as FormChangeChecker from 'core_form/changechecker';\nimport * as FormEvents from 'core_form/events';\nimport Ajax from 'core/ajax';\nimport Fragment from 'core/fragment';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Templates from 'core/templates';\nimport {get_strings as getStrings} from 'core/str';\nimport {serialize} from './util';\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 = 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 * @fires event:formSubmittedByJavascript\n */\n processNoSubmitButton(button) {\n const pendingPromise = new Pending('core_form/dynamicform:nosubmit');\n const form = this.getFormNode();\n const formData = new URLSearchParams([...(new FormData(form)).entries()]);\n formData.append(button.getAttribute('name'), button.getAttribute('value'));\n\n FormEvents.notifyFormSubmittedByJavascript(form, true);\n\n // Add the button name to the form data and submit it.\n this.disableButtons();\n\n this.getBody(formData.toString())\n .then(resp => this.updateForm(resp))\n .then(pendingPromise.resolve)\n .catch(exception => this.onSubmitError(exception));\n }\n\n /**\n * Get the form node from the Dialogue.\n *\n * @returns {HTMLFormElement}\n */\n getFormNode() {\n return this.container.querySelector('form');\n }\n\n /**\n * Notifies listeners that form dirty state should be reset.\n *\n * @fires event:formSubmittedByJavascript\n */\n notifyResetFormChanges() {\n FormEvents.notifyFormSubmittedByJavascript(this.getFormNode(), true);\n FormChangeChecker.resetFormDirtyState(this.getFormNode());\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\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 }\n\n /**\n * Update form contents\n *\n * @param {object} param\n * @param {string} param.html\n * @param {string} param.js\n * @returns {Promise}\n */\n updateForm({html, js}) {\n return Templates.replaceNodeContents(this.container, html, js);\n }\n\n /**\n * Validate form elements\n * @return {Boolean} Whether client-side validation has passed, false if there are errors\n * @fires event:formSubmittedByJavascript\n */\n validateElements() {\n // Notify listeners that the form is about to be submitted (this will reset atto autosave).\n FormEvents.notifyFormSubmittedByJavascript(this.getFormNode());\n\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 * 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 this.onSubmitSuccess(data);\n }\n return null;\n })\n .catch(exception => this.onSubmitError(exception));\n }\n}\n"],"names":["constructor","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","load","args","formData","pendingPromise","Pending","getBody","then","resp","updateForm","resolve","eventName","CustomEvent","detail","cancelable","dispatchEvent","formDataString","Ajax","call","methodname","formdata","form","response","html","js","Fragment","processCollectedJavascript","javascript","onSubmitSuccess","innerHTML","onSubmitError","button","getFormNode","URLSearchParams","FormData","entries","append","getAttribute","FormEvents","notifyFormSubmittedByJavascript","disableButtons","toString","querySelector","notifyResetFormChanges","FormChangeChecker","resetFormDirtyState","Templates","replaceNodeContents","validateElements","invalid","querySelectorAll","length","focus","forEach","el","setAttribute","enableButtons","removeAttribute","submitted","data","JSON","parse"],"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 * as FormChangeChecker from 'core_form/changechecker';\nimport * as FormEvents from 'core_form/events';\nimport Ajax from 'core/ajax';\nimport Fragment from 'core/fragment';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Templates from 'core/templates';\nimport {getStrings} from 'core/str';\nimport {serialize} from './util';\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 = 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 * @fires event:formSubmittedByJavascript\n */\n processNoSubmitButton(button) {\n const pendingPromise = new Pending('core_form/dynamicform:nosubmit');\n const form = this.getFormNode();\n const formData = new URLSearchParams([...(new FormData(form)).entries()]);\n formData.append(button.getAttribute('name'), button.getAttribute('value'));\n\n FormEvents.notifyFormSubmittedByJavascript(form, true);\n\n // Add the button name to the form data and submit it.\n this.disableButtons();\n\n this.getBody(formData.toString())\n .then(resp => this.updateForm(resp))\n .then(pendingPromise.resolve)\n .catch(exception => this.onSubmitError(exception));\n }\n\n /**\n * Get the form node from the Dialogue.\n *\n * @returns {HTMLFormElement}\n */\n getFormNode() {\n return this.container.querySelector('form');\n }\n\n /**\n * Notifies listeners that form dirty state should be reset.\n *\n * @fires event:formSubmittedByJavascript\n */\n notifyResetFormChanges() {\n FormEvents.notifyFormSubmittedByJavascript(this.getFormNode(), true);\n FormChangeChecker.resetFormDirtyState(this.getFormNode());\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\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 }\n\n /**\n * Update form contents\n *\n * @param {object} param\n * @param {string} param.html\n * @param {string} param.js\n * @returns {Promise}\n */\n updateForm({html, js}) {\n return Templates.replaceNodeContents(this.container, html, js);\n }\n\n /**\n * Validate form elements\n * @return {Boolean} Whether client-side validation has passed, false if there are errors\n * @fires event:formSubmittedByJavascript\n */\n validateElements() {\n // Notify listeners that the form is about to be submitted (this will reset atto autosave).\n FormEvents.notifyFormSubmittedByJavascript(this.getFormNode());\n\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 * 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 this.onSubmitSuccess(data);\n }\n return null;\n })\n .catch(exception => this.onSubmitError(exception));\n }\n}\n"],"names":["constructor","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","load","args","formData","pendingPromise","Pending","getBody","then","resp","updateForm","resolve","eventName","CustomEvent","detail","cancelable","dispatchEvent","formDataString","Ajax","call","methodname","formdata","form","response","html","js","Fragment","processCollectedJavascript","javascript","onSubmitSuccess","innerHTML","onSubmitError","button","getFormNode","URLSearchParams","FormData","entries","append","getAttribute","FormEvents","notifyFormSubmittedByJavascript","disableButtons","toString","querySelector","notifyResetFormChanges","FormChangeChecker","resetFormDirtyState","Templates","replaceNodeContents","validateElements","invalid","querySelectorAll","length","focus","forEach","el","setAttribute","enableButtons","removeAttribute","submitted","data","JSON","parse"],"mappings":"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"} \ No newline at end of file diff --git a/lib/form/amd/build/events.min.js b/lib/form/amd/build/events.min.js index 2063180892c..1769f39faba 100644 --- a/lib/form/amd/build/events.min.js +++ b/lib/form/amd/build/events.min.js @@ -14,6 +14,6 @@ define("core_form/events",["exports","core/str","core/event_dispatcher","jquery" * window.console.log(e.target); // The form that was submitted. * window.console.log(e.detail.skipValidation); // Whether form validation was skipped. * }); - */let changesMadeString;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.types=_exports.triggerUploadStarted=_exports.triggerUploadCompleted=_exports.notifyUploadStarted=_exports.notifyUploadCompleted=_exports.notifyUploadChanged=_exports.notifyFormSubmittedByJavascript=_exports.notifyFormError=_exports.notifyFieldValidationFailure=_exports.eventTypes=void 0,_jquery=_interopRequireDefault(_jquery),_yui=_interopRequireDefault(_yui);const changesMadeCheck=e=>{e&&(e.returnValue=changesMadeString)},eventTypes={formError:"core_form/error",formSubmittedByJavascript:"core_form/submittedByJavascript",formFieldValidationFailed:"core_form/fieldValidationFailed",uploadStarted:"core_form/uploadStarted",uploadCompleted:"core_form/uploadCompleted",uploadChanged:"core_form/uploadChanged"};_exports.eventTypes=eventTypes;_exports.notifyFormError=field=>(0,_event_dispatcher.dispatchEvent)(eventTypes.formError,{},field);_exports.notifyFormSubmittedByJavascript=function(form){let skipValidation=arguments.length>1&&void 0!==arguments[1]&&arguments[1],fallbackHandled=arguments.length>2&&void 0!==arguments[2]&&arguments[2];skipValidation&&(window.skipClientValidation=!0);const customEvent=(0,_event_dispatcher.dispatchEvent)(eventTypes.formSubmittedByJavascript,{skipValidation:skipValidation,fallbackHandled:fallbackHandled},form);return skipValidation&&(window.skipClientValidation=!1),customEvent};_exports.notifyFieldValidationFailure=(field,message)=>(0,_event_dispatcher.dispatchEvent)(eventTypes.formFieldValidationFailed,{message:message},field,{cancelable:!0});const notifyUploadStarted=async elementId=>(changesMadeString=await(0,_str.get_string)("changesmadereallygoaway","moodle"),window.addEventListener("beforeunload",changesMadeCheck),(0,_event_dispatcher.dispatchEvent)(eventTypes.uploadStarted,{},document.getElementById(elementId),{bubbles:!0,cancellable:!1}));_exports.notifyUploadStarted=notifyUploadStarted;const notifyUploadCompleted=elementId=>(window.removeEventListener("beforeunload",changesMadeCheck),(0,_event_dispatcher.dispatchEvent)(eventTypes.uploadCompleted,{},document.getElementById(elementId),{bubbles:!0,cancellable:!1}));_exports.notifyUploadCompleted=notifyUploadCompleted;const triggerUploadStarted=notifyUploadStarted;_exports.triggerUploadStarted=triggerUploadStarted;const triggerUploadCompleted=notifyUploadCompleted;_exports.triggerUploadCompleted=triggerUploadCompleted;_exports.types={uploadStarted:"core_form/uploadStarted",uploadCompleted:"core_form/uploadCompleted"};let legacyEventsRegistered=!1;legacyEventsRegistered||(_yui.default.use("event","moodle-core-event",(()=>{document.addEventListener(eventTypes.formError,(e=>{const element=_yui.default.one(e.target),formElement=_yui.default.one(e.target.closest("form"));_yui.default.Global.fire(M.core.globalEvents.FORM_ERROR,{formid:formElement.generateID(),elementid:element.generateID()})})),document.addEventListener(eventTypes.formSubmittedByJavascript,(e=>{if(e.detail.fallbackHandled)return;e.skipValidation&&(window.skipClientValidation=!0);const form=_yui.default.one(e.target);form.fire(M.core.event.FORM_SUBMIT_AJAX,{currentTarget:form,fallbackHandled:!0}),e.skipValidation&&(window.skipClientValidation=!1)}))})),document.addEventListener(eventTypes.formFieldValidationFailed,(e=>{const legacyEvent=_jquery.default.Event("core_form-field-validation");(0,_jquery.default)(e.target).trigger(legacyEvent,e.detail.message)})),legacyEventsRegistered=!0);_exports.notifyUploadChanged=elementId=>(0,_event_dispatcher.dispatchEvent)(eventTypes.uploadChanged,{},document.getElementById(elementId),{bubbles:!0,cancellable:!1})})); + */let changesMadeString;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.types=_exports.triggerUploadStarted=_exports.triggerUploadCompleted=_exports.notifyUploadStarted=_exports.notifyUploadCompleted=_exports.notifyUploadChanged=_exports.notifyFormSubmittedByJavascript=_exports.notifyFormError=_exports.notifyFieldValidationFailure=_exports.eventTypes=void 0,_jquery=_interopRequireDefault(_jquery),_yui=_interopRequireDefault(_yui);const changesMadeCheck=e=>{e&&(e.returnValue=changesMadeString)},eventTypes={formError:"core_form/error",formSubmittedByJavascript:"core_form/submittedByJavascript",formFieldValidationFailed:"core_form/fieldValidationFailed",uploadStarted:"core_form/uploadStarted",uploadCompleted:"core_form/uploadCompleted",uploadChanged:"core_form/uploadChanged"};_exports.eventTypes=eventTypes;_exports.notifyFormError=field=>(0,_event_dispatcher.dispatchEvent)(eventTypes.formError,{},field);_exports.notifyFormSubmittedByJavascript=function(form){let skipValidation=arguments.length>1&&void 0!==arguments[1]&&arguments[1],fallbackHandled=arguments.length>2&&void 0!==arguments[2]&&arguments[2];skipValidation&&(window.skipClientValidation=!0);const customEvent=(0,_event_dispatcher.dispatchEvent)(eventTypes.formSubmittedByJavascript,{skipValidation:skipValidation,fallbackHandled:fallbackHandled},form);return skipValidation&&(window.skipClientValidation=!1),customEvent};_exports.notifyFieldValidationFailure=(field,message)=>(0,_event_dispatcher.dispatchEvent)(eventTypes.formFieldValidationFailed,{message:message},field,{cancelable:!0});const notifyUploadStarted=async elementId=>(changesMadeString=await(0,_str.getString)("changesmadereallygoaway","moodle"),window.addEventListener("beforeunload",changesMadeCheck),(0,_event_dispatcher.dispatchEvent)(eventTypes.uploadStarted,{},document.getElementById(elementId),{bubbles:!0,cancellable:!1}));_exports.notifyUploadStarted=notifyUploadStarted;const notifyUploadCompleted=elementId=>(window.removeEventListener("beforeunload",changesMadeCheck),(0,_event_dispatcher.dispatchEvent)(eventTypes.uploadCompleted,{},document.getElementById(elementId),{bubbles:!0,cancellable:!1}));_exports.notifyUploadCompleted=notifyUploadCompleted;const triggerUploadStarted=notifyUploadStarted;_exports.triggerUploadStarted=triggerUploadStarted;const triggerUploadCompleted=notifyUploadCompleted;_exports.triggerUploadCompleted=triggerUploadCompleted;_exports.types={uploadStarted:"core_form/uploadStarted",uploadCompleted:"core_form/uploadCompleted"};let legacyEventsRegistered=!1;legacyEventsRegistered||(_yui.default.use("event","moodle-core-event",(()=>{document.addEventListener(eventTypes.formError,(e=>{const element=_yui.default.one(e.target),formElement=_yui.default.one(e.target.closest("form"));_yui.default.Global.fire(M.core.globalEvents.FORM_ERROR,{formid:formElement.generateID(),elementid:element.generateID()})})),document.addEventListener(eventTypes.formSubmittedByJavascript,(e=>{if(e.detail.fallbackHandled)return;e.skipValidation&&(window.skipClientValidation=!0);const form=_yui.default.one(e.target);form.fire(M.core.event.FORM_SUBMIT_AJAX,{currentTarget:form,fallbackHandled:!0}),e.skipValidation&&(window.skipClientValidation=!1)}))})),document.addEventListener(eventTypes.formFieldValidationFailed,(e=>{const legacyEvent=_jquery.default.Event("core_form-field-validation");(0,_jquery.default)(e.target).trigger(legacyEvent,e.detail.message)})),legacyEventsRegistered=!0);_exports.notifyUploadChanged=elementId=>(0,_event_dispatcher.dispatchEvent)(eventTypes.uploadChanged,{},document.getElementById(elementId),{bubbles:!0,cancellable:!1})})); //# sourceMappingURL=events.min.js.map \ No newline at end of file diff --git a/lib/form/amd/build/events.min.js.map b/lib/form/amd/build/events.min.js.map index df1bbf3fd6b..f928286812d 100644 --- a/lib/form/amd/build/events.min.js.map +++ b/lib/form/amd/build/events.min.js.map @@ -1 +1 @@ -{"version":3,"file":"events.min.js","sources":["../src/events.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 * Javascript events for the `core_form` subsystem.\n *\n * @module core_form/events\n * @copyright 2021 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.10\n *\n * @example Example of listening to a form event.\n * import {eventTypes as formEventTypes} from 'core_form/events';\n *\n * document.addEventListener(formEventTypes.formSubmittedByJavascript, e => {\n * window.console.log(e.target); // The form that was submitted.\n * window.console.log(e.detail.skipValidation); // Whether form validation was skipped.\n * });\n */\n\nimport {get_string as getString} from 'core/str';\nimport {dispatchEvent} from 'core/event_dispatcher';\n\nlet changesMadeString;\n\n/**\n * Prevent user navigate away when upload progress still running.\n * @param {Event} e The event\n */\nconst changesMadeCheck = e => {\n if (e) {\n e.returnValue = changesMadeString;\n }\n};\n\n/**\n * Events for `core_form`.\n *\n * @constant\n * @property {String} formError See {@link event:core_form/error}\n * @property {String} formFieldValidationFailed See {@link event:core_form/fieldValidationFailed}\n * @property {String} formSubmittedByJavascript See {@link event:core_form/submittedByJavascript}\n * @property {String} uploadChanged See {@link event:core_form/uploadChanged}\n */\nexport const eventTypes = {\n /**\n * An event triggered when a form contains an error\n *\n * @event formError\n * @type {CustomEvent}\n * @property {HTMLElement} target The form field which errored\n */\n formError: 'core_form/error',\n\n /**\n * An event triggered when an mform is about to be submitted via javascript.\n *\n * @event core_form/submittedByJavascript\n * @type {CustomEvent}\n * @property {HTMLElement} target The form that was submitted\n * @property {object} detail\n * @property {boolean} detail.skipValidation Whether the form was submitted without validation (i.e. via a Cancel button)\n * @property {boolean} detail.fallbackHandled Whether the legacy YUI event has been handled\n */\n formSubmittedByJavascript: 'core_form/submittedByJavascript',\n\n /**\n * An event triggered upon form field validation failure.\n *\n * @event core_form/fieldValidationFailed\n * @type {CustomEvent}\n * @property {HTMLElement} target The field that failed validation\n * @property {object} detail\n * @property {String} detail.message The message displayed upon failure\n */\n formFieldValidationFailed: 'core_form/fieldValidationFailed',\n\n /**\n * An event triggered when an upload is started\n *\n * @event core_form/uploadStarted\n * @type {CustomEvent}\n * @property {HTMLElement} target The location where the upload began\n */\n uploadStarted: 'core_form/uploadStarted',\n\n /**\n * An event triggered when an upload completes\n *\n * @event core_form/uploadCompleted\n * @type {CustomEvent}\n * @property {HTMLElement} target The location where the upload completed\n */\n uploadCompleted: 'core_form/uploadCompleted',\n\n /**\n * An event triggered when a file upload field has been changed.\n *\n * @event core_form/uploadChanged\n * @type {CustomEvent}\n * @property {HTMLElement} target The form field which was changed\n */\n uploadChanged: 'core_form/uploadChanged',\n};\n\n// These are only imported for legacy.\nimport jQuery from 'jquery';\nimport Y from 'core/yui';\n\n/**\n * Trigger an event to indicate that a form field contained an error.\n *\n * @method notifyFormError\n * @param {HTMLElement} field The form field causing the error\n * @returns {CustomEvent}\n * @fires formError\n */\nexport const notifyFormError = field => dispatchEvent(eventTypes.formError, {}, field);\n\n/**\n * Trigger an event to indiciate that a form was submitted by Javascript.\n *\n * @method\n * @param {HTMLElement} form The form that was submitted\n * @param {Boolean} skipValidation Submit the form without validation. E.g. \"Cancel\".\n * @param {Boolean} fallbackHandled The legacy YUI event has been handled\n * @returns {CustomEvent}\n * @fires formSubmittedByJavascript\n */\nexport const notifyFormSubmittedByJavascript = (form, skipValidation = false, fallbackHandled = false) => {\n if (skipValidation) {\n window.skipClientValidation = true;\n }\n\n const customEvent = dispatchEvent(\n eventTypes.formSubmittedByJavascript,\n {\n skipValidation,\n fallbackHandled,\n },\n form\n );\n\n if (skipValidation) {\n window.skipClientValidation = false;\n }\n\n return customEvent;\n};\n\n/**\n * Trigger an event to indicate that a form field contained an error.\n *\n * @method notifyFieldValidationFailure\n * @param {HTMLElement} field The field which failed validation\n * @param {String} message The message displayed\n * @returns {CustomEvent}\n * @fires formFieldValidationFailed\n */\nexport const notifyFieldValidationFailure = (field, message) => dispatchEvent(\n eventTypes.formFieldValidationFailed,\n {\n message,\n },\n field,\n {\n cancelable: true\n }\n);\n\n/**\n * Trigger an event to indicate that an upload was started.\n *\n * @method\n * @param {String} elementId The element which was uploaded to\n * @returns {CustomEvent}\n * @fires uploadStarted\n */\nexport const notifyUploadStarted = async elementId => {\n // Add an additional check for changes made.\n changesMadeString = await getString('changesmadereallygoaway', 'moodle');\n window.addEventListener('beforeunload', changesMadeCheck);\n\n return dispatchEvent(\n eventTypes.uploadStarted,\n {},\n document.getElementById(elementId),\n {\n bubbles: true,\n cancellable: false,\n }\n );\n};\n\n/**\n * Trigger an event to indicate that an upload was completed.\n *\n * @method\n * @param {String} elementId The element which was uploaded to\n * @returns {CustomEvent}\n * @fires uploadCompleted\n */\nexport const notifyUploadCompleted = elementId => {\n // Remove the additional check for changes made.\n window.removeEventListener('beforeunload', changesMadeCheck);\n\n return dispatchEvent(\n eventTypes.uploadCompleted,\n {},\n document.getElementById(elementId),\n {\n bubbles: true,\n cancellable: false,\n }\n );\n};\n\n/**\n * Trigger upload start event.\n *\n * @method\n * @param {String} elementId\n * @returns {CustomEvent}\n * @fires uploadStarted\n * @deprecated Since Moodle 4.0 See {@link module:core_form/events.notifyUploadStarted notifyUploadStarted}\n */\nexport const triggerUploadStarted = notifyUploadStarted;\n\n/**\n * Trigger upload complete event.\n *\n * @method\n * @param {String} elementId\n * @returns {CustomEvent}\n * @fires uploadCompleted\n * @deprecated Since Moodle 4.0 See {@link module:core_form/events.notifyUploadCompleted notifyUploadCompleted}\n */\nexport const triggerUploadCompleted = notifyUploadCompleted;\n\n/**\n * List of the events.\n *\n * @deprecated since Moodle 4.0. See {@link module:core_form/events.eventTypes eventTypes} instead.\n **/\nexport const types = {\n uploadStarted: 'core_form/uploadStarted',\n uploadCompleted: 'core_form/uploadCompleted',\n};\n\nlet legacyEventsRegistered = false;\nif (!legacyEventsRegistered) {\n // The following event triggers are legacy and will be removed in the future.\n // The following approach provides a backwards-compatability layer for the new events.\n // Code should be updated to make use of native events.\n Y.use('event', 'moodle-core-event', () => {\n\n // Watch for the new native formError event, and trigger the legacy YUI event.\n document.addEventListener(eventTypes.formError, e => {\n const element = Y.one(e.target);\n const formElement = Y.one(e.target.closest('form'));\n\n Y.Global.fire(\n M.core.globalEvents.FORM_ERROR,\n {\n formid: formElement.generateID(),\n elementid: element.generateID(),\n }\n );\n });\n\n // Watch for the new native formSubmittedByJavascript event, and trigger the legacy YUI event.\n document.addEventListener(eventTypes.formSubmittedByJavascript, e => {\n if (e.detail.fallbackHandled) {\n // This event was originally generated by a YUI event.\n // Do not generate another as this will recurse.\n return;\n }\n\n if (e.skipValidation) {\n window.skipClientValidation = true;\n }\n\n // Trigger the legacy YUI event.\n const form = Y.one(e.target);\n form.fire(\n M.core.event.FORM_SUBMIT_AJAX,\n {\n currentTarget: form,\n fallbackHandled: true,\n }\n );\n\n if (e.skipValidation) {\n window.skipClientValidation = false;\n }\n });\n });\n\n // Watch for the new native formFieldValidationFailed event, and trigger the legacy jQuery event.\n document.addEventListener(eventTypes.formFieldValidationFailed, e => {\n // Note: The \"core_form-field-validation\" event is hard-coded in core/event.\n // This is not included to prevent cyclic module dependencies.\n const legacyEvent = jQuery.Event(\"core_form-field-validation\");\n\n jQuery(e.target).trigger(legacyEvent, e.detail.message);\n });\n\n legacyEventsRegistered = true;\n}\n\n/**\n * Trigger an event to notify the file upload field has been changed.\n *\n * @method\n * @param {string} elementId The element which was changed\n * @returns {CustomEvent}\n * @fires uploadChanged\n */\nexport const notifyUploadChanged = elementId => dispatchEvent(\n eventTypes.uploadChanged,\n {},\n document.getElementById(elementId),\n {\n bubbles: true,\n cancellable: false,\n }\n);\n"],"names":["changesMadeString","changesMadeCheck","e","returnValue","eventTypes","formError","formSubmittedByJavascript","formFieldValidationFailed","uploadStarted","uploadCompleted","uploadChanged","field","form","skipValidation","fallbackHandled","window","skipClientValidation","customEvent","message","cancelable","notifyUploadStarted","async","addEventListener","document","getElementById","elementId","bubbles","cancellable","notifyUploadCompleted","removeEventListener","triggerUploadStarted","triggerUploadCompleted","legacyEventsRegistered","use","element","Y","one","target","formElement","closest","Global","fire","M","core","globalEvents","FORM_ERROR","formid","generateID","elementid","detail","event","FORM_SUBMIT_AJAX","currentTarget","legacyEvent","jQuery","Event","trigger"],"mappings":";;;;;;;;;;;;;;;;SAmCIA,mcAMEC,iBAAmBC,IACjBA,IACAA,EAAEC,YAAcH,oBAaXI,WAAa,CAQtBC,UAAW,kBAYXC,0BAA2B,kCAW3BC,0BAA2B,kCAS3BC,cAAe,0BASfC,gBAAiB,4BASjBC,cAAe,mFAeYC,QAAS,mCAAcP,WAAWC,UAAW,GAAIM,gDAYjC,SAACC,UAAMC,uEAAwBC,wEACtED,iBACAE,OAAOC,sBAAuB,SAG5BC,aAAc,mCAChBb,WAAWE,0BACX,CACIO,eAAAA,eACAC,gBAAAA,iBAEJF,aAGAC,iBACAE,OAAOC,sBAAuB,GAG3BC,mDAYiC,CAACN,MAAOO,WAAY,mCAC5Dd,WAAWG,0BACX,CACIW,QAAAA,SAEJP,MACA,CACIQ,YAAY,UAYPC,oBAAsBC,MAAAA,YAE/BrB,wBAA0B,mBAAU,0BAA2B,UAC/De,OAAOO,iBAAiB,eAAgBrB,mBAEjC,mCACHG,WAAWI,cACX,GACAe,SAASC,eAAeC,WACxB,CACIC,SAAS,EACTC,aAAa,4DAaZC,sBAAwBH,YAEjCV,OAAOc,oBAAoB,eAAgB5B,mBAEpC,mCACHG,WAAWK,gBACX,GACAc,SAASC,eAAeC,WACxB,CACIC,SAAS,EACTC,aAAa,gEAcZG,qBAAuBV,6EAWvBW,uBAAyBH,4FAOjB,CACjBpB,cAAe,0BACfC,gBAAiB,iCAGjBuB,wBAAyB,EACxBA,sCAICC,IAAI,QAAS,qBAAqB,KAGhCV,SAASD,iBAAiBlB,WAAWC,WAAWH,UACtCgC,QAAUC,aAAEC,IAAIlC,EAAEmC,QAClBC,YAAcH,aAAEC,IAAIlC,EAAEmC,OAAOE,QAAQ,sBAEzCC,OAAOC,KACLC,EAAEC,KAAKC,aAAaC,WACpB,CACIC,OAAQR,YAAYS,aACpBC,UAAWd,QAAQa,kBAM/BxB,SAASD,iBAAiBlB,WAAWE,2BAA2BJ,OACxDA,EAAE+C,OAAOnC,uBAMTZ,EAAEW,iBACFE,OAAOC,sBAAuB,SAI5BJ,KAAOuB,aAAEC,IAAIlC,EAAEmC,QACrBzB,KAAK6B,KACDC,EAAEC,KAAKO,MAAMC,iBACb,CACIC,cAAexC,KACfE,iBAAiB,IAIrBZ,EAAEW,iBACFE,OAAOC,sBAAuB,SAM1CO,SAASD,iBAAiBlB,WAAWG,2BAA2BL,UAGtDmD,YAAcC,gBAAOC,MAAM,kDAE1BrD,EAAEmC,QAAQmB,QAAQH,YAAanD,EAAE+C,OAAO/B,YAGnDc,wBAAyB,gCAWMP,YAAa,mCAC5CrB,WAAWM,cACX,GACAa,SAASC,eAAeC,WACxB,CACIC,SAAS,EACTC,aAAa"} \ No newline at end of file +{"version":3,"file":"events.min.js","sources":["../src/events.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 * Javascript events for the `core_form` subsystem.\n *\n * @module core_form/events\n * @copyright 2021 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.10\n *\n * @example Example of listening to a form event.\n * import {eventTypes as formEventTypes} from 'core_form/events';\n *\n * document.addEventListener(formEventTypes.formSubmittedByJavascript, e => {\n * window.console.log(e.target); // The form that was submitted.\n * window.console.log(e.detail.skipValidation); // Whether form validation was skipped.\n * });\n */\n\nimport {getString} from 'core/str';\nimport {dispatchEvent} from 'core/event_dispatcher';\n\nlet changesMadeString;\n\n/**\n * Prevent user navigate away when upload progress still running.\n * @param {Event} e The event\n */\nconst changesMadeCheck = e => {\n if (e) {\n e.returnValue = changesMadeString;\n }\n};\n\n/**\n * Events for `core_form`.\n *\n * @constant\n * @property {String} formError See {@link event:core_form/error}\n * @property {String} formFieldValidationFailed See {@link event:core_form/fieldValidationFailed}\n * @property {String} formSubmittedByJavascript See {@link event:core_form/submittedByJavascript}\n * @property {String} uploadChanged See {@link event:core_form/uploadChanged}\n */\nexport const eventTypes = {\n /**\n * An event triggered when a form contains an error\n *\n * @event formError\n * @type {CustomEvent}\n * @property {HTMLElement} target The form field which errored\n */\n formError: 'core_form/error',\n\n /**\n * An event triggered when an mform is about to be submitted via javascript.\n *\n * @event core_form/submittedByJavascript\n * @type {CustomEvent}\n * @property {HTMLElement} target The form that was submitted\n * @property {object} detail\n * @property {boolean} detail.skipValidation Whether the form was submitted without validation (i.e. via a Cancel button)\n * @property {boolean} detail.fallbackHandled Whether the legacy YUI event has been handled\n */\n formSubmittedByJavascript: 'core_form/submittedByJavascript',\n\n /**\n * An event triggered upon form field validation failure.\n *\n * @event core_form/fieldValidationFailed\n * @type {CustomEvent}\n * @property {HTMLElement} target The field that failed validation\n * @property {object} detail\n * @property {String} detail.message The message displayed upon failure\n */\n formFieldValidationFailed: 'core_form/fieldValidationFailed',\n\n /**\n * An event triggered when an upload is started\n *\n * @event core_form/uploadStarted\n * @type {CustomEvent}\n * @property {HTMLElement} target The location where the upload began\n */\n uploadStarted: 'core_form/uploadStarted',\n\n /**\n * An event triggered when an upload completes\n *\n * @event core_form/uploadCompleted\n * @type {CustomEvent}\n * @property {HTMLElement} target The location where the upload completed\n */\n uploadCompleted: 'core_form/uploadCompleted',\n\n /**\n * An event triggered when a file upload field has been changed.\n *\n * @event core_form/uploadChanged\n * @type {CustomEvent}\n * @property {HTMLElement} target The form field which was changed\n */\n uploadChanged: 'core_form/uploadChanged',\n};\n\n// These are only imported for legacy.\nimport jQuery from 'jquery';\nimport Y from 'core/yui';\n\n/**\n * Trigger an event to indicate that a form field contained an error.\n *\n * @method notifyFormError\n * @param {HTMLElement} field The form field causing the error\n * @returns {CustomEvent}\n * @fires formError\n */\nexport const notifyFormError = field => dispatchEvent(eventTypes.formError, {}, field);\n\n/**\n * Trigger an event to indiciate that a form was submitted by Javascript.\n *\n * @method\n * @param {HTMLElement} form The form that was submitted\n * @param {Boolean} skipValidation Submit the form without validation. E.g. \"Cancel\".\n * @param {Boolean} fallbackHandled The legacy YUI event has been handled\n * @returns {CustomEvent}\n * @fires formSubmittedByJavascript\n */\nexport const notifyFormSubmittedByJavascript = (form, skipValidation = false, fallbackHandled = false) => {\n if (skipValidation) {\n window.skipClientValidation = true;\n }\n\n const customEvent = dispatchEvent(\n eventTypes.formSubmittedByJavascript,\n {\n skipValidation,\n fallbackHandled,\n },\n form\n );\n\n if (skipValidation) {\n window.skipClientValidation = false;\n }\n\n return customEvent;\n};\n\n/**\n * Trigger an event to indicate that a form field contained an error.\n *\n * @method notifyFieldValidationFailure\n * @param {HTMLElement} field The field which failed validation\n * @param {String} message The message displayed\n * @returns {CustomEvent}\n * @fires formFieldValidationFailed\n */\nexport const notifyFieldValidationFailure = (field, message) => dispatchEvent(\n eventTypes.formFieldValidationFailed,\n {\n message,\n },\n field,\n {\n cancelable: true\n }\n);\n\n/**\n * Trigger an event to indicate that an upload was started.\n *\n * @method\n * @param {String} elementId The element which was uploaded to\n * @returns {CustomEvent}\n * @fires uploadStarted\n */\nexport const notifyUploadStarted = async elementId => {\n // Add an additional check for changes made.\n changesMadeString = await getString('changesmadereallygoaway', 'moodle');\n window.addEventListener('beforeunload', changesMadeCheck);\n\n return dispatchEvent(\n eventTypes.uploadStarted,\n {},\n document.getElementById(elementId),\n {\n bubbles: true,\n cancellable: false,\n }\n );\n};\n\n/**\n * Trigger an event to indicate that an upload was completed.\n *\n * @method\n * @param {String} elementId The element which was uploaded to\n * @returns {CustomEvent}\n * @fires uploadCompleted\n */\nexport const notifyUploadCompleted = elementId => {\n // Remove the additional check for changes made.\n window.removeEventListener('beforeunload', changesMadeCheck);\n\n return dispatchEvent(\n eventTypes.uploadCompleted,\n {},\n document.getElementById(elementId),\n {\n bubbles: true,\n cancellable: false,\n }\n );\n};\n\n/**\n * Trigger upload start event.\n *\n * @method\n * @param {String} elementId\n * @returns {CustomEvent}\n * @fires uploadStarted\n * @deprecated Since Moodle 4.0 See {@link module:core_form/events.notifyUploadStarted notifyUploadStarted}\n */\nexport const triggerUploadStarted = notifyUploadStarted;\n\n/**\n * Trigger upload complete event.\n *\n * @method\n * @param {String} elementId\n * @returns {CustomEvent}\n * @fires uploadCompleted\n * @deprecated Since Moodle 4.0 See {@link module:core_form/events.notifyUploadCompleted notifyUploadCompleted}\n */\nexport const triggerUploadCompleted = notifyUploadCompleted;\n\n/**\n * List of the events.\n *\n * @deprecated since Moodle 4.0. See {@link module:core_form/events.eventTypes eventTypes} instead.\n **/\nexport const types = {\n uploadStarted: 'core_form/uploadStarted',\n uploadCompleted: 'core_form/uploadCompleted',\n};\n\nlet legacyEventsRegistered = false;\nif (!legacyEventsRegistered) {\n // The following event triggers are legacy and will be removed in the future.\n // The following approach provides a backwards-compatability layer for the new events.\n // Code should be updated to make use of native events.\n Y.use('event', 'moodle-core-event', () => {\n\n // Watch for the new native formError event, and trigger the legacy YUI event.\n document.addEventListener(eventTypes.formError, e => {\n const element = Y.one(e.target);\n const formElement = Y.one(e.target.closest('form'));\n\n Y.Global.fire(\n M.core.globalEvents.FORM_ERROR,\n {\n formid: formElement.generateID(),\n elementid: element.generateID(),\n }\n );\n });\n\n // Watch for the new native formSubmittedByJavascript event, and trigger the legacy YUI event.\n document.addEventListener(eventTypes.formSubmittedByJavascript, e => {\n if (e.detail.fallbackHandled) {\n // This event was originally generated by a YUI event.\n // Do not generate another as this will recurse.\n return;\n }\n\n if (e.skipValidation) {\n window.skipClientValidation = true;\n }\n\n // Trigger the legacy YUI event.\n const form = Y.one(e.target);\n form.fire(\n M.core.event.FORM_SUBMIT_AJAX,\n {\n currentTarget: form,\n fallbackHandled: true,\n }\n );\n\n if (e.skipValidation) {\n window.skipClientValidation = false;\n }\n });\n });\n\n // Watch for the new native formFieldValidationFailed event, and trigger the legacy jQuery event.\n document.addEventListener(eventTypes.formFieldValidationFailed, e => {\n // Note: The \"core_form-field-validation\" event is hard-coded in core/event.\n // This is not included to prevent cyclic module dependencies.\n const legacyEvent = jQuery.Event(\"core_form-field-validation\");\n\n jQuery(e.target).trigger(legacyEvent, e.detail.message);\n });\n\n legacyEventsRegistered = true;\n}\n\n/**\n * Trigger an event to notify the file upload field has been changed.\n *\n * @method\n * @param {string} elementId The element which was changed\n * @returns {CustomEvent}\n * @fires uploadChanged\n */\nexport const notifyUploadChanged = elementId => dispatchEvent(\n eventTypes.uploadChanged,\n {},\n document.getElementById(elementId),\n {\n bubbles: true,\n cancellable: false,\n }\n);\n"],"names":["changesMadeString","changesMadeCheck","e","returnValue","eventTypes","formError","formSubmittedByJavascript","formFieldValidationFailed","uploadStarted","uploadCompleted","uploadChanged","field","form","skipValidation","fallbackHandled","window","skipClientValidation","customEvent","message","cancelable","notifyUploadStarted","async","addEventListener","document","getElementById","elementId","bubbles","cancellable","notifyUploadCompleted","removeEventListener","triggerUploadStarted","triggerUploadCompleted","legacyEventsRegistered","use","element","Y","one","target","formElement","closest","Global","fire","M","core","globalEvents","FORM_ERROR","formid","generateID","elementid","detail","event","FORM_SUBMIT_AJAX","currentTarget","legacyEvent","jQuery","Event","trigger"],"mappings":";;;;;;;;;;;;;;;;SAmCIA,mcAMEC,iBAAmBC,IACjBA,IACAA,EAAEC,YAAcH,oBAaXI,WAAa,CAQtBC,UAAW,kBAYXC,0BAA2B,kCAW3BC,0BAA2B,kCAS3BC,cAAe,0BASfC,gBAAiB,4BASjBC,cAAe,mFAeYC,QAAS,mCAAcP,WAAWC,UAAW,GAAIM,gDAYjC,SAACC,UAAMC,uEAAwBC,wEACtED,iBACAE,OAAOC,sBAAuB,SAG5BC,aAAc,mCAChBb,WAAWE,0BACX,CACIO,eAAAA,eACAC,gBAAAA,iBAEJF,aAGAC,iBACAE,OAAOC,sBAAuB,GAG3BC,mDAYiC,CAACN,MAAOO,WAAY,mCAC5Dd,WAAWG,0BACX,CACIW,QAAAA,SAEJP,MACA,CACIQ,YAAY,UAYPC,oBAAsBC,MAAAA,YAE/BrB,wBAA0B,kBAAU,0BAA2B,UAC/De,OAAOO,iBAAiB,eAAgBrB,mBAEjC,mCACHG,WAAWI,cACX,GACAe,SAASC,eAAeC,WACxB,CACIC,SAAS,EACTC,aAAa,4DAaZC,sBAAwBH,YAEjCV,OAAOc,oBAAoB,eAAgB5B,mBAEpC,mCACHG,WAAWK,gBACX,GACAc,SAASC,eAAeC,WACxB,CACIC,SAAS,EACTC,aAAa,gEAcZG,qBAAuBV,6EAWvBW,uBAAyBH,4FAOjB,CACjBpB,cAAe,0BACfC,gBAAiB,iCAGjBuB,wBAAyB,EACxBA,sCAICC,IAAI,QAAS,qBAAqB,KAGhCV,SAASD,iBAAiBlB,WAAWC,WAAWH,UACtCgC,QAAUC,aAAEC,IAAIlC,EAAEmC,QAClBC,YAAcH,aAAEC,IAAIlC,EAAEmC,OAAOE,QAAQ,sBAEzCC,OAAOC,KACLC,EAAEC,KAAKC,aAAaC,WACpB,CACIC,OAAQR,YAAYS,aACpBC,UAAWd,QAAQa,kBAM/BxB,SAASD,iBAAiBlB,WAAWE,2BAA2BJ,OACxDA,EAAE+C,OAAOnC,uBAMTZ,EAAEW,iBACFE,OAAOC,sBAAuB,SAI5BJ,KAAOuB,aAAEC,IAAIlC,EAAEmC,QACrBzB,KAAK6B,KACDC,EAAEC,KAAKO,MAAMC,iBACb,CACIC,cAAexC,KACfE,iBAAiB,IAIrBZ,EAAEW,iBACFE,OAAOC,sBAAuB,SAM1CO,SAASD,iBAAiBlB,WAAWG,2BAA2BL,UAGtDmD,YAAcC,gBAAOC,MAAM,kDAE1BrD,EAAEmC,QAAQmB,QAAQH,YAAanD,EAAE+C,OAAO/B,YAGnDc,wBAAyB,gCAWMP,YAAa,mCAC5CrB,WAAWM,cACX,GACAa,SAASC,eAAeC,WACxB,CACIC,SAAS,EACTC,aAAa"} \ No newline at end of file diff --git a/lib/form/amd/src/changechecker.js b/lib/form/amd/src/changechecker.js index 9acae70b1f7..2cd64fcdd7e 100644 --- a/lib/form/amd/src/changechecker.js +++ b/lib/form/amd/src/changechecker.js @@ -64,7 +64,7 @@ */ import {eventTypes} from 'core_editor/events'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; /** * @property {Bool} initialised Whether the change checker has been initialised diff --git a/lib/form/amd/src/dynamicform.js b/lib/form/amd/src/dynamicform.js index 0eae1c51837..6342d231369 100644 --- a/lib/form/amd/src/dynamicform.js +++ b/lib/form/amd/src/dynamicform.js @@ -42,7 +42,7 @@ import Fragment from 'core/fragment'; import Notification from 'core/notification'; import Pending from 'core/pending'; import Templates from 'core/templates'; -import {get_strings as getStrings} from 'core/str'; +import {getStrings} from 'core/str'; import {serialize} from './util'; /** diff --git a/lib/form/amd/src/events.js b/lib/form/amd/src/events.js index 53dcb9671f1..2cbedb241e0 100644 --- a/lib/form/amd/src/events.js +++ b/lib/form/amd/src/events.js @@ -30,7 +30,7 @@ * }); */ -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {dispatchEvent} from 'core/event_dispatcher'; let changesMadeString; diff --git a/mod/assign/amd/build/timer.min.js b/mod/assign/amd/build/timer.min.js index 4c266d36ad1..11ad16ee23d 100644 --- a/mod/assign/amd/build/timer.min.js +++ b/mod/assign/amd/build/timer.min.js @@ -4,6 +4,6 @@ define("mod_assign/timer",["exports","core/notification","core/str"],(function(_ * * @copyright 2020 Matt Porritt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};let endTime=0,timeoutId=null,timer=null;const update=()=>{const now=(new Date).getTime(),secondsLeft=Math.floor((endTime-now)/1e3);if(secondsLeft<=0)return timer.classList.add("alert","alert-danger"),timer.innerHTML="00:00:00",document.getElementById("mod_assign_timelimit_block")&&(0,_str.get_string)("caneditsubmission","mod_assign").then((message=>_notification.default.addNotification({message:message}))).catch(_notification.default.exception),void(timeoutId&&clearTimeout(timeoutId));var secs;secondsLeft<300?(timer.classList.remove("alert-warning"),timer.classList.add("alert","alert-danger")):secondsLeft<900&&(timer.classList.remove("alert-danger"),timer.classList.add("alert","alert-warning")),timer.innerHTML=(secs=secondsLeft,[Math.floor(secs/3600),Math.floor(secs/60)%60,secs%60].filter(((value,index)=>0!==value||index>0)).map((value=>"".concat(value).padStart(2,"0"))).join(":")),timeoutId=setTimeout(update,500)};_exports.init=timerId=>{timer=document.getElementById(timerId),endTime=M.pageloadstarttime.getTime()+1e3*timer.dataset.starttime,update()}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};let endTime=0,timeoutId=null,timer=null;const update=()=>{const now=(new Date).getTime(),secondsLeft=Math.floor((endTime-now)/1e3);if(secondsLeft<=0)return timer.classList.add("alert","alert-danger"),timer.innerHTML="00:00:00",document.getElementById("mod_assign_timelimit_block")&&(0,_str.getString)("caneditsubmission","mod_assign").then((message=>_notification.default.addNotification({message:message}))).catch(_notification.default.exception),void(timeoutId&&clearTimeout(timeoutId));var secs;secondsLeft<300?(timer.classList.remove("alert-warning"),timer.classList.add("alert","alert-danger")):secondsLeft<900&&(timer.classList.remove("alert-danger"),timer.classList.add("alert","alert-warning")),timer.innerHTML=(secs=secondsLeft,[Math.floor(secs/3600),Math.floor(secs/60)%60,secs%60].filter(((value,index)=>0!==value||index>0)).map((value=>"".concat(value).padStart(2,"0"))).join(":")),timeoutId=setTimeout(update,500)};_exports.init=timerId=>{timer=document.getElementById(timerId),endTime=M.pageloadstarttime.getTime()+1e3*timer.dataset.starttime,update()}})); //# sourceMappingURL=timer.min.js.map \ No newline at end of file diff --git a/mod/assign/amd/build/timer.min.js.map b/mod/assign/amd/build/timer.min.js.map index d09357dd3fd..07a0fe4278a 100644 --- a/mod/assign/amd/build/timer.min.js.map +++ b/mod/assign/amd/build/timer.min.js.map @@ -1 +1 @@ -{"version":3,"file":"timer.min.js","sources":["../src/timer.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\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\n\n/**\n * A javascript module for the time in the assign module.\n *\n * @copyright 2020 Matt Porritt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Timestamp at which time runs out.\n *\n * @property {Number} endTime\n */\nlet endTime = 0;\n\n/**\n * ID of the timeout that updates the clock.\n *\n * @property {Number} timeoutId\n */\nlet timeoutId = null;\n\n/**\n * The timer element.\n *\n * @property {Element} timer\n */\nlet timer = null;\n\n/**\n * Helper method to convert time remaining in seconds into HH:MM:SS format.\n *\n * @method formatSeconds\n * @param {Number} secs Time remaining in seconds to get value for.\n * @return {String} Time remaining in HH:MM:SS format.\n */\nconst formatSeconds = (secs) => {\n const hours = Math.floor(secs / 3600);\n const minutes = Math.floor(secs / 60) % 60;\n const seconds = secs % 60;\n\n return [hours, minutes, seconds]\n // Remove the hours column if there is less than 1 hour left.\n .filter((value, index) => value !== 0 || index > 0)\n // Ensure that all fields are two digit numbers.\n .map(value => `${value}`.padStart(2, '0'))\n .join(\":\");\n};\n\n/**\n * Stop the timer, if it is running.\n *\n * @method stop\n */\nconst stop = () => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n};\n\n/**\n * Function to update the clock with the current time left.\n *\n * @method update\n */\nconst update = () => {\n const now = new Date().getTime();\n const secondsLeft = Math.floor((endTime - now) / 1000);\n\n // If time has expired, set the hidden form field that says time has expired.\n if (secondsLeft <= 0) {\n timer.classList.add('alert', 'alert-danger');\n timer.innerHTML = '00:00:00';\n\n // Only add a notification on the assign submission page.\n if (document.getElementById(\"mod_assign_timelimit_block\")) {\n getString('caneditsubmission', 'mod_assign')\n .then(message => Notification.addNotification({message}))\n .catch(Notification.exception);\n }\n\n stop();\n return;\n } else if (secondsLeft < 300) { // Add danger style when less than 5 minutes left.\n timer.classList.remove('alert-warning');\n timer.classList.add('alert', 'alert-danger');\n } else if (secondsLeft < 900) { // Add warning style when less than 15 minutes left.\n timer.classList.remove('alert-danger');\n timer.classList.add('alert', 'alert-warning');\n }\n\n // Update the time display.\n timer.innerHTML = formatSeconds(secondsLeft);\n\n // Arrange for this method to be called again soon.\n timeoutId = setTimeout(update, 500);\n};\n\n/**\n * Set up the submission timer.\n *\n * @method init\n * @param {Number} timerId Unique ID of the timer element.\n */\nexport const init = (timerId) => {\n timer = document.getElementById(timerId);\n endTime = M.pageloadstarttime.getTime() + (timer.dataset.starttime * 1000);\n update();\n};\n"],"names":["endTime","timeoutId","timer","update","now","Date","getTime","secondsLeft","Math","floor","classList","add","innerHTML","document","getElementById","then","message","Notification","addNotification","catch","exception","clearTimeout","secs","remove","filter","value","index","map","padStart","join","setTimeout","timerId","M","pageloadstarttime","dataset","starttime"],"mappings":";;;;;;0JA8BIA,QAAU,EAOVC,UAAY,KAOZC,MAAQ,WAsCNC,OAAS,WACLC,KAAM,IAAIC,MAAOC,UACjBC,YAAcC,KAAKC,OAAOT,QAAUI,KAAO,QAG7CG,aAAe,SACfL,MAAMQ,UAAUC,IAAI,QAAS,gBAC7BT,MAAMU,UAAY,WAGdC,SAASC,eAAe,mDACd,oBAAqB,cAC1BC,MAAKC,SAAWC,sBAAaC,gBAAgB,CAACF,QAAAA,YAC9CG,MAAMF,sBAAaG,gBAvB5BnB,WACAoB,aAAapB,YApBEqB,IAAAA,KA+CRf,YAAc,KACrBL,MAAMQ,UAAUa,OAAO,iBACvBrB,MAAMQ,UAAUC,IAAI,QAAS,iBACtBJ,YAAc,MACrBL,MAAMQ,UAAUa,OAAO,gBACvBrB,MAAMQ,UAAUC,IAAI,QAAS,kBAIjCT,MAAMU,WAxDaU,KAwDaf,YAnDzB,CAJOC,KAAKC,MAAMa,KAAO,MAChBd,KAAKC,MAAMa,KAAO,IAAM,GACxBA,KAAO,IAIlBE,QAAO,CAACC,MAAOC,QAAoB,IAAVD,OAAeC,MAAQ,IAEhDC,KAAIF,OAAS,UAAGA,OAAQG,SAAS,EAAG,OACpCC,KAAK,MAiDV5B,UAAY6B,WAAW3B,OAAQ,oBASd4B,UACjB7B,MAAQW,SAASC,eAAeiB,SAChC/B,QAAUgC,EAAEC,kBAAkB3B,UAAuC,IAA1BJ,MAAMgC,QAAQC,UACzDhC"} \ No newline at end of file +{"version":3,"file":"timer.min.js","sources":["../src/timer.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\nimport Notification from 'core/notification';\nimport {getString} from 'core/str';\n\n/**\n * A javascript module for the time in the assign module.\n *\n * @copyright 2020 Matt Porritt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Timestamp at which time runs out.\n *\n * @property {Number} endTime\n */\nlet endTime = 0;\n\n/**\n * ID of the timeout that updates the clock.\n *\n * @property {Number} timeoutId\n */\nlet timeoutId = null;\n\n/**\n * The timer element.\n *\n * @property {Element} timer\n */\nlet timer = null;\n\n/**\n * Helper method to convert time remaining in seconds into HH:MM:SS format.\n *\n * @method formatSeconds\n * @param {Number} secs Time remaining in seconds to get value for.\n * @return {String} Time remaining in HH:MM:SS format.\n */\nconst formatSeconds = (secs) => {\n const hours = Math.floor(secs / 3600);\n const minutes = Math.floor(secs / 60) % 60;\n const seconds = secs % 60;\n\n return [hours, minutes, seconds]\n // Remove the hours column if there is less than 1 hour left.\n .filter((value, index) => value !== 0 || index > 0)\n // Ensure that all fields are two digit numbers.\n .map(value => `${value}`.padStart(2, '0'))\n .join(\":\");\n};\n\n/**\n * Stop the timer, if it is running.\n *\n * @method stop\n */\nconst stop = () => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n};\n\n/**\n * Function to update the clock with the current time left.\n *\n * @method update\n */\nconst update = () => {\n const now = new Date().getTime();\n const secondsLeft = Math.floor((endTime - now) / 1000);\n\n // If time has expired, set the hidden form field that says time has expired.\n if (secondsLeft <= 0) {\n timer.classList.add('alert', 'alert-danger');\n timer.innerHTML = '00:00:00';\n\n // Only add a notification on the assign submission page.\n if (document.getElementById(\"mod_assign_timelimit_block\")) {\n getString('caneditsubmission', 'mod_assign')\n .then(message => Notification.addNotification({message}))\n .catch(Notification.exception);\n }\n\n stop();\n return;\n } else if (secondsLeft < 300) { // Add danger style when less than 5 minutes left.\n timer.classList.remove('alert-warning');\n timer.classList.add('alert', 'alert-danger');\n } else if (secondsLeft < 900) { // Add warning style when less than 15 minutes left.\n timer.classList.remove('alert-danger');\n timer.classList.add('alert', 'alert-warning');\n }\n\n // Update the time display.\n timer.innerHTML = formatSeconds(secondsLeft);\n\n // Arrange for this method to be called again soon.\n timeoutId = setTimeout(update, 500);\n};\n\n/**\n * Set up the submission timer.\n *\n * @method init\n * @param {Number} timerId Unique ID of the timer element.\n */\nexport const init = (timerId) => {\n timer = document.getElementById(timerId);\n endTime = M.pageloadstarttime.getTime() + (timer.dataset.starttime * 1000);\n update();\n};\n"],"names":["endTime","timeoutId","timer","update","now","Date","getTime","secondsLeft","Math","floor","classList","add","innerHTML","document","getElementById","then","message","Notification","addNotification","catch","exception","clearTimeout","secs","remove","filter","value","index","map","padStart","join","setTimeout","timerId","M","pageloadstarttime","dataset","starttime"],"mappings":";;;;;;0JA8BIA,QAAU,EAOVC,UAAY,KAOZC,MAAQ,WAsCNC,OAAS,WACLC,KAAM,IAAIC,MAAOC,UACjBC,YAAcC,KAAKC,OAAOT,QAAUI,KAAO,QAG7CG,aAAe,SACfL,MAAMQ,UAAUC,IAAI,QAAS,gBAC7BT,MAAMU,UAAY,WAGdC,SAASC,eAAe,kDACd,oBAAqB,cAC1BC,MAAKC,SAAWC,sBAAaC,gBAAgB,CAACF,QAAAA,YAC9CG,MAAMF,sBAAaG,gBAvB5BnB,WACAoB,aAAapB,YApBEqB,IAAAA,KA+CRf,YAAc,KACrBL,MAAMQ,UAAUa,OAAO,iBACvBrB,MAAMQ,UAAUC,IAAI,QAAS,iBACtBJ,YAAc,MACrBL,MAAMQ,UAAUa,OAAO,gBACvBrB,MAAMQ,UAAUC,IAAI,QAAS,kBAIjCT,MAAMU,WAxDaU,KAwDaf,YAnDzB,CAJOC,KAAKC,MAAMa,KAAO,MAChBd,KAAKC,MAAMa,KAAO,IAAM,GACxBA,KAAO,IAIlBE,QAAO,CAACC,MAAOC,QAAoB,IAAVD,OAAeC,MAAQ,IAEhDC,KAAIF,OAAS,UAAGA,OAAQG,SAAS,EAAG,OACpCC,KAAK,MAiDV5B,UAAY6B,WAAW3B,OAAQ,oBASd4B,UACjB7B,MAAQW,SAASC,eAAeiB,SAChC/B,QAAUgC,EAAEC,kBAAkB3B,UAAuC,IAA1BJ,MAAMgC,QAAQC,UACzDhC"} \ No newline at end of file diff --git a/mod/assign/amd/src/timer.js b/mod/assign/amd/src/timer.js index 1ce7cce0d33..5588dfe2a2b 100644 --- a/mod/assign/amd/src/timer.js +++ b/mod/assign/amd/src/timer.js @@ -14,7 +14,7 @@ // along with Moodle. If not, see . import Notification from 'core/notification'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; /** * A javascript module for the time in the assign module. diff --git a/mod/bigbluebuttonbn/amd/build/actions.min.js b/mod/bigbluebuttonbn/amd/build/actions.min.js index 188908bb66f..cb86bed5a17 100644 --- a/mod/bigbluebuttonbn/amd/build/actions.min.js +++ b/mod/bigbluebuttonbn/amd/build/actions.min.js @@ -1,3 +1,3 @@ -define("mod_bigbluebuttonbn/actions",["./repository","core/notification","./events","core/str"],(function(_repository,_notification,_events,_str){let listening=!1;listening||((()=>{document.addEventListener("click",(e=>{const actionButton=e.target.closest('.bbb-btn-action[data-action="end"]');if(!actionButton)return;e.preventDefault();const bbbId=actionButton.dataset.bbbId,groupId=actionButton.dataset.groupId?actionButton.dataset.groupId:0;var title,question,saveLabel;(title=(0,_str.get_string)("end_session_confirm_title","mod_bigbluebuttonbn"),question=(0,_str.get_string)("end_session_confirm","mod_bigbluebuttonbn"),saveLabel=(0,_str.get_string)("yes","moodle"),new Promise((resolve=>{(0,_notification.saveCancel)(title,question,saveLabel,resolve)}))).then((()=>(0,_repository.endMeeting)(bbbId,groupId))).then((()=>{(0,_events.notifySessionEnded)(bbbId,groupId)})).catch(_notification.exception)}))})(),listening=!0)})); +define("mod_bigbluebuttonbn/actions",["./repository","core/notification","./events","core/str"],(function(_repository,_notification,_events,_str){let listening=!1;listening||((()=>{document.addEventListener("click",(e=>{const actionButton=e.target.closest('.bbb-btn-action[data-action="end"]');if(!actionButton)return;e.preventDefault();const bbbId=actionButton.dataset.bbbId,groupId=actionButton.dataset.groupId?actionButton.dataset.groupId:0;var title,question,saveLabel;(title=(0,_str.getString)("end_session_confirm_title","mod_bigbluebuttonbn"),question=(0,_str.getString)("end_session_confirm","mod_bigbluebuttonbn"),saveLabel=(0,_str.getString)("yes","moodle"),new Promise((resolve=>{(0,_notification.saveCancel)(title,question,saveLabel,resolve)}))).then((()=>(0,_repository.endMeeting)(bbbId,groupId))).then((()=>{(0,_events.notifySessionEnded)(bbbId,groupId)})).catch(_notification.exception)}))})(),listening=!0)})); //# sourceMappingURL=actions.min.js.map \ No newline at end of file diff --git a/mod/bigbluebuttonbn/amd/build/actions.min.js.map b/mod/bigbluebuttonbn/amd/build/actions.min.js.map index 6191dc5ba02..59f8fdd34f0 100644 --- a/mod/bigbluebuttonbn/amd/build/actions.min.js.map +++ b/mod/bigbluebuttonbn/amd/build/actions.min.js.map @@ -1 +1 @@ -{"version":3,"file":"actions.min.js","sources":["../src/actions.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 * JS actions.\n *\n * @module mod_bigbluebuttonbn/actions\n * @copyright 2021 Blindside Networks Inc\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {endMeeting as requestEndMeeting} from './repository';\nimport {\n exception as displayException,\n saveCancel,\n} from 'core/notification';\nimport {notifySessionEnded} from './events';\nimport {get_string as getString} from 'core/str';\n\nconst confirmedPromise = (title, question, saveLabel) => new Promise(resolve => {\n saveCancel(title, question, saveLabel, resolve);\n});\n\nconst registerEventListeners = () => {\n document.addEventListener('click', e => {\n const actionButton = e.target.closest('.bbb-btn-action[data-action=\"end\"]');\n if (!actionButton) {\n return;\n }\n\n e.preventDefault();\n\n const bbbId = actionButton.dataset.bbbId;\n const groupId = actionButton.dataset.groupId ? actionButton.dataset.groupId : 0;\n\n confirmedPromise(\n getString('end_session_confirm_title', 'mod_bigbluebuttonbn'),\n getString('end_session_confirm', 'mod_bigbluebuttonbn'),\n getString('yes', 'moodle')\n )\n .then(() => requestEndMeeting(bbbId, groupId))\n .then(() => {\n notifySessionEnded(bbbId, groupId);\n\n return;\n })\n .catch(displayException);\n });\n};\n\nlet listening = false;\nif (!listening) {\n registerEventListeners();\n listening = true;\n}\n"],"names":["listening","document","addEventListener","e","actionButton","target","closest","preventDefault","bbbId","dataset","groupId","title","question","saveLabel","Promise","resolve","then","catch","displayException","registerEventListeners"],"mappings":"sJA6DIA,WAAY,EACXA,YA5B0B,MAC3BC,SAASC,iBAAiB,SAASC,UACzBC,aAAeD,EAAEE,OAAOC,QAAQ,0CACjCF,oBAILD,EAAEI,uBAEIC,MAAQJ,aAAaK,QAAQD,MAC7BE,QAAUN,aAAaK,QAAQC,QAAUN,aAAaK,QAAQC,QAAU,EAd7D,IAACC,MAAOC,SAAUC,WAAjBF,OAiBd,mBAAU,4BAA6B,uBAjBlBC,UAkBrB,mBAAU,sBAAuB,uBAlBFC,WAmB/B,mBAAU,MAAO,UAnB4B,IAAIC,SAAQC,uCACtDJ,MAAOC,SAAUC,UAAWE,aAoBlCC,MAAK,KAAM,0BAAkBR,MAAOE,WACpCM,MAAK,oCACiBR,MAAOE,YAI7BO,MAAMC,6BAMXC,GACAnB,WAAY"} \ No newline at end of file +{"version":3,"file":"actions.min.js","sources":["../src/actions.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 * JS actions.\n *\n * @module mod_bigbluebuttonbn/actions\n * @copyright 2021 Blindside Networks Inc\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {endMeeting as requestEndMeeting} from './repository';\nimport {\n exception as displayException,\n saveCancel,\n} from 'core/notification';\nimport {notifySessionEnded} from './events';\nimport {getString} from 'core/str';\n\nconst confirmedPromise = (title, question, saveLabel) => new Promise(resolve => {\n saveCancel(title, question, saveLabel, resolve);\n});\n\nconst registerEventListeners = () => {\n document.addEventListener('click', e => {\n const actionButton = e.target.closest('.bbb-btn-action[data-action=\"end\"]');\n if (!actionButton) {\n return;\n }\n\n e.preventDefault();\n\n const bbbId = actionButton.dataset.bbbId;\n const groupId = actionButton.dataset.groupId ? actionButton.dataset.groupId : 0;\n\n confirmedPromise(\n getString('end_session_confirm_title', 'mod_bigbluebuttonbn'),\n getString('end_session_confirm', 'mod_bigbluebuttonbn'),\n getString('yes', 'moodle')\n )\n .then(() => requestEndMeeting(bbbId, groupId))\n .then(() => {\n notifySessionEnded(bbbId, groupId);\n\n return;\n })\n .catch(displayException);\n });\n};\n\nlet listening = false;\nif (!listening) {\n registerEventListeners();\n listening = true;\n}\n"],"names":["listening","document","addEventListener","e","actionButton","target","closest","preventDefault","bbbId","dataset","groupId","title","question","saveLabel","Promise","resolve","then","catch","displayException","registerEventListeners"],"mappings":"sJA6DIA,WAAY,EACXA,YA5B0B,MAC3BC,SAASC,iBAAiB,SAASC,UACzBC,aAAeD,EAAEE,OAAOC,QAAQ,0CACjCF,oBAILD,EAAEI,uBAEIC,MAAQJ,aAAaK,QAAQD,MAC7BE,QAAUN,aAAaK,QAAQC,QAAUN,aAAaK,QAAQC,QAAU,EAd7D,IAACC,MAAOC,SAAUC,WAAjBF,OAiBd,kBAAU,4BAA6B,uBAjBlBC,UAkBrB,kBAAU,sBAAuB,uBAlBFC,WAmB/B,kBAAU,MAAO,UAnB4B,IAAIC,SAAQC,uCACtDJ,MAAOC,SAAUC,UAAWE,aAoBlCC,MAAK,KAAM,0BAAkBR,MAAOE,WACpCM,MAAK,oCACiBR,MAAOE,YAI7BO,MAAMC,6BAMXC,GACAnB,WAAY"} \ No newline at end of file diff --git a/mod/bigbluebuttonbn/amd/build/guest_access_modal.min.js b/mod/bigbluebuttonbn/amd/build/guest_access_modal.min.js index efd539a9f13..cde8ac70ad1 100644 --- a/mod/bigbluebuttonbn/amd/build/guest_access_modal.min.js +++ b/mod/bigbluebuttonbn/amd/build/guest_access_modal.min.js @@ -5,6 +5,6 @@ define("mod_bigbluebuttonbn/guest_access_modal",["exports","core/str","core_form * @module mod_bigbluebuttonbn/guest_access_modal * @copyright 2022 Blindside Networks Inc * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=(obj=_modalform)&&obj.__esModule?obj:{default:obj};const selectors_showGuestAccessButton='[data-action="show-guest-access"]';_exports.init=guestInfo=>{const showGuestAccessButton=document.querySelector(selectors_showGuestAccessButton);if(null===showGuestAccessButton)return;const modalForm=new _modalform.default({modalConfig:{title:(0,_str.get_string)("guestaccess_title","mod_bigbluebuttonbn"),large:!0},args:guestInfo,saveButtonText:(0,_str.get_string)("ok","core_moodle"),formClass:"mod_bigbluebuttonbn\\form\\guest_add"});showGuestAccessButton.addEventListener("click",(event=>{modalForm.show().then((()=>((0,_toast.addToastRegion)(modalForm.modal.getRoot()[0]),!0))).catch(_notification.exception),modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(e=>{modalForm.modal.getRoot()[0].querySelectorAll(".toast-wrapper").forEach((reg=>reg.remove())),e.detail.result?e.detail.emailcount>0&&(0,_toast.add)((0,_str.get_string)("guestaccess_invite_success","mod_bigbluebuttonbn",e.detail),{type:"success"}):(0,_toast.add)((0,_str.get_string)("guestaccess_invite_failure","mod_bigbluebuttonbn",e.detail),{type:"warning"})}),{once:!0}),event.stopPropagation()}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=(obj=_modalform)&&obj.__esModule?obj:{default:obj};const selectors_showGuestAccessButton='[data-action="show-guest-access"]';_exports.init=guestInfo=>{const showGuestAccessButton=document.querySelector(selectors_showGuestAccessButton);if(null===showGuestAccessButton)return;const modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("guestaccess_title","mod_bigbluebuttonbn"),large:!0},args:guestInfo,saveButtonText:(0,_str.getString)("ok","core_moodle"),formClass:"mod_bigbluebuttonbn\\form\\guest_add"});showGuestAccessButton.addEventListener("click",(event=>{modalForm.show().then((()=>((0,_toast.addToastRegion)(modalForm.modal.getRoot()[0]),!0))).catch(_notification.exception),modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(e=>{modalForm.modal.getRoot()[0].querySelectorAll(".toast-wrapper").forEach((reg=>reg.remove())),e.detail.result?e.detail.emailcount>0&&(0,_toast.add)((0,_str.getString)("guestaccess_invite_success","mod_bigbluebuttonbn",e.detail),{type:"success"}):(0,_toast.add)((0,_str.getString)("guestaccess_invite_failure","mod_bigbluebuttonbn",e.detail),{type:"warning"})}),{once:!0}),event.stopPropagation()}))}})); //# sourceMappingURL=guest_access_modal.min.js.map \ No newline at end of file diff --git a/mod/bigbluebuttonbn/amd/build/guest_access_modal.min.js.map b/mod/bigbluebuttonbn/amd/build/guest_access_modal.min.js.map index 66a4e945ab1..26acd3c7709 100644 --- a/mod/bigbluebuttonbn/amd/build/guest_access_modal.min.js.map +++ b/mod/bigbluebuttonbn/amd/build/guest_access_modal.min.js.map @@ -1 +1 @@ -{"version":3,"file":"guest_access_modal.min.js","sources":["../src/guest_access_modal.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 * Javascript module for importing presets.\n *\n * @module mod_bigbluebuttonbn/guest_access_modal\n * @copyright 2022 Blindside Networks Inc\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {get_string as getString} from 'core/str';\nimport ModalForm from 'core_form/modalform';\nimport {add as toastAdd, addToastRegion} from 'core/toast';\nimport {\n exception as displayException,\n} from 'core/notification';\nconst selectors = {\n showGuestAccessButton: '[data-action=\"show-guest-access\"]',\n};\n\n/**\n * Intialise the object and click event to show the popup form\n *\n * @param {object} guestInfo\n * @param {string} guestInfo.id\n * @param {string} guestInfo.groupid\n * @param {string} guestInfo.guestjoinurl\n * @param {string} guestInfo.guestpassword\n */\nexport const init = (guestInfo) => {\n const showGuestAccessButton = document.querySelector(selectors.showGuestAccessButton);\n if (showGuestAccessButton === null) {\n return;\n }\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('guestaccess_title', 'mod_bigbluebuttonbn'),\n large: true,\n },\n args: guestInfo,\n saveButtonText: getString('ok', 'core_moodle'),\n formClass: 'mod_bigbluebuttonbn\\\\form\\\\guest_add',\n });\n showGuestAccessButton.addEventListener('click', event => {\n modalForm.show().then(() => {\n addToastRegion(modalForm.modal.getRoot()[0]);\n return true;\n }).catch(displayException);\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, (e) => {\n // Remove toast region as if not it will be displayed on the closed modal.\n const modalElement = modalForm.modal.getRoot()[0];\n const regions = modalElement.querySelectorAll('.toast-wrapper');\n regions.forEach((reg) => reg.remove());\n if (e.detail.result) {\n if (e.detail.emailcount > 0) {\n toastAdd(getString('guestaccess_invite_success', 'mod_bigbluebuttonbn', e.detail),\n {\n type: 'success',\n }\n );\n }\n } else {\n toastAdd(getString('guestaccess_invite_failure', 'mod_bigbluebuttonbn', e.detail),\n {\n type: 'warning',\n }\n );\n }\n }, {once: true});\n event.stopPropagation();\n });\n};\n"],"names":["selectors","guestInfo","showGuestAccessButton","document","querySelector","modalForm","ModalForm","modalConfig","title","large","args","saveButtonText","formClass","addEventListener","event","show","then","modal","getRoot","catch","displayException","events","FORM_SUBMITTED","e","querySelectorAll","forEach","reg","remove","detail","result","emailcount","type","once","stopPropagation"],"mappings":";;;;;;;sJA4BMA,gCACqB,kDAYNC,kBACXC,sBAAwBC,SAASC,cAAcJ,oCACvB,OAA1BE,mCAIEG,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,mBAAU,oBAAqB,uBACtCC,OAAO,GAEXC,KAAMT,UACNU,gBAAgB,mBAAU,KAAM,eAChCC,UAAW,yCAEfV,sBAAsBW,iBAAiB,SAASC,QAC5CT,UAAUU,OAAOC,MAAK,+BACHX,UAAUY,MAAMC,UAAU,KAClC,KACRC,MAAMC,yBACTf,UAAUQ,iBAAiBR,UAAUgB,OAAOC,gBAAiBC,IAEpClB,UAAUY,MAAMC,UAAU,GAClBM,iBAAiB,kBACtCC,SAASC,KAAQA,IAAIC,WACzBJ,EAAEK,OAAOC,OACLN,EAAEK,OAAOE,WAAa,mBACb,mBAAU,6BAA8B,sBAAuBP,EAAEK,QACtE,CACIG,KAAM,4BAKT,mBAAU,6BAA8B,sBAAuBR,EAAEK,QACtE,CACIG,KAAM,cAInB,CAACC,MAAM,IACVlB,MAAMmB"} \ No newline at end of file +{"version":3,"file":"guest_access_modal.min.js","sources":["../src/guest_access_modal.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 * Javascript module for importing presets.\n *\n * @module mod_bigbluebuttonbn/guest_access_modal\n * @copyright 2022 Blindside Networks Inc\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {getString} from 'core/str';\nimport ModalForm from 'core_form/modalform';\nimport {add as toastAdd, addToastRegion} from 'core/toast';\nimport {\n exception as displayException,\n} from 'core/notification';\nconst selectors = {\n showGuestAccessButton: '[data-action=\"show-guest-access\"]',\n};\n\n/**\n * Intialise the object and click event to show the popup form\n *\n * @param {object} guestInfo\n * @param {string} guestInfo.id\n * @param {string} guestInfo.groupid\n * @param {string} guestInfo.guestjoinurl\n * @param {string} guestInfo.guestpassword\n */\nexport const init = (guestInfo) => {\n const showGuestAccessButton = document.querySelector(selectors.showGuestAccessButton);\n if (showGuestAccessButton === null) {\n return;\n }\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('guestaccess_title', 'mod_bigbluebuttonbn'),\n large: true,\n },\n args: guestInfo,\n saveButtonText: getString('ok', 'core_moodle'),\n formClass: 'mod_bigbluebuttonbn\\\\form\\\\guest_add',\n });\n showGuestAccessButton.addEventListener('click', event => {\n modalForm.show().then(() => {\n addToastRegion(modalForm.modal.getRoot()[0]);\n return true;\n }).catch(displayException);\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, (e) => {\n // Remove toast region as if not it will be displayed on the closed modal.\n const modalElement = modalForm.modal.getRoot()[0];\n const regions = modalElement.querySelectorAll('.toast-wrapper');\n regions.forEach((reg) => reg.remove());\n if (e.detail.result) {\n if (e.detail.emailcount > 0) {\n toastAdd(getString('guestaccess_invite_success', 'mod_bigbluebuttonbn', e.detail),\n {\n type: 'success',\n }\n );\n }\n } else {\n toastAdd(getString('guestaccess_invite_failure', 'mod_bigbluebuttonbn', e.detail),\n {\n type: 'warning',\n }\n );\n }\n }, {once: true});\n event.stopPropagation();\n });\n};\n"],"names":["selectors","guestInfo","showGuestAccessButton","document","querySelector","modalForm","ModalForm","modalConfig","title","large","args","saveButtonText","formClass","addEventListener","event","show","then","modal","getRoot","catch","displayException","events","FORM_SUBMITTED","e","querySelectorAll","forEach","reg","remove","detail","result","emailcount","type","once","stopPropagation"],"mappings":";;;;;;;sJA4BMA,gCACqB,kDAYNC,kBACXC,sBAAwBC,SAASC,cAAcJ,oCACvB,OAA1BE,mCAIEG,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,oBAAqB,uBACtCC,OAAO,GAEXC,KAAMT,UACNU,gBAAgB,kBAAU,KAAM,eAChCC,UAAW,yCAEfV,sBAAsBW,iBAAiB,SAASC,QAC5CT,UAAUU,OAAOC,MAAK,+BACHX,UAAUY,MAAMC,UAAU,KAClC,KACRC,MAAMC,yBACTf,UAAUQ,iBAAiBR,UAAUgB,OAAOC,gBAAiBC,IAEpClB,UAAUY,MAAMC,UAAU,GAClBM,iBAAiB,kBACtCC,SAASC,KAAQA,IAAIC,WACzBJ,EAAEK,OAAOC,OACLN,EAAEK,OAAOE,WAAa,mBACb,kBAAU,6BAA8B,sBAAuBP,EAAEK,QACtE,CACIG,KAAM,4BAKT,kBAAU,6BAA8B,sBAAuBR,EAAEK,QACtE,CACIG,KAAM,cAInB,CAACC,MAAM,IACVlB,MAAMmB"} \ No newline at end of file diff --git a/mod/bigbluebuttonbn/amd/build/modform.min.js b/mod/bigbluebuttonbn/amd/build/modform.min.js index d90336f5202..85e58c30221 100644 --- a/mod/bigbluebuttonbn/amd/build/modform.min.js +++ b/mod/bigbluebuttonbn/amd/build/modform.min.js @@ -5,6 +5,6 @@ define("mod_bigbluebuttonbn/modform",["exports","core/str","core/notification"," * @module mod_bigbluebuttonbn/modform * @copyright 2021 Blindside Networks Inc * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_templates=_interopRequireDefault(_templates);const ELEMENT_SELECTOR_instanceTypeSelection=()=>document.querySelector("select#id_type"),ELEMENT_SELECTOR_instanceTypeProfiles=()=>document.querySelector("[data-profile-types]"),ELEMENT_SELECTOR_participantData=()=>document.querySelector("[data-participant-data]"),ELEMENT_SELECTOR_participantList=()=>document.getElementsByName("participants")[0],ELEMENT_SELECTOR_participantTable=()=>document.getElementById("participant_list_table"),ELEMENT_SELECTOR_participantSelectionType=()=>document.getElementsByName("bigbluebuttonbn_participant_selection_type")[0],ELEMENT_SELECTOR_participantSelection=()=>document.getElementsByName("bigbluebuttonbn_participant_selection")[0],ELEMENT_SELECTOR_participantAddButton=()=>document.getElementsByName("bigbluebuttonbn_participant_selection_add")[0];_exports.init=info=>{const selectedType=ELEMENT_SELECTOR_instanceTypeSelection(),instanceTypeProfiles=JSON.parse(ELEMENT_SELECTOR_instanceTypeProfiles().dataset.profileTypes);let profileType=info.instanceTypeDefault;null!==selectedType&&-1!==selectedType.selectedIndex&&(profileType=selectedType.options[selectedType.selectedIndex].value);const isFeatureEnabled=(profileType,feature)=>-1!==instanceTypeProfiles[profileType].features.indexOf(feature);applyInstanceTypeProfile(profileType,isFeatureEnabled),selectedType.addEventListener("change",(e=>{applyInstanceTypeProfile(e.target.value,isFeatureEnabled)})),ELEMENT_SELECTOR_participantSelectionType().addEventListener("change",(e=>{const currentTypeSelect=e.target;updateSelectionFromType(currentTypeSelect)})),ELEMENT_SELECTOR_participantAddButton().addEventListener("click",(e=>{e.stopPropagation(),e.preventDefault(),participantAddFromCurrentSelection()})),participantListInit()};const applyInstanceTypeProfile=(profileType,isFeatureEnabled)=>{let showAll=isFeatureEnabled(profileType,"all");const showFieldset=(id,show)=>{const node=document.querySelector("#"+id);node&&(node.style.display=show?"block":"none")},showInput=(id,show)=>{const node=document.querySelector("#"+id);if(node){var ancestor=node.closest("div").closest("div");ancestor.style.display=show?"block":"none"}},showFormGroup=(id,show)=>{const node=document.querySelector("#fgroup_id_"+id);node&&(show?node.classList.remove("hidden"):node.classList.add("hidden"))};showFieldset("id_room",showAll||isFeatureEnabled(profileType,"showroom")),showInput("id_record",showAll||isFeatureEnabled(profileType,"showroom")),showFieldset("id_recordings",showAll||isFeatureEnabled(profileType,"showrecordings")),showInput("id_recordings_imported",showAll||isFeatureEnabled(profileType,"showrecordings")),showFieldset("id_lock",showAll||isFeatureEnabled(profileType,"lock")),showFieldset("id_guestaccess",showAll||isFeatureEnabled(profileType,"showroom")),showFieldset("id_preuploadpresentation",showAll||isFeatureEnabled(profileType,"preuploadpresentation")),showFieldset("id_permissions",showAll||isFeatureEnabled(profileType,"permissions")),showFieldset("id_schedule",showAll||isFeatureEnabled(profileType,"schedule")),showFieldset("id_modstandardelshdr",showAll||isFeatureEnabled(profileType,"modstandardelshdr")),showFieldset("id_availabilityconditionsheader",showAll||isFeatureEnabled(profileType,"availabilityconditionsheader")),showFieldset("id_tagshdr",showAll||isFeatureEnabled(profileType,"tagshdr")),showFieldset("id_competenciessection",showAll||isFeatureEnabled(profileType,"competenciessection")),showFormGroup("completionattendancegroup",showAll||isFeatureEnabled(profileType,"completionattendance")),showFormGroup("completionengagementgroup",showAll||isFeatureEnabled(profileType,"completionengagement"))},participantListInit=()=>{const participantData=JSON.parse(ELEMENT_SELECTOR_participantData().dataset.participantData);getParticipantList().forEach((participant=>{const selectionTypeValue=participant.selectiontype,selectionValue=participant.selectionid,selectionRole=participant.role;"all"!==participant.selectiontype&&void 0===participantData[participant.selectiontype].children[participant.selectionid]||participantAddToForm(selectionTypeValue,selectionValue,selectionRole,!0).then()}))},participantAddToForm=async(selectionTypeValue,selectionValue,selectedRole,canRemove)=>{const participantData=JSON.parse(ELEMENT_SELECTOR_participantData().dataset.participantData);let roles={viewer:{id:"viewer",label:await(0,_str.get_string)("mod_form_field_participant_bbb_role_viewer","mod_bigbluebuttonbn")},moderator:{id:"moderator",label:await(0,_str.get_string)("mod_form_field_participant_bbb_role_moderator","mod_bigbluebuttonbn")}};roles[selectedRole].isselected=!0;try{const listTable=document.querySelector("#participant_list_table tbody"),templateContext={selectiontypevalue:selectionTypeValue,selectionvalue:selectionValue,participanttype:participantData[selectionTypeValue].name,participantvalue:"all"!==selectionTypeValue?participantData[selectionTypeValue].children[selectionValue].name:null,roles:Object.values(roles),canRemove:canRemove},{html:html,js:js}=await _templates.default.renderForPromise("mod_bigbluebuttonbn/participant_form_add",templateContext),newNode=_templates.default.appendNodeContents(listTable,html,js)[0];newNode.querySelector(".participant-select").addEventListener("change",(()=>{participantListRoleUpdate(selectionTypeValue,selectionValue)}));const removeNode=newNode.querySelector(".remove-button");removeNode&&removeNode.addEventListener("click",(()=>{participantRemove(selectionTypeValue,selectionValue)}))}catch(e){_notification.default.exception(e)}},participantListUpdate=list=>{ELEMENT_SELECTOR_participantList().value=JSON.stringify(list)},getParticipantList=()=>{const participantListValue=ELEMENT_SELECTOR_participantList().value;return participantListValue?JSON.parse(participantListValue):[]},participantRemove=(selectionTypeValue,selectionValue)=>{const pList=getParticipantList(),id="participant_list_tr_"+selectionTypeValue+"-"+selectionValue,participantListTable=ELEMENT_SELECTOR_participantTable(),selectionid=""===selectionValue?null:selectionValue;for(let i=0;i{const participantListRoleSelection=document.querySelector("#participant_list_tr_".concat(type,"-").concat(id," .participant-select")),pList=getParticipantList();for(var i=0;i{let selectionType=ELEMENT_SELECTOR_participantSelectionType(),selection=ELEMENT_SELECTOR_participantSelection();const pList=getParticipantList();for(var i=0;i{const createNewOption=(selectItem,label,value)=>{const option=document.createElement("option");option.text=label,option.value=value,selectItem.add(option)},participantData=JSON.parse(ELEMENT_SELECTOR_participantData().dataset.participantData),participantSelect=ELEMENT_SELECTOR_participantSelection();for(;participantSelect.firstChild;)participantSelect.removeChild(participantSelect.firstChild);if(-1!==currentTypeSelect.selectedIndex){Object.values(participantData[currentTypeSelect.value].children).forEach((option=>{createNewOption(participantSelect,option.name,option.id)})),"all"===currentTypeSelect.value?(createNewOption(participantSelect,"---------------","all"),participantSelect.disabled=!0):participantSelect.disabled=!1}}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_templates=_interopRequireDefault(_templates);const ELEMENT_SELECTOR_instanceTypeSelection=()=>document.querySelector("select#id_type"),ELEMENT_SELECTOR_instanceTypeProfiles=()=>document.querySelector("[data-profile-types]"),ELEMENT_SELECTOR_participantData=()=>document.querySelector("[data-participant-data]"),ELEMENT_SELECTOR_participantList=()=>document.getElementsByName("participants")[0],ELEMENT_SELECTOR_participantTable=()=>document.getElementById("participant_list_table"),ELEMENT_SELECTOR_participantSelectionType=()=>document.getElementsByName("bigbluebuttonbn_participant_selection_type")[0],ELEMENT_SELECTOR_participantSelection=()=>document.getElementsByName("bigbluebuttonbn_participant_selection")[0],ELEMENT_SELECTOR_participantAddButton=()=>document.getElementsByName("bigbluebuttonbn_participant_selection_add")[0];_exports.init=info=>{const selectedType=ELEMENT_SELECTOR_instanceTypeSelection(),instanceTypeProfiles=JSON.parse(ELEMENT_SELECTOR_instanceTypeProfiles().dataset.profileTypes);let profileType=info.instanceTypeDefault;null!==selectedType&&-1!==selectedType.selectedIndex&&(profileType=selectedType.options[selectedType.selectedIndex].value);const isFeatureEnabled=(profileType,feature)=>-1!==instanceTypeProfiles[profileType].features.indexOf(feature);applyInstanceTypeProfile(profileType,isFeatureEnabled),selectedType.addEventListener("change",(e=>{applyInstanceTypeProfile(e.target.value,isFeatureEnabled)})),ELEMENT_SELECTOR_participantSelectionType().addEventListener("change",(e=>{const currentTypeSelect=e.target;updateSelectionFromType(currentTypeSelect)})),ELEMENT_SELECTOR_participantAddButton().addEventListener("click",(e=>{e.stopPropagation(),e.preventDefault(),participantAddFromCurrentSelection()})),participantListInit()};const applyInstanceTypeProfile=(profileType,isFeatureEnabled)=>{let showAll=isFeatureEnabled(profileType,"all");const showFieldset=(id,show)=>{const node=document.querySelector("#"+id);node&&(node.style.display=show?"block":"none")},showInput=(id,show)=>{const node=document.querySelector("#"+id);if(node){var ancestor=node.closest("div").closest("div");ancestor.style.display=show?"block":"none"}},showFormGroup=(id,show)=>{const node=document.querySelector("#fgroup_id_"+id);node&&(show?node.classList.remove("hidden"):node.classList.add("hidden"))};showFieldset("id_room",showAll||isFeatureEnabled(profileType,"showroom")),showInput("id_record",showAll||isFeatureEnabled(profileType,"showroom")),showFieldset("id_recordings",showAll||isFeatureEnabled(profileType,"showrecordings")),showInput("id_recordings_imported",showAll||isFeatureEnabled(profileType,"showrecordings")),showFieldset("id_lock",showAll||isFeatureEnabled(profileType,"lock")),showFieldset("id_guestaccess",showAll||isFeatureEnabled(profileType,"showroom")),showFieldset("id_preuploadpresentation",showAll||isFeatureEnabled(profileType,"preuploadpresentation")),showFieldset("id_permissions",showAll||isFeatureEnabled(profileType,"permissions")),showFieldset("id_schedule",showAll||isFeatureEnabled(profileType,"schedule")),showFieldset("id_modstandardelshdr",showAll||isFeatureEnabled(profileType,"modstandardelshdr")),showFieldset("id_availabilityconditionsheader",showAll||isFeatureEnabled(profileType,"availabilityconditionsheader")),showFieldset("id_tagshdr",showAll||isFeatureEnabled(profileType,"tagshdr")),showFieldset("id_competenciessection",showAll||isFeatureEnabled(profileType,"competenciessection")),showFormGroup("completionattendancegroup",showAll||isFeatureEnabled(profileType,"completionattendance")),showFormGroup("completionengagementgroup",showAll||isFeatureEnabled(profileType,"completionengagement"))},participantListInit=()=>{const participantData=JSON.parse(ELEMENT_SELECTOR_participantData().dataset.participantData);getParticipantList().forEach((participant=>{const selectionTypeValue=participant.selectiontype,selectionValue=participant.selectionid,selectionRole=participant.role;"all"!==participant.selectiontype&&void 0===participantData[participant.selectiontype].children[participant.selectionid]||participantAddToForm(selectionTypeValue,selectionValue,selectionRole,!0).then()}))},participantAddToForm=async(selectionTypeValue,selectionValue,selectedRole,canRemove)=>{const participantData=JSON.parse(ELEMENT_SELECTOR_participantData().dataset.participantData);let roles={viewer:{id:"viewer",label:await(0,_str.getString)("mod_form_field_participant_bbb_role_viewer","mod_bigbluebuttonbn")},moderator:{id:"moderator",label:await(0,_str.getString)("mod_form_field_participant_bbb_role_moderator","mod_bigbluebuttonbn")}};roles[selectedRole].isselected=!0;try{const listTable=document.querySelector("#participant_list_table tbody"),templateContext={selectiontypevalue:selectionTypeValue,selectionvalue:selectionValue,participanttype:participantData[selectionTypeValue].name,participantvalue:"all"!==selectionTypeValue?participantData[selectionTypeValue].children[selectionValue].name:null,roles:Object.values(roles),canRemove:canRemove},{html:html,js:js}=await _templates.default.renderForPromise("mod_bigbluebuttonbn/participant_form_add",templateContext),newNode=_templates.default.appendNodeContents(listTable,html,js)[0];newNode.querySelector(".participant-select").addEventListener("change",(()=>{participantListRoleUpdate(selectionTypeValue,selectionValue)}));const removeNode=newNode.querySelector(".remove-button");removeNode&&removeNode.addEventListener("click",(()=>{participantRemove(selectionTypeValue,selectionValue)}))}catch(e){_notification.default.exception(e)}},participantListUpdate=list=>{ELEMENT_SELECTOR_participantList().value=JSON.stringify(list)},getParticipantList=()=>{const participantListValue=ELEMENT_SELECTOR_participantList().value;return participantListValue?JSON.parse(participantListValue):[]},participantRemove=(selectionTypeValue,selectionValue)=>{const pList=getParticipantList(),id="participant_list_tr_"+selectionTypeValue+"-"+selectionValue,participantListTable=ELEMENT_SELECTOR_participantTable(),selectionid=""===selectionValue?null:selectionValue;for(let i=0;i{const participantListRoleSelection=document.querySelector("#participant_list_tr_".concat(type,"-").concat(id," .participant-select")),pList=getParticipantList();for(var i=0;i{let selectionType=ELEMENT_SELECTOR_participantSelectionType(),selection=ELEMENT_SELECTOR_participantSelection();const pList=getParticipantList();for(var i=0;i{const createNewOption=(selectItem,label,value)=>{const option=document.createElement("option");option.text=label,option.value=value,selectItem.add(option)},participantData=JSON.parse(ELEMENT_SELECTOR_participantData().dataset.participantData),participantSelect=ELEMENT_SELECTOR_participantSelection();for(;participantSelect.firstChild;)participantSelect.removeChild(participantSelect.firstChild);if(-1!==currentTypeSelect.selectedIndex){Object.values(participantData[currentTypeSelect.value].children).forEach((option=>{createNewOption(participantSelect,option.name,option.id)})),"all"===currentTypeSelect.value?(createNewOption(participantSelect,"---------------","all"),participantSelect.disabled=!0):participantSelect.disabled=!1}}})); //# sourceMappingURL=modform.min.js.map \ No newline at end of file diff --git a/mod/bigbluebuttonbn/amd/build/modform.min.js.map b/mod/bigbluebuttonbn/amd/build/modform.min.js.map index a233b57173b..bab500ac2a4 100644 --- a/mod/bigbluebuttonbn/amd/build/modform.min.js.map +++ b/mod/bigbluebuttonbn/amd/build/modform.min.js.map @@ -1 +1 @@ -{"version":3,"file":"modform.min.js","sources":["../src/modform.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 * JS for the mod_form page on mod_bigbluebuttonbn plugin.\n *\n * @module mod_bigbluebuttonbn/modform\n * @copyright 2021 Blindside Networks Inc\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString} from 'core/str';\nimport Notification from 'core/notification';\nimport Templates from \"core/templates\";\n\n/**\n * Get all selectors in one place.\n *\n */\nconst ELEMENT_SELECTOR = {\n instanceTypeSelection: () => document.querySelector('select#id_type'),\n instanceTypeProfiles: () => document.querySelector('[data-profile-types]'),\n participantData: () => document.querySelector('[data-participant-data]'),\n participantList: () => document.getElementsByName('participants')[0],\n participantTable: () => document.getElementById('participant_list_table'),\n participantSelectionType: () => document.getElementsByName('bigbluebuttonbn_participant_selection_type')[0],\n participantSelection: () => document.getElementsByName('bigbluebuttonbn_participant_selection')[0],\n participantAddButton: () => document.getElementsByName('bigbluebuttonbn_participant_selection_add')[0],\n};\n/**\n * Initialise the moodle form code.\n *\n * This will help hide or show items depending on the selection of the instance type.\n *\n * @method init\n * @param {object} info\n */\nexport const init = (info) => {\n const selectedType = ELEMENT_SELECTOR.instanceTypeSelection();\n const instanceTypeProfiles = JSON.parse(ELEMENT_SELECTOR.instanceTypeProfiles().dataset.profileTypes);\n\n let profileType = info.instanceTypeDefault;\n if (selectedType !== null && selectedType.selectedIndex !== -1) {\n profileType = selectedType.options[selectedType.selectedIndex].value;\n }\n\n const isFeatureEnabled = (profileType, feature) => {\n const features = instanceTypeProfiles[profileType].features;\n return (features.indexOf(feature) !== -1);\n };\n applyInstanceTypeProfile(profileType, isFeatureEnabled);\n\n // Change form visible fields depending on the selection.\n selectedType.addEventListener('change', (e) => {\n applyInstanceTypeProfile(e.target.value, isFeatureEnabled);\n });\n\n ELEMENT_SELECTOR.participantSelectionType().addEventListener('change', (e) => {\n const currentTypeSelect = e.target;\n updateSelectionFromType(currentTypeSelect);\n });\n\n ELEMENT_SELECTOR.participantAddButton().addEventListener('click', (e) => {\n e.stopPropagation();\n e.preventDefault();\n participantAddFromCurrentSelection();\n });\n\n participantListInit();\n};\n\n/**\n * Show or hide form element depending on the selected profile\n *\n * @param {string} profileType\n * @param {function} isFeatureEnabled\n */\nconst applyInstanceTypeProfile = (profileType, isFeatureEnabled) => {\n let showAll = isFeatureEnabled(profileType, 'all');\n const showFieldset = (id, show) => {\n // Show room settings validation.\n const node = document.querySelector('#' + id);\n if (!node) {\n return;\n }\n if (show) {\n node.style.display = 'block';\n return;\n }\n node.style.display = 'none';\n };\n const showInput = (id, show) => {\n // Show room settings validation.\n const node = document.querySelector('#' + id);\n if (!node) {\n return;\n }\n var ancestor = node.closest('div').closest('div');\n if (show) {\n ancestor.style.display = 'block';\n return;\n }\n ancestor.style.display = 'none';\n };\n const showFormGroup = (id, show) => {\n // Show room settings validation.\n const node = document.querySelector('#fgroup_id_' + id);\n if (!node) {\n return;\n }\n if (show) {\n node.classList.remove('hidden');\n return;\n }\n node.classList.add('hidden');\n };\n // Show room settings validation.\n showFieldset('id_room', showAll ||\n isFeatureEnabled(profileType, 'showroom'));\n showInput('id_record', showAll ||\n isFeatureEnabled(profileType, 'showroom'));\n // Show recordings settings validation.\n showFieldset('id_recordings', showAll ||\n isFeatureEnabled(profileType, 'showrecordings'));\n // Show recordings imported settings validation.\n showInput('id_recordings_imported', showAll ||\n isFeatureEnabled(profileType, 'showrecordings'));\n // Show lock settings validation.\n showFieldset('id_lock', showAll ||\n isFeatureEnabled(profileType, 'lock'));\n // Show guest settings validation.\n showFieldset('id_guestaccess', showAll ||\n isFeatureEnabled(profileType, 'showroom'));\n // Preuploadpresentation feature validation.\n showFieldset('id_preuploadpresentation', showAll ||\n isFeatureEnabled(profileType, 'preuploadpresentation'));\n // Participants feature validation.\n showFieldset('id_permissions', showAll ||\n isFeatureEnabled(profileType, 'permissions'));\n // Schedule feature validation.\n showFieldset('id_schedule', showAll ||\n isFeatureEnabled(profileType, 'schedule'));\n // Common module settings validation.\n showFieldset('id_modstandardelshdr', showAll ||\n isFeatureEnabled(profileType, 'modstandardelshdr'));\n // Restrict access validation.\n showFieldset('id_availabilityconditionsheader', showAll ||\n isFeatureEnabled(profileType, 'availabilityconditionsheader'));\n // Tags validation.\n showFieldset('id_tagshdr', showAll || isFeatureEnabled(profileType, 'tagshdr'));\n // Competencies validation.\n showFieldset('id_competenciessection', showAll ||\n isFeatureEnabled(profileType, 'competenciessection'));\n // Completion validation.\n showFormGroup('completionattendancegroup', showAll ||\n isFeatureEnabled(profileType, 'completionattendance'));\n // Completion validation.\n showFormGroup('completionengagementgroup', showAll ||\n isFeatureEnabled(profileType, 'completionengagement'));\n};\n\n/**\n * Init the participant list\n */\nconst participantListInit = () => {\n const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);\n const participantList = getParticipantList();\n participantList.forEach(participant => {\n const selectionTypeValue = participant.selectiontype;\n const selectionValue = participant.selectionid;\n const selectionRole = participant.role;\n if (participant.selectiontype === 'all' ||\n typeof participantData[participant.selectiontype].children[participant.selectionid] !== 'undefined') {\n // Add it to the form, but don't add the delete button if it is the first item.\n participantAddToForm(selectionTypeValue, selectionValue, selectionRole, true).then();\n }\n });\n};\n\n/**\n * Add rows to the participant list depending on the current selection.\n *\n * @param {string} selectionTypeValue\n * @param {string} selectionValue\n * @param {string} selectedRole\n * @param {boolean} canRemove\n * @returns {Promise}\n */\nconst participantAddToForm = async(selectionTypeValue, selectionValue, selectedRole, canRemove) => {\n const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);\n const sviewer = await getString('mod_form_field_participant_bbb_role_viewer', 'mod_bigbluebuttonbn');\n const smoderator = await getString('mod_form_field_participant_bbb_role_moderator', 'mod_bigbluebuttonbn');\n let roles = {\n viewer: {'id': 'viewer', label: sviewer},\n moderator: {'id': 'moderator', label: smoderator}\n };\n roles[selectedRole].isselected = true;\n try {\n const listTable = document.querySelector('#participant_list_table tbody');\n const templateContext = {\n 'selectiontypevalue': selectionTypeValue,\n 'selectionvalue': selectionValue,\n 'participanttype': participantData[selectionTypeValue].name,\n 'participantvalue':\n (selectionTypeValue !== 'all') ?\n participantData[selectionTypeValue].children[selectionValue].name : null,\n 'roles': Object.values(roles),\n 'canRemove': canRemove\n };\n const {html, js} = await Templates.renderForPromise('mod_bigbluebuttonbn/participant_form_add', templateContext);\n const newNode = Templates.appendNodeContents(listTable, html, js)[0];\n newNode.querySelector('.participant-select').addEventListener('change', () => {\n participantListRoleUpdate(selectionTypeValue, selectionValue);\n });\n // Now add the callbacks: participantListRoleUpdate() and participantRemove().\n const removeNode = newNode.querySelector('.remove-button');\n if (removeNode) {\n removeNode\n .addEventListener('click', () => {\n participantRemove(selectionTypeValue, selectionValue);\n });\n }\n\n } catch (e) {\n Notification.exception(e);\n }\n};\n/*\n\n */\n\n/**\n * Update the related form element with the list value.\n *\n * @param {object} list\n */\nconst participantListUpdate = (list) => {\n const participantList = ELEMENT_SELECTOR.participantList();\n participantList.value = JSON.stringify(list);\n};\n\n/**\n *\n * @returns {any}\n */\nconst getParticipantList = () => {\n const participantListValue = ELEMENT_SELECTOR.participantList().value;\n if (participantListValue) {\n return JSON.parse(participantListValue);\n }\n return [];\n};\n\n/**\n * Remove participant both in the table/form and in the form element.\n *\n * @param {string} selectionTypeValue\n * @param {string} selectionValue\n */\nconst participantRemove = (selectionTypeValue, selectionValue) => {\n const pList = getParticipantList();\n const id = 'participant_list_tr_' + selectionTypeValue + '-' + selectionValue;\n const participantListTable = ELEMENT_SELECTOR.participantTable();\n const selectionid = (selectionValue === '' ? null : selectionValue);\n for (let i = 0; i < pList.length; i++) {\n if (pList[i].selectiontype === selectionTypeValue &&\n pList[i].selectionid === selectionid) {\n pList.splice(i, 1);\n }\n }\n // Remove from the form.\n for (let i = 0; i < participantListTable.rows.length; i++) {\n if (participantListTable.rows[i].id === id) {\n participantListTable.deleteRow(i);\n }\n }\n // Update value in the form.\n participantListUpdate(pList);\n};\n\n/**\n * Role update\n *\n * @param {string} type\n * @param {string} id\n */\nconst participantListRoleUpdate = (type, id) => {\n // Update in memory.\n const participantListRoleSelection = document.querySelector(`#participant_list_tr_${type}-${id} .participant-select`);\n const pList = getParticipantList();\n\n for (var i = 0; i < pList.length; i++) {\n if (pList[i].selectiontype === type && pList[i].selectionid === id) {\n pList[i].role = participantListRoleSelection.value;\n }\n }\n // Update in the form.\n participantListUpdate(pList);\n};\n\n/**\n * Add participant from the currently selected options\n */\nconst participantAddFromCurrentSelection = () => {\n let selectionType = ELEMENT_SELECTOR.participantSelectionType();\n let selection = ELEMENT_SELECTOR.participantSelection();\n const pList = getParticipantList();\n // Lookup to see if it has been added already.\n for (var i = 0; i < pList.length; i++) {\n if (pList[i].selectiontype === selectionType.value &&\n pList[i].selectionid === selection.value) {\n return;\n }\n }\n pList.push({\n \"selectiontype\": selectionType.value,\n \"selectionid\": selection.value,\n \"role\": \"viewer\"\n });\n // Add it to the form.\n participantAddToForm(selectionType.value, selection.value, 'viewer', true).then();\n // Update in the form.\n participantListUpdate(pList);\n};\n\n/**\n * Update selectable options when changing types\n *\n * @param {HTMLNode} currentTypeSelect\n */\nconst updateSelectionFromType = (currentTypeSelect) => {\n const createNewOption = (selectItem, label, value) => {\n const option = document.createElement('option');\n option.text = label;\n option.value = value;\n\n selectItem.add(option);\n };\n\n const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);\n // Clear all selection items.\n const participantSelect = ELEMENT_SELECTOR.participantSelection();\n while (participantSelect.firstChild) {\n participantSelect.removeChild(participantSelect.firstChild);\n }\n // Add options depending on the selection.\n if (currentTypeSelect.selectedIndex !== -1) {\n const options = Object.values(participantData[currentTypeSelect.value].children);\n options.forEach(option => {\n createNewOption(participantSelect, option.name, option.id);\n });\n\n if (currentTypeSelect.value === 'all') {\n createNewOption(participantSelect, '---------------', 'all');\n participantSelect.disabled = true;\n } else {\n participantSelect.disabled = false;\n }\n }\n};\n"],"names":["ELEMENT_SELECTOR","document","querySelector","getElementsByName","getElementById","info","selectedType","instanceTypeProfiles","JSON","parse","dataset","profileTypes","profileType","instanceTypeDefault","selectedIndex","options","value","isFeatureEnabled","feature","features","indexOf","applyInstanceTypeProfile","addEventListener","e","target","currentTypeSelect","updateSelectionFromType","stopPropagation","preventDefault","participantAddFromCurrentSelection","participantListInit","showAll","showFieldset","id","show","node","style","display","showInput","ancestor","closest","showFormGroup","classList","remove","add","participantData","getParticipantList","forEach","participant","selectionTypeValue","selectiontype","selectionValue","selectionid","selectionRole","role","children","participantAddToForm","then","async","selectedRole","canRemove","roles","viewer","label","moderator","isselected","listTable","templateContext","name","Object","values","html","js","Templates","renderForPromise","newNode","appendNodeContents","participantListRoleUpdate","removeNode","participantRemove","exception","participantListUpdate","list","stringify","participantListValue","pList","participantListTable","i","length","splice","rows","deleteRow","type","participantListRoleSelection","selectionType","selection","push","createNewOption","selectItem","option","createElement","text","participantSelect","firstChild","removeChild","disabled"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"modform.min.js","sources":["../src/modform.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 * JS for the mod_form page on mod_bigbluebuttonbn plugin.\n *\n * @module mod_bigbluebuttonbn/modform\n * @copyright 2021 Blindside Networks Inc\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getString} from 'core/str';\nimport Notification from 'core/notification';\nimport Templates from \"core/templates\";\n\n/**\n * Get all selectors in one place.\n *\n */\nconst ELEMENT_SELECTOR = {\n instanceTypeSelection: () => document.querySelector('select#id_type'),\n instanceTypeProfiles: () => document.querySelector('[data-profile-types]'),\n participantData: () => document.querySelector('[data-participant-data]'),\n participantList: () => document.getElementsByName('participants')[0],\n participantTable: () => document.getElementById('participant_list_table'),\n participantSelectionType: () => document.getElementsByName('bigbluebuttonbn_participant_selection_type')[0],\n participantSelection: () => document.getElementsByName('bigbluebuttonbn_participant_selection')[0],\n participantAddButton: () => document.getElementsByName('bigbluebuttonbn_participant_selection_add')[0],\n};\n/**\n * Initialise the moodle form code.\n *\n * This will help hide or show items depending on the selection of the instance type.\n *\n * @method init\n * @param {object} info\n */\nexport const init = (info) => {\n const selectedType = ELEMENT_SELECTOR.instanceTypeSelection();\n const instanceTypeProfiles = JSON.parse(ELEMENT_SELECTOR.instanceTypeProfiles().dataset.profileTypes);\n\n let profileType = info.instanceTypeDefault;\n if (selectedType !== null && selectedType.selectedIndex !== -1) {\n profileType = selectedType.options[selectedType.selectedIndex].value;\n }\n\n const isFeatureEnabled = (profileType, feature) => {\n const features = instanceTypeProfiles[profileType].features;\n return (features.indexOf(feature) !== -1);\n };\n applyInstanceTypeProfile(profileType, isFeatureEnabled);\n\n // Change form visible fields depending on the selection.\n selectedType.addEventListener('change', (e) => {\n applyInstanceTypeProfile(e.target.value, isFeatureEnabled);\n });\n\n ELEMENT_SELECTOR.participantSelectionType().addEventListener('change', (e) => {\n const currentTypeSelect = e.target;\n updateSelectionFromType(currentTypeSelect);\n });\n\n ELEMENT_SELECTOR.participantAddButton().addEventListener('click', (e) => {\n e.stopPropagation();\n e.preventDefault();\n participantAddFromCurrentSelection();\n });\n\n participantListInit();\n};\n\n/**\n * Show or hide form element depending on the selected profile\n *\n * @param {string} profileType\n * @param {function} isFeatureEnabled\n */\nconst applyInstanceTypeProfile = (profileType, isFeatureEnabled) => {\n let showAll = isFeatureEnabled(profileType, 'all');\n const showFieldset = (id, show) => {\n // Show room settings validation.\n const node = document.querySelector('#' + id);\n if (!node) {\n return;\n }\n if (show) {\n node.style.display = 'block';\n return;\n }\n node.style.display = 'none';\n };\n const showInput = (id, show) => {\n // Show room settings validation.\n const node = document.querySelector('#' + id);\n if (!node) {\n return;\n }\n var ancestor = node.closest('div').closest('div');\n if (show) {\n ancestor.style.display = 'block';\n return;\n }\n ancestor.style.display = 'none';\n };\n const showFormGroup = (id, show) => {\n // Show room settings validation.\n const node = document.querySelector('#fgroup_id_' + id);\n if (!node) {\n return;\n }\n if (show) {\n node.classList.remove('hidden');\n return;\n }\n node.classList.add('hidden');\n };\n // Show room settings validation.\n showFieldset('id_room', showAll ||\n isFeatureEnabled(profileType, 'showroom'));\n showInput('id_record', showAll ||\n isFeatureEnabled(profileType, 'showroom'));\n // Show recordings settings validation.\n showFieldset('id_recordings', showAll ||\n isFeatureEnabled(profileType, 'showrecordings'));\n // Show recordings imported settings validation.\n showInput('id_recordings_imported', showAll ||\n isFeatureEnabled(profileType, 'showrecordings'));\n // Show lock settings validation.\n showFieldset('id_lock', showAll ||\n isFeatureEnabled(profileType, 'lock'));\n // Show guest settings validation.\n showFieldset('id_guestaccess', showAll ||\n isFeatureEnabled(profileType, 'showroom'));\n // Preuploadpresentation feature validation.\n showFieldset('id_preuploadpresentation', showAll ||\n isFeatureEnabled(profileType, 'preuploadpresentation'));\n // Participants feature validation.\n showFieldset('id_permissions', showAll ||\n isFeatureEnabled(profileType, 'permissions'));\n // Schedule feature validation.\n showFieldset('id_schedule', showAll ||\n isFeatureEnabled(profileType, 'schedule'));\n // Common module settings validation.\n showFieldset('id_modstandardelshdr', showAll ||\n isFeatureEnabled(profileType, 'modstandardelshdr'));\n // Restrict access validation.\n showFieldset('id_availabilityconditionsheader', showAll ||\n isFeatureEnabled(profileType, 'availabilityconditionsheader'));\n // Tags validation.\n showFieldset('id_tagshdr', showAll || isFeatureEnabled(profileType, 'tagshdr'));\n // Competencies validation.\n showFieldset('id_competenciessection', showAll ||\n isFeatureEnabled(profileType, 'competenciessection'));\n // Completion validation.\n showFormGroup('completionattendancegroup', showAll ||\n isFeatureEnabled(profileType, 'completionattendance'));\n // Completion validation.\n showFormGroup('completionengagementgroup', showAll ||\n isFeatureEnabled(profileType, 'completionengagement'));\n};\n\n/**\n * Init the participant list\n */\nconst participantListInit = () => {\n const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);\n const participantList = getParticipantList();\n participantList.forEach(participant => {\n const selectionTypeValue = participant.selectiontype;\n const selectionValue = participant.selectionid;\n const selectionRole = participant.role;\n if (participant.selectiontype === 'all' ||\n typeof participantData[participant.selectiontype].children[participant.selectionid] !== 'undefined') {\n // Add it to the form, but don't add the delete button if it is the first item.\n participantAddToForm(selectionTypeValue, selectionValue, selectionRole, true).then();\n }\n });\n};\n\n/**\n * Add rows to the participant list depending on the current selection.\n *\n * @param {string} selectionTypeValue\n * @param {string} selectionValue\n * @param {string} selectedRole\n * @param {boolean} canRemove\n * @returns {Promise}\n */\nconst participantAddToForm = async(selectionTypeValue, selectionValue, selectedRole, canRemove) => {\n const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);\n const sviewer = await getString('mod_form_field_participant_bbb_role_viewer', 'mod_bigbluebuttonbn');\n const smoderator = await getString('mod_form_field_participant_bbb_role_moderator', 'mod_bigbluebuttonbn');\n let roles = {\n viewer: {'id': 'viewer', label: sviewer},\n moderator: {'id': 'moderator', label: smoderator}\n };\n roles[selectedRole].isselected = true;\n try {\n const listTable = document.querySelector('#participant_list_table tbody');\n const templateContext = {\n 'selectiontypevalue': selectionTypeValue,\n 'selectionvalue': selectionValue,\n 'participanttype': participantData[selectionTypeValue].name,\n 'participantvalue':\n (selectionTypeValue !== 'all') ?\n participantData[selectionTypeValue].children[selectionValue].name : null,\n 'roles': Object.values(roles),\n 'canRemove': canRemove\n };\n const {html, js} = await Templates.renderForPromise('mod_bigbluebuttonbn/participant_form_add', templateContext);\n const newNode = Templates.appendNodeContents(listTable, html, js)[0];\n newNode.querySelector('.participant-select').addEventListener('change', () => {\n participantListRoleUpdate(selectionTypeValue, selectionValue);\n });\n // Now add the callbacks: participantListRoleUpdate() and participantRemove().\n const removeNode = newNode.querySelector('.remove-button');\n if (removeNode) {\n removeNode\n .addEventListener('click', () => {\n participantRemove(selectionTypeValue, selectionValue);\n });\n }\n\n } catch (e) {\n Notification.exception(e);\n }\n};\n/*\n\n */\n\n/**\n * Update the related form element with the list value.\n *\n * @param {object} list\n */\nconst participantListUpdate = (list) => {\n const participantList = ELEMENT_SELECTOR.participantList();\n participantList.value = JSON.stringify(list);\n};\n\n/**\n *\n * @returns {any}\n */\nconst getParticipantList = () => {\n const participantListValue = ELEMENT_SELECTOR.participantList().value;\n if (participantListValue) {\n return JSON.parse(participantListValue);\n }\n return [];\n};\n\n/**\n * Remove participant both in the table/form and in the form element.\n *\n * @param {string} selectionTypeValue\n * @param {string} selectionValue\n */\nconst participantRemove = (selectionTypeValue, selectionValue) => {\n const pList = getParticipantList();\n const id = 'participant_list_tr_' + selectionTypeValue + '-' + selectionValue;\n const participantListTable = ELEMENT_SELECTOR.participantTable();\n const selectionid = (selectionValue === '' ? null : selectionValue);\n for (let i = 0; i < pList.length; i++) {\n if (pList[i].selectiontype === selectionTypeValue &&\n pList[i].selectionid === selectionid) {\n pList.splice(i, 1);\n }\n }\n // Remove from the form.\n for (let i = 0; i < participantListTable.rows.length; i++) {\n if (participantListTable.rows[i].id === id) {\n participantListTable.deleteRow(i);\n }\n }\n // Update value in the form.\n participantListUpdate(pList);\n};\n\n/**\n * Role update\n *\n * @param {string} type\n * @param {string} id\n */\nconst participantListRoleUpdate = (type, id) => {\n // Update in memory.\n const participantListRoleSelection = document.querySelector(`#participant_list_tr_${type}-${id} .participant-select`);\n const pList = getParticipantList();\n\n for (var i = 0; i < pList.length; i++) {\n if (pList[i].selectiontype === type && pList[i].selectionid === id) {\n pList[i].role = participantListRoleSelection.value;\n }\n }\n // Update in the form.\n participantListUpdate(pList);\n};\n\n/**\n * Add participant from the currently selected options\n */\nconst participantAddFromCurrentSelection = () => {\n let selectionType = ELEMENT_SELECTOR.participantSelectionType();\n let selection = ELEMENT_SELECTOR.participantSelection();\n const pList = getParticipantList();\n // Lookup to see if it has been added already.\n for (var i = 0; i < pList.length; i++) {\n if (pList[i].selectiontype === selectionType.value &&\n pList[i].selectionid === selection.value) {\n return;\n }\n }\n pList.push({\n \"selectiontype\": selectionType.value,\n \"selectionid\": selection.value,\n \"role\": \"viewer\"\n });\n // Add it to the form.\n participantAddToForm(selectionType.value, selection.value, 'viewer', true).then();\n // Update in the form.\n participantListUpdate(pList);\n};\n\n/**\n * Update selectable options when changing types\n *\n * @param {HTMLNode} currentTypeSelect\n */\nconst updateSelectionFromType = (currentTypeSelect) => {\n const createNewOption = (selectItem, label, value) => {\n const option = document.createElement('option');\n option.text = label;\n option.value = value;\n\n selectItem.add(option);\n };\n\n const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);\n // Clear all selection items.\n const participantSelect = ELEMENT_SELECTOR.participantSelection();\n while (participantSelect.firstChild) {\n participantSelect.removeChild(participantSelect.firstChild);\n }\n // Add options depending on the selection.\n if (currentTypeSelect.selectedIndex !== -1) {\n const options = Object.values(participantData[currentTypeSelect.value].children);\n options.forEach(option => {\n createNewOption(participantSelect, option.name, option.id);\n });\n\n if (currentTypeSelect.value === 'all') {\n createNewOption(participantSelect, '---------------', 'all');\n participantSelect.disabled = true;\n } else {\n participantSelect.disabled = false;\n }\n }\n};\n"],"names":["ELEMENT_SELECTOR","document","querySelector","getElementsByName","getElementById","info","selectedType","instanceTypeProfiles","JSON","parse","dataset","profileTypes","profileType","instanceTypeDefault","selectedIndex","options","value","isFeatureEnabled","feature","features","indexOf","applyInstanceTypeProfile","addEventListener","e","target","currentTypeSelect","updateSelectionFromType","stopPropagation","preventDefault","participantAddFromCurrentSelection","participantListInit","showAll","showFieldset","id","show","node","style","display","showInput","ancestor","closest","showFormGroup","classList","remove","add","participantData","getParticipantList","forEach","participant","selectionTypeValue","selectiontype","selectionValue","selectionid","selectionRole","role","children","participantAddToForm","then","async","selectedRole","canRemove","roles","viewer","label","moderator","isselected","listTable","templateContext","name","Object","values","html","js","Templates","renderForPromise","newNode","appendNodeContents","participantListRoleUpdate","removeNode","participantRemove","exception","participantListUpdate","list","stringify","participantListValue","pList","participantListTable","i","length","splice","rows","deleteRow","type","participantListRoleSelection","selectionType","selection","push","createNewOption","selectItem","option","createElement","text","participantSelect","firstChild","removeChild","disabled"],"mappings":"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"} \ No newline at end of file diff --git a/mod/bigbluebuttonbn/amd/build/recordings.min.js b/mod/bigbluebuttonbn/amd/build/recordings.min.js index ca072d3cb3a..c1a76b03c5a 100644 --- a/mod/bigbluebuttonbn/amd/build/recordings.min.js +++ b/mod/bigbluebuttonbn/amd/build/recordings.min.js @@ -5,6 +5,6 @@ define("mod_bigbluebuttonbn/recordings",["exports","./repository","core/notifica * @module mod_bigbluebuttonbn/recordings * @copyright 2021 Blindside Networks Inc * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */(repository),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_pending=_interopRequireDefault(_pending);const stringsWithKeys={first:"view_recording_yui_first",prev:"view_recording_yui_prev",next:"view_recording_yui_next",last:"view_recording_yui_last",goToLabel:"view_recording_yui_page",goToAction:"view_recording_yui_go",perPage:"view_recording_yui_rows",showAll:"view_recording_yui_show_all"};(0,_prefetch.prefetchStrings)("bigbluebuttonbn",Object.entries(stringsWithKeys).map((entry=>entry[1])));const getStringsForYui=()=>{const stringMap=Object.keys(stringsWithKeys).map((key=>({key:stringsWithKeys[key],component:"mod_bigbluebuttonbn"})));return(0,_str.get_strings)(stringMap).then((stringArray=>Object.assign({},...Object.keys(stringsWithKeys).map(((key,index)=>({[key]:stringArray[index]})))))).catch()},getFormattedData=response=>{const recordingData=response.tabledata;return JSON.parse(recordingData.data)},getTableNode=tableSelector=>document.querySelector(tableSelector),fetchRecordingData=tableSelector=>{const tableNode=getTableNode(tableSelector);return null===tableNode?Promise.resolve(!1):tableNode.dataset.importMode?repository.fetchRecordingsToImport(tableNode.dataset.bbbid,tableNode.dataset.bbbSourceInstanceId,tableNode.dataset.bbbSourceCourseId,tableNode.dataset.tools,tableNode.dataset.groupId):repository.fetchRecordings(tableNode.dataset.bbbid,tableNode.dataset.tools,tableNode.dataset.groupId)},getDataTableFunctions=(tableId,searchFormId,dataTable)=>{const bbbid=getTableNode(tableId).dataset.bbbid,updateTableFromResponse=response=>{if(!response||!response.status)return;dataTable.get("data").reset(getFormattedData(response)),dataTable.set("currentData",dataTable.get("data"));const currentFilter=dataTable.get("currentFilter");currentFilter&&filterByText(currentFilter)},refreshTableData=()=>fetchRecordingData(tableId).then(updateTableFromResponse),filterByText=value=>{const dataModel=dataTable.get("currentData");dataTable.set("currentFilter",value);const escapedRegex=value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),rsearch=new RegExp(".*?".concat(escapedRegex,".*?"),"i");dataTable.set("data",dataModel.filter({asList:!0},(item=>{const name=item.get("recording");if(name&&rsearch.test(name))return!0;const description=item.get("description");return description&&rsearch.test(description)})))},recordingConfirmationMessage=async data=>{var _document$querySelect,_document$querySelect2;const playbackElement=document.querySelector("#playbacks-".concat(data.recordingid)),recordingType=await(0,_str.get_string)("true"===playbackElement.dataset.imported?"view_recording_link":"view_recording","bigbluebuttonbn"),confirmation=await(0,_str.get_string)("view_recording_".concat(data.action,"_confirmation"),"bigbluebuttonbn",recordingType);if("import"===data.action)return confirmation;const associatedLinkCount=null===(_document$querySelect=document.querySelector("a#recording-".concat(data.action,"-").concat(data.recordingid)))||void 0===_document$querySelect||null===(_document$querySelect2=_document$querySelect.dataset)||void 0===_document$querySelect2?void 0:_document$querySelect2.links;if(!associatedLinkCount||0===associatedLinkCount)return confirmation;return await(0,_str.get_string)("view_recording_".concat(data.action,1===associatedLinkCount?"_confirmation_warning_p":"_confirmation_warning_s"),"bigbluebuttonbn",associatedLinkCount)+"\n\n"+confirmation},processAction=e=>{const popoutLink=e.target.closest('[data-action="play"]');if(popoutLink){e.preventDefault();const videoPlayer=window.open("","_blank");return videoPlayer.opener=null,void(videoPlayer.location.href=popoutLink.href)}const clickedLink=e.target.closest("a[data-action]");if(clickedLink&&!clickedLink.classList.contains("disabled")){e.preventDefault();const iconPromise=(0,_loadingicon.addIconToContainerWithPromise)(dataTable.get("boundingBox").getDOMNode());(element=>{const getDataFromAction=(element,dataType)=>{const dataElement=element.closest("[data-".concat(dataType,"]"));return dataElement?dataElement.dataset[dataType]:null},elementData=element.dataset,payload={bigbluebuttonbnid:bbbid,recordingid:getDataFromAction(element,"recordingid"),additionaloptions:getDataFromAction(element,"additionaloptions"),action:elementData.action};if(payload.additionaloptions||(payload.additionaloptions={}),"import"===elementData.action){const bbbsourceid=getDataFromAction(element,"source-instance-id"),bbbcourseid=getDataFromAction(element,"source-course-id");payload.additionaloptions||(payload.additionaloptions={}),payload.additionaloptions.sourceid=bbbsourceid||0,payload.additionaloptions.bbbcourseid=bbbcourseid||0}return payload.additionaloptions=JSON.stringify(payload.additionaloptions),"1"===element.dataset.requireConfirmation?new Promise((resolve=>_modal_factory.default.create({title:(0,_str.get_string)("confirm"),body:recordingConfirmationMessage(payload),type:_modal_factory.default.types.SAVE_CANCEL}).then((async modal=>(modal.setSaveButtonText(await(0,_str.get_string)("ok","moodle")),modal.getRoot().on(_modal_events.default.save,(()=>{resolve(!0)})),modal.getRoot().on(_modal_events.default.hidden,(()=>{modal.destroy(),resolve(!1)})),modal.show(),modal))).catch(_notification.exception))).then((proceed=>proceed?repository.updateRecording(payload):()=>null)):repository.updateRecording(payload)})(clickedLink).then(refreshTableData).then(iconPromise.resolve).catch(_notification.exception)}},processSearchSubmission=e=>{e.preventDefault();const searchInput=e.target.closest("div[role=search]").querySelector("input[name=search]");filterByText(searchInput.value)};return{filterByText:filterByText,refreshTableData:refreshTableData,registerEventListeners:()=>{dataTable.get("boundingBox").getDOMNode().addEventListener("click",processAction);if(document.querySelector(searchFormId)){document.querySelector(searchFormId+" button").addEventListener("click",processSearchSubmission)}}}},setupDatatable=(tableId,searchFormId,response)=>{if(!response)return Promise.resolve();if(!response.status)return Promise.resolve();const recordingData=response.tabledata,pendingPromise=new _pending.default("mod_bigbluebuttonbn/recordings/setupDatatable");return Promise.all([(lang=recordingData.locale,new Promise((resolve=>{YUI({lang:lang}).use("intl","datatable","datatable-sort","datatable-paginator","datatype-number",(Y=>{resolve(Y)}))}))),getStringsForYui()]).then((_ref=>{let[yuiInstance,strings]=_ref;return yuiInstance.Intl.add("datatable-paginator",yuiInstance.config.lang,{...strings}),yuiInstance.DataTable.BodyView.Formatters.customDate=()=>cell=>{return locale=recordingData.locale,date=cell.value,new Date(date).toLocaleDateString(locale,{weekday:"long",year:"numeric",month:"long",day:"numeric"});var locale,date},yuiInstance})).then((yuiInstance=>{const tableData=getFormattedData(response);return yuiInstance.RecordsPaginatorView=Y.Base.create("my-paginator-view",yuiInstance.DataTable.Paginator.View,[],{_modelChange:function(e){var changed=e.changed;changed&&changed.totalItems&&this._updateControlsUI(e.target.get("page"))}}),new yuiInstance.DataTable({paginatorView:"RecordsPaginatorView",width:"1195px",columns:recordingData.columns,data:tableData,rowsPerPage:10,paginatorLocation:["header","footer"],autoSync:!0})})).then((dataTable=>{dataTable.render(tableId);const{registerEventListeners:registerEventListeners}=getDataTableFunctions(tableId,searchFormId,dataTable);return registerEventListeners(),dataTable})).then((dataTable=>(pendingPromise.resolve(),dataTable)));var lang};_exports.init=(tableId,searchFormId)=>{const pendingPromise=new _pending.default("mod_bigbluebuttonbn/recordings:init");fetchRecordingData(tableId).then((response=>setupDatatable(tableId,searchFormId,response))).then((()=>pendingPromise.resolve())).catch(_notification.exception)}})); + */(repository),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_pending=_interopRequireDefault(_pending);const stringsWithKeys={first:"view_recording_yui_first",prev:"view_recording_yui_prev",next:"view_recording_yui_next",last:"view_recording_yui_last",goToLabel:"view_recording_yui_page",goToAction:"view_recording_yui_go",perPage:"view_recording_yui_rows",showAll:"view_recording_yui_show_all"};(0,_prefetch.prefetchStrings)("bigbluebuttonbn",Object.entries(stringsWithKeys).map((entry=>entry[1])));const getStringsForYui=()=>{const stringMap=Object.keys(stringsWithKeys).map((key=>({key:stringsWithKeys[key],component:"mod_bigbluebuttonbn"})));return(0,_str.getStrings)(stringMap).then((stringArray=>Object.assign({},...Object.keys(stringsWithKeys).map(((key,index)=>({[key]:stringArray[index]})))))).catch()},getFormattedData=response=>{const recordingData=response.tabledata;return JSON.parse(recordingData.data)},getTableNode=tableSelector=>document.querySelector(tableSelector),fetchRecordingData=tableSelector=>{const tableNode=getTableNode(tableSelector);return null===tableNode?Promise.resolve(!1):tableNode.dataset.importMode?repository.fetchRecordingsToImport(tableNode.dataset.bbbid,tableNode.dataset.bbbSourceInstanceId,tableNode.dataset.bbbSourceCourseId,tableNode.dataset.tools,tableNode.dataset.groupId):repository.fetchRecordings(tableNode.dataset.bbbid,tableNode.dataset.tools,tableNode.dataset.groupId)},getDataTableFunctions=(tableId,searchFormId,dataTable)=>{const bbbid=getTableNode(tableId).dataset.bbbid,updateTableFromResponse=response=>{if(!response||!response.status)return;dataTable.get("data").reset(getFormattedData(response)),dataTable.set("currentData",dataTable.get("data"));const currentFilter=dataTable.get("currentFilter");currentFilter&&filterByText(currentFilter)},refreshTableData=()=>fetchRecordingData(tableId).then(updateTableFromResponse),filterByText=value=>{const dataModel=dataTable.get("currentData");dataTable.set("currentFilter",value);const escapedRegex=value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),rsearch=new RegExp(".*?".concat(escapedRegex,".*?"),"i");dataTable.set("data",dataModel.filter({asList:!0},(item=>{const name=item.get("recording");if(name&&rsearch.test(name))return!0;const description=item.get("description");return description&&rsearch.test(description)})))},recordingConfirmationMessage=async data=>{var _document$querySelect,_document$querySelect2;const playbackElement=document.querySelector("#playbacks-".concat(data.recordingid)),recordingType=await(0,_str.getString)("true"===playbackElement.dataset.imported?"view_recording_link":"view_recording","bigbluebuttonbn"),confirmation=await(0,_str.getString)("view_recording_".concat(data.action,"_confirmation"),"bigbluebuttonbn",recordingType);if("import"===data.action)return confirmation;const associatedLinkCount=null===(_document$querySelect=document.querySelector("a#recording-".concat(data.action,"-").concat(data.recordingid)))||void 0===_document$querySelect||null===(_document$querySelect2=_document$querySelect.dataset)||void 0===_document$querySelect2?void 0:_document$querySelect2.links;if(!associatedLinkCount||0===associatedLinkCount)return confirmation;return await(0,_str.getString)("view_recording_".concat(data.action,1===associatedLinkCount?"_confirmation_warning_p":"_confirmation_warning_s"),"bigbluebuttonbn",associatedLinkCount)+"\n\n"+confirmation},processAction=e=>{const popoutLink=e.target.closest('[data-action="play"]');if(popoutLink){e.preventDefault();const videoPlayer=window.open("","_blank");return videoPlayer.opener=null,void(videoPlayer.location.href=popoutLink.href)}const clickedLink=e.target.closest("a[data-action]");if(clickedLink&&!clickedLink.classList.contains("disabled")){e.preventDefault();const iconPromise=(0,_loadingicon.addIconToContainerWithPromise)(dataTable.get("boundingBox").getDOMNode());(element=>{const getDataFromAction=(element,dataType)=>{const dataElement=element.closest("[data-".concat(dataType,"]"));return dataElement?dataElement.dataset[dataType]:null},elementData=element.dataset,payload={bigbluebuttonbnid:bbbid,recordingid:getDataFromAction(element,"recordingid"),additionaloptions:getDataFromAction(element,"additionaloptions"),action:elementData.action};if(payload.additionaloptions||(payload.additionaloptions={}),"import"===elementData.action){const bbbsourceid=getDataFromAction(element,"source-instance-id"),bbbcourseid=getDataFromAction(element,"source-course-id");payload.additionaloptions||(payload.additionaloptions={}),payload.additionaloptions.sourceid=bbbsourceid||0,payload.additionaloptions.bbbcourseid=bbbcourseid||0}return payload.additionaloptions=JSON.stringify(payload.additionaloptions),"1"===element.dataset.requireConfirmation?new Promise((resolve=>_modal_factory.default.create({title:(0,_str.getString)("confirm"),body:recordingConfirmationMessage(payload),type:_modal_factory.default.types.SAVE_CANCEL}).then((async modal=>(modal.setSaveButtonText(await(0,_str.getString)("ok","moodle")),modal.getRoot().on(_modal_events.default.save,(()=>{resolve(!0)})),modal.getRoot().on(_modal_events.default.hidden,(()=>{modal.destroy(),resolve(!1)})),modal.show(),modal))).catch(_notification.exception))).then((proceed=>proceed?repository.updateRecording(payload):()=>null)):repository.updateRecording(payload)})(clickedLink).then(refreshTableData).then(iconPromise.resolve).catch(_notification.exception)}},processSearchSubmission=e=>{e.preventDefault();const searchInput=e.target.closest("div[role=search]").querySelector("input[name=search]");filterByText(searchInput.value)};return{filterByText:filterByText,refreshTableData:refreshTableData,registerEventListeners:()=>{dataTable.get("boundingBox").getDOMNode().addEventListener("click",processAction);if(document.querySelector(searchFormId)){document.querySelector(searchFormId+" button").addEventListener("click",processSearchSubmission)}}}},setupDatatable=(tableId,searchFormId,response)=>{if(!response)return Promise.resolve();if(!response.status)return Promise.resolve();const recordingData=response.tabledata,pendingPromise=new _pending.default("mod_bigbluebuttonbn/recordings/setupDatatable");return Promise.all([(lang=recordingData.locale,new Promise((resolve=>{YUI({lang:lang}).use("intl","datatable","datatable-sort","datatable-paginator","datatype-number",(Y=>{resolve(Y)}))}))),getStringsForYui()]).then((_ref=>{let[yuiInstance,strings]=_ref;return yuiInstance.Intl.add("datatable-paginator",yuiInstance.config.lang,{...strings}),yuiInstance.DataTable.BodyView.Formatters.customDate=()=>cell=>{return locale=recordingData.locale,date=cell.value,new Date(date).toLocaleDateString(locale,{weekday:"long",year:"numeric",month:"long",day:"numeric"});var locale,date},yuiInstance})).then((yuiInstance=>{const tableData=getFormattedData(response);return yuiInstance.RecordsPaginatorView=Y.Base.create("my-paginator-view",yuiInstance.DataTable.Paginator.View,[],{_modelChange:function(e){var changed=e.changed;changed&&changed.totalItems&&this._updateControlsUI(e.target.get("page"))}}),new yuiInstance.DataTable({paginatorView:"RecordsPaginatorView",width:"1195px",columns:recordingData.columns,data:tableData,rowsPerPage:10,paginatorLocation:["header","footer"],autoSync:!0})})).then((dataTable=>{dataTable.render(tableId);const{registerEventListeners:registerEventListeners}=getDataTableFunctions(tableId,searchFormId,dataTable);return registerEventListeners(),dataTable})).then((dataTable=>(pendingPromise.resolve(),dataTable)));var lang};_exports.init=(tableId,searchFormId)=>{const pendingPromise=new _pending.default("mod_bigbluebuttonbn/recordings:init");fetchRecordingData(tableId).then((response=>setupDatatable(tableId,searchFormId,response))).then((()=>pendingPromise.resolve())).catch(_notification.exception)}})); //# sourceMappingURL=recordings.min.js.map \ No newline at end of file diff --git a/mod/bigbluebuttonbn/amd/build/recordings.min.js.map b/mod/bigbluebuttonbn/amd/build/recordings.min.js.map index 977112e6d7c..a44f368fef3 100644 --- a/mod/bigbluebuttonbn/amd/build/recordings.min.js.map +++ b/mod/bigbluebuttonbn/amd/build/recordings.min.js.map @@ -1 +1 @@ -{"version":3,"file":"recordings.min.js","sources":["../src/recordings.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 * JS for the recordings page on mod_bigbluebuttonbn plugin.\n *\n * @module mod_bigbluebuttonbn/recordings\n * @copyright 2021 Blindside Networks Inc\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as repository from './repository';\nimport {exception as displayException} from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString, get_strings as getStrings} from 'core/str';\nimport {addIconToContainerWithPromise} from 'core/loadingicon';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Pending from 'core/pending';\n\nconst stringsWithKeys = {\n first: 'view_recording_yui_first',\n prev: 'view_recording_yui_prev',\n next: 'view_recording_yui_next',\n last: 'view_recording_yui_last',\n goToLabel: 'view_recording_yui_page',\n goToAction: 'view_recording_yui_go',\n perPage: 'view_recording_yui_rows',\n showAll: 'view_recording_yui_show_all',\n};\n// Load global strings.\nprefetchStrings('bigbluebuttonbn', Object.entries(stringsWithKeys).map((entry) => entry[1]));\n\nconst getStringsForYui = () => {\n const stringMap = Object.keys(stringsWithKeys).map(key => {\n return {\n key: stringsWithKeys[key],\n component: 'mod_bigbluebuttonbn',\n };\n });\n\n // Return an object with the matching string keys (we want an object with {: ...}).\n return getStrings(stringMap)\n .then((stringArray) => Object.assign({}, ...Object.keys(stringsWithKeys).map(\n (key, index) => ({[key]: stringArray[index]})))\n ).catch();\n};\n\nconst getYuiInstance = lang => new Promise(resolve => {\n // eslint-disable-next-line\n YUI({\n lang,\n }).use('intl', 'datatable', 'datatable-sort', 'datatable-paginator', 'datatype-number', Y => {\n resolve(Y);\n });\n});\n\n/**\n * Format the supplied date per the specified locale.\n *\n * @param {string} locale\n * @param {number} date\n * @returns {array}\n */\nconst formatDate = (locale, date) => {\n const realDate = new Date(date);\n return realDate.toLocaleDateString(locale, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n });\n};\n\n/**\n * Format response data for the table.\n *\n * @param {string} response JSON-encoded table data\n * @returns {array}\n */\nconst getFormattedData = response => {\n const recordingData = response.tabledata;\n return JSON.parse(recordingData.data);\n};\n\nconst getTableNode = tableSelector => document.querySelector(tableSelector);\n\nconst fetchRecordingData = tableSelector => {\n const tableNode = getTableNode(tableSelector);\n if (tableNode === null) {\n return Promise.resolve(false);\n }\n\n if (tableNode.dataset.importMode) {\n return repository.fetchRecordingsToImport(\n tableNode.dataset.bbbid,\n tableNode.dataset.bbbSourceInstanceId,\n tableNode.dataset.bbbSourceCourseId,\n tableNode.dataset.tools,\n tableNode.dataset.groupId\n );\n } else {\n return repository.fetchRecordings(\n tableNode.dataset.bbbid,\n tableNode.dataset.tools,\n tableNode.dataset.groupId\n );\n }\n};\n\n/**\n * Fetch the data table functinos for the specified table.\n *\n * @param {String} tableId in which we will display the table\n * @param {String} searchFormId The Id of the relate.\n * @param {Object} dataTable\n * @returns {Object}\n * @private\n */\nconst getDataTableFunctions = (tableId, searchFormId, dataTable) => {\n const tableNode = getTableNode(tableId);\n const bbbid = tableNode.dataset.bbbid;\n\n const updateTableFromResponse = response => {\n if (!response || !response.status) {\n // There was no output at all.\n return;\n }\n\n dataTable.get('data').reset(getFormattedData(response));\n dataTable.set(\n 'currentData',\n dataTable.get('data')\n );\n\n const currentFilter = dataTable.get('currentFilter');\n if (currentFilter) {\n filterByText(currentFilter);\n }\n };\n\n const refreshTableData = () => fetchRecordingData(tableId).then(updateTableFromResponse);\n\n const filterByText = value => {\n const dataModel = dataTable.get('currentData');\n dataTable.set('currentFilter', value);\n\n const escapedRegex = value.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, \"\\\\$&\");\n const rsearch = new RegExp(`.*?${escapedRegex}.*?`, 'i');\n\n dataTable.set('data', dataModel.filter({asList: true}, item => {\n const name = item.get('recording');\n if (name && rsearch.test(name)) {\n return true;\n }\n\n const description = item.get('description');\n return description && rsearch.test(description);\n }));\n };\n\n const requestAction = (element) => {\n const getDataFromAction = (element, dataType) => {\n const dataElement = element.closest(`[data-${dataType}]`);\n if (dataElement) {\n return dataElement.dataset[dataType];\n }\n\n return null;\n };\n\n const elementData = element.dataset;\n const payload = {\n bigbluebuttonbnid: bbbid,\n recordingid: getDataFromAction(element, 'recordingid'),\n additionaloptions: getDataFromAction(element, 'additionaloptions'),\n action: elementData.action,\n };\n // Slight change for import, for additional options.\n if (!payload.additionaloptions) {\n payload.additionaloptions = {};\n }\n if (elementData.action === 'import') {\n const bbbsourceid = getDataFromAction(element, 'source-instance-id');\n const bbbcourseid = getDataFromAction(element, 'source-course-id');\n if (!payload.additionaloptions) {\n payload.additionaloptions = {};\n }\n payload.additionaloptions.sourceid = bbbsourceid ? bbbsourceid : 0;\n payload.additionaloptions.bbbcourseid = bbbcourseid ? bbbcourseid : 0;\n }\n // Now additional options should be a json string.\n payload.additionaloptions = JSON.stringify(payload.additionaloptions);\n if (element.dataset.requireConfirmation === \"1\") {\n // Create the confirmation dialogue.\n return new Promise((resolve) =>\n ModalFactory.create({\n title: getString('confirm'),\n body: recordingConfirmationMessage(payload),\n type: ModalFactory.types.SAVE_CANCEL\n }).then(async(modal) => {\n modal.setSaveButtonText(await getString('ok', 'moodle'));\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, () => {\n resolve(true);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, () => {\n // Destroy when hidden.\n modal.destroy();\n resolve(false);\n });\n\n modal.show();\n\n return modal;\n }).catch(displayException)\n ).then((proceed) =>\n proceed ? repository.updateRecording(payload) : () => null\n );\n } else {\n return repository.updateRecording(payload);\n }\n };\n\n const recordingConfirmationMessage = async(data) => {\n\n const playbackElement = document.querySelector(`#playbacks-${data.recordingid}`);\n const recordingType = await getString(\n playbackElement.dataset.imported === 'true' ? 'view_recording_link' : 'view_recording',\n 'bigbluebuttonbn'\n );\n\n const confirmation = await getString(`view_recording_${data.action}_confirmation`, 'bigbluebuttonbn', recordingType);\n\n if (data.action === 'import') {\n return confirmation;\n }\n\n // If it has associated links imported in a different course/activity, show that in confirmation dialog.\n const associatedLinkCount = document.querySelector(`a#recording-${data.action}-${data.recordingid}`)?.dataset?.links;\n if (!associatedLinkCount || associatedLinkCount === 0) {\n return confirmation;\n }\n\n const confirmationWarning = await getString(\n associatedLinkCount === 1\n ? `view_recording_${data.action}_confirmation_warning_p`\n : `view_recording_${data.action}_confirmation_warning_s`,\n 'bigbluebuttonbn',\n associatedLinkCount\n );\n\n return confirmationWarning + '\\n\\n' + confirmation;\n };\n\n /**\n * Process an action event.\n *\n * @param {Event} e\n */\n const processAction = e => {\n const popoutLink = e.target.closest('[data-action=\"play\"]');\n if (popoutLink) {\n e.preventDefault();\n\n const videoPlayer = window.open('', '_blank');\n videoPlayer.opener = null;\n videoPlayer.location.href = popoutLink.href;\n // TODO send a recording viewed event when this event will be implemented.\n return;\n }\n\n // Fetch any clicked anchor.\n const clickedLink = e.target.closest('a[data-action]');\n if (clickedLink && !clickedLink.classList.contains('disabled')) {\n e.preventDefault();\n\n // Create a spinning icon on the table.\n const iconPromise = addIconToContainerWithPromise(dataTable.get('boundingBox').getDOMNode());\n\n requestAction(clickedLink)\n .then(refreshTableData)\n .then(iconPromise.resolve)\n .catch(displayException);\n }\n };\n\n const processSearchSubmission = e => {\n // Prevent the default action.\n e.preventDefault();\n const parentNode = e.target.closest('div[role=search]');\n const searchInput = parentNode.querySelector('input[name=search]');\n filterByText(searchInput.value);\n };\n\n const registerEventListeners = () => {\n // Add event listeners to the table boundingBox.\n const boundingBox = dataTable.get('boundingBox').getDOMNode();\n boundingBox.addEventListener('click', processAction);\n\n // Setup the search from handlers.\n const searchForm = document.querySelector(searchFormId);\n if (searchForm) {\n const searchButton = document.querySelector(searchFormId + ' button');\n searchButton.addEventListener('click', processSearchSubmission);\n }\n };\n\n return {\n filterByText,\n refreshTableData,\n registerEventListeners,\n };\n};\n\n/**\n * Setup the data table for the specified BBB instance.\n *\n * @param {String} tableId in which we will display the table\n * @param {String} searchFormId The Id of the relate.\n * @param {object} response The response from the data request\n * @returns {Promise}\n */\nconst setupDatatable = (tableId, searchFormId, response) => {\n if (!response) {\n return Promise.resolve();\n }\n\n if (!response.status) {\n // Something failed. Continue to show the plain output.\n return Promise.resolve();\n }\n\n const recordingData = response.tabledata;\n\n const pendingPromise = new Pending('mod_bigbluebuttonbn/recordings/setupDatatable');\n return Promise.all([getYuiInstance(recordingData.locale), getStringsForYui()])\n .then(([yuiInstance, strings]) => {\n // Here we use a custom formatter for date.\n // See https://clarle.github.io/yui3/yui/docs/api/classes/DataTable.BodyView.Formatters.html\n // Inspired from examples here: https://clarle.github.io/yui3/yui/docs/datatable/\n // Normally formatter have the prototype: (col) => (cell) => , see:\n // https://clarle.github.io/yui3/yui/docs/api/files/datatable_js_formatters.js.html#l100 .\n const dateCustomFormatter = () => (cell) => formatDate(recordingData.locale, cell.value);\n // Add the fetched strings to the YUI Instance.\n yuiInstance.Intl.add('datatable-paginator', yuiInstance.config.lang, {...strings});\n yuiInstance.DataTable.BodyView.Formatters.customDate = dateCustomFormatter;\n return yuiInstance;\n })\n .then(yuiInstance => {\n\n const tableData = getFormattedData(response);\n yuiInstance.RecordsPaginatorView = Y.Base.create('my-paginator-view', yuiInstance.DataTable.Paginator.View, [], {\n _modelChange: function(e) {\n var changed = e.changed,\n totalItems = (changed && changed.totalItems);\n if (totalItems) {\n this._updateControlsUI(e.target.get('page'));\n }\n }\n });\n return new yuiInstance.DataTable({\n paginatorView: \"RecordsPaginatorView\",\n width: \"1195px\",\n columns: recordingData.columns,\n data: tableData,\n rowsPerPage: 10,\n paginatorLocation: ['header', 'footer'],\n autoSync: true\n });\n })\n .then(dataTable => {\n dataTable.render(tableId);\n const {registerEventListeners} = getDataTableFunctions(\n tableId,\n searchFormId,\n dataTable);\n registerEventListeners();\n return dataTable;\n })\n .then(dataTable => {\n pendingPromise.resolve();\n return dataTable;\n });\n};\n\n/**\n * Initialise recordings code.\n *\n * @method init\n * @param {String} tableId in which we will display the table\n * @param {String} searchFormId The Id of the relate.\n */\nexport const init = (tableId, searchFormId) => {\n const pendingPromise = new Pending('mod_bigbluebuttonbn/recordings:init');\n\n fetchRecordingData(tableId)\n .then(response => setupDatatable(tableId, searchFormId, response))\n .then(() => pendingPromise.resolve())\n .catch(displayException);\n};\n"],"names":["stringsWithKeys","first","prev","next","last","goToLabel","goToAction","perPage","showAll","Object","entries","map","entry","getStringsForYui","stringMap","keys","key","component","then","stringArray","assign","index","catch","getFormattedData","response","recordingData","tabledata","JSON","parse","data","getTableNode","tableSelector","document","querySelector","fetchRecordingData","tableNode","Promise","resolve","dataset","importMode","repository","fetchRecordingsToImport","bbbid","bbbSourceInstanceId","bbbSourceCourseId","tools","groupId","fetchRecordings","getDataTableFunctions","tableId","searchFormId","dataTable","updateTableFromResponse","status","get","reset","set","currentFilter","filterByText","refreshTableData","value","dataModel","escapedRegex","replace","rsearch","RegExp","filter","asList","item","name","test","description","recordingConfirmationMessage","async","playbackElement","recordingid","recordingType","imported","confirmation","action","associatedLinkCount","_document$querySelect","_document$querySelect2","links","processAction","e","popoutLink","target","closest","preventDefault","videoPlayer","window","open","opener","location","href","clickedLink","classList","contains","iconPromise","getDOMNode","element","getDataFromAction","dataType","dataElement","elementData","payload","bigbluebuttonbnid","additionaloptions","bbbsourceid","bbbcourseid","sourceid","stringify","requireConfirmation","ModalFactory","create","title","body","type","types","SAVE_CANCEL","modal","setSaveButtonText","getRoot","on","ModalEvents","save","hidden","destroy","show","displayException","proceed","updateRecording","requestAction","processSearchSubmission","searchInput","registerEventListeners","addEventListener","setupDatatable","pendingPromise","Pending","all","lang","locale","YUI","use","Y","_ref","yuiInstance","strings","Intl","add","config","DataTable","BodyView","Formatters","customDate","cell","formatDate","date","Date","toLocaleDateString","weekday","year","month","day","tableData","RecordsPaginatorView","Base","Paginator","View","_modelChange","changed","totalItems","_updateControlsUI","paginatorView","width","columns","rowsPerPage","paginatorLocation","autoSync","render"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"recordings.min.js","sources":["../src/recordings.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 * JS for the recordings page on mod_bigbluebuttonbn plugin.\n *\n * @module mod_bigbluebuttonbn/recordings\n * @copyright 2021 Blindside Networks Inc\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as repository from './repository';\nimport {exception as displayException} from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString, getStrings} from 'core/str';\nimport {addIconToContainerWithPromise} from 'core/loadingicon';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Pending from 'core/pending';\n\nconst stringsWithKeys = {\n first: 'view_recording_yui_first',\n prev: 'view_recording_yui_prev',\n next: 'view_recording_yui_next',\n last: 'view_recording_yui_last',\n goToLabel: 'view_recording_yui_page',\n goToAction: 'view_recording_yui_go',\n perPage: 'view_recording_yui_rows',\n showAll: 'view_recording_yui_show_all',\n};\n// Load global strings.\nprefetchStrings('bigbluebuttonbn', Object.entries(stringsWithKeys).map((entry) => entry[1]));\n\nconst getStringsForYui = () => {\n const stringMap = Object.keys(stringsWithKeys).map(key => {\n return {\n key: stringsWithKeys[key],\n component: 'mod_bigbluebuttonbn',\n };\n });\n\n // Return an object with the matching string keys (we want an object with {: ...}).\n return getStrings(stringMap)\n .then((stringArray) => Object.assign({}, ...Object.keys(stringsWithKeys).map(\n (key, index) => ({[key]: stringArray[index]})))\n ).catch();\n};\n\nconst getYuiInstance = lang => new Promise(resolve => {\n // eslint-disable-next-line\n YUI({\n lang,\n }).use('intl', 'datatable', 'datatable-sort', 'datatable-paginator', 'datatype-number', Y => {\n resolve(Y);\n });\n});\n\n/**\n * Format the supplied date per the specified locale.\n *\n * @param {string} locale\n * @param {number} date\n * @returns {array}\n */\nconst formatDate = (locale, date) => {\n const realDate = new Date(date);\n return realDate.toLocaleDateString(locale, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n });\n};\n\n/**\n * Format response data for the table.\n *\n * @param {string} response JSON-encoded table data\n * @returns {array}\n */\nconst getFormattedData = response => {\n const recordingData = response.tabledata;\n return JSON.parse(recordingData.data);\n};\n\nconst getTableNode = tableSelector => document.querySelector(tableSelector);\n\nconst fetchRecordingData = tableSelector => {\n const tableNode = getTableNode(tableSelector);\n if (tableNode === null) {\n return Promise.resolve(false);\n }\n\n if (tableNode.dataset.importMode) {\n return repository.fetchRecordingsToImport(\n tableNode.dataset.bbbid,\n tableNode.dataset.bbbSourceInstanceId,\n tableNode.dataset.bbbSourceCourseId,\n tableNode.dataset.tools,\n tableNode.dataset.groupId\n );\n } else {\n return repository.fetchRecordings(\n tableNode.dataset.bbbid,\n tableNode.dataset.tools,\n tableNode.dataset.groupId\n );\n }\n};\n\n/**\n * Fetch the data table functinos for the specified table.\n *\n * @param {String} tableId in which we will display the table\n * @param {String} searchFormId The Id of the relate.\n * @param {Object} dataTable\n * @returns {Object}\n * @private\n */\nconst getDataTableFunctions = (tableId, searchFormId, dataTable) => {\n const tableNode = getTableNode(tableId);\n const bbbid = tableNode.dataset.bbbid;\n\n const updateTableFromResponse = response => {\n if (!response || !response.status) {\n // There was no output at all.\n return;\n }\n\n dataTable.get('data').reset(getFormattedData(response));\n dataTable.set(\n 'currentData',\n dataTable.get('data')\n );\n\n const currentFilter = dataTable.get('currentFilter');\n if (currentFilter) {\n filterByText(currentFilter);\n }\n };\n\n const refreshTableData = () => fetchRecordingData(tableId).then(updateTableFromResponse);\n\n const filterByText = value => {\n const dataModel = dataTable.get('currentData');\n dataTable.set('currentFilter', value);\n\n const escapedRegex = value.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, \"\\\\$&\");\n const rsearch = new RegExp(`.*?${escapedRegex}.*?`, 'i');\n\n dataTable.set('data', dataModel.filter({asList: true}, item => {\n const name = item.get('recording');\n if (name && rsearch.test(name)) {\n return true;\n }\n\n const description = item.get('description');\n return description && rsearch.test(description);\n }));\n };\n\n const requestAction = (element) => {\n const getDataFromAction = (element, dataType) => {\n const dataElement = element.closest(`[data-${dataType}]`);\n if (dataElement) {\n return dataElement.dataset[dataType];\n }\n\n return null;\n };\n\n const elementData = element.dataset;\n const payload = {\n bigbluebuttonbnid: bbbid,\n recordingid: getDataFromAction(element, 'recordingid'),\n additionaloptions: getDataFromAction(element, 'additionaloptions'),\n action: elementData.action,\n };\n // Slight change for import, for additional options.\n if (!payload.additionaloptions) {\n payload.additionaloptions = {};\n }\n if (elementData.action === 'import') {\n const bbbsourceid = getDataFromAction(element, 'source-instance-id');\n const bbbcourseid = getDataFromAction(element, 'source-course-id');\n if (!payload.additionaloptions) {\n payload.additionaloptions = {};\n }\n payload.additionaloptions.sourceid = bbbsourceid ? bbbsourceid : 0;\n payload.additionaloptions.bbbcourseid = bbbcourseid ? bbbcourseid : 0;\n }\n // Now additional options should be a json string.\n payload.additionaloptions = JSON.stringify(payload.additionaloptions);\n if (element.dataset.requireConfirmation === \"1\") {\n // Create the confirmation dialogue.\n return new Promise((resolve) =>\n ModalFactory.create({\n title: getString('confirm'),\n body: recordingConfirmationMessage(payload),\n type: ModalFactory.types.SAVE_CANCEL\n }).then(async(modal) => {\n modal.setSaveButtonText(await getString('ok', 'moodle'));\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, () => {\n resolve(true);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, () => {\n // Destroy when hidden.\n modal.destroy();\n resolve(false);\n });\n\n modal.show();\n\n return modal;\n }).catch(displayException)\n ).then((proceed) =>\n proceed ? repository.updateRecording(payload) : () => null\n );\n } else {\n return repository.updateRecording(payload);\n }\n };\n\n const recordingConfirmationMessage = async(data) => {\n\n const playbackElement = document.querySelector(`#playbacks-${data.recordingid}`);\n const recordingType = await getString(\n playbackElement.dataset.imported === 'true' ? 'view_recording_link' : 'view_recording',\n 'bigbluebuttonbn'\n );\n\n const confirmation = await getString(`view_recording_${data.action}_confirmation`, 'bigbluebuttonbn', recordingType);\n\n if (data.action === 'import') {\n return confirmation;\n }\n\n // If it has associated links imported in a different course/activity, show that in confirmation dialog.\n const associatedLinkCount = document.querySelector(`a#recording-${data.action}-${data.recordingid}`)?.dataset?.links;\n if (!associatedLinkCount || associatedLinkCount === 0) {\n return confirmation;\n }\n\n const confirmationWarning = await getString(\n associatedLinkCount === 1\n ? `view_recording_${data.action}_confirmation_warning_p`\n : `view_recording_${data.action}_confirmation_warning_s`,\n 'bigbluebuttonbn',\n associatedLinkCount\n );\n\n return confirmationWarning + '\\n\\n' + confirmation;\n };\n\n /**\n * Process an action event.\n *\n * @param {Event} e\n */\n const processAction = e => {\n const popoutLink = e.target.closest('[data-action=\"play\"]');\n if (popoutLink) {\n e.preventDefault();\n\n const videoPlayer = window.open('', '_blank');\n videoPlayer.opener = null;\n videoPlayer.location.href = popoutLink.href;\n // TODO send a recording viewed event when this event will be implemented.\n return;\n }\n\n // Fetch any clicked anchor.\n const clickedLink = e.target.closest('a[data-action]');\n if (clickedLink && !clickedLink.classList.contains('disabled')) {\n e.preventDefault();\n\n // Create a spinning icon on the table.\n const iconPromise = addIconToContainerWithPromise(dataTable.get('boundingBox').getDOMNode());\n\n requestAction(clickedLink)\n .then(refreshTableData)\n .then(iconPromise.resolve)\n .catch(displayException);\n }\n };\n\n const processSearchSubmission = e => {\n // Prevent the default action.\n e.preventDefault();\n const parentNode = e.target.closest('div[role=search]');\n const searchInput = parentNode.querySelector('input[name=search]');\n filterByText(searchInput.value);\n };\n\n const registerEventListeners = () => {\n // Add event listeners to the table boundingBox.\n const boundingBox = dataTable.get('boundingBox').getDOMNode();\n boundingBox.addEventListener('click', processAction);\n\n // Setup the search from handlers.\n const searchForm = document.querySelector(searchFormId);\n if (searchForm) {\n const searchButton = document.querySelector(searchFormId + ' button');\n searchButton.addEventListener('click', processSearchSubmission);\n }\n };\n\n return {\n filterByText,\n refreshTableData,\n registerEventListeners,\n };\n};\n\n/**\n * Setup the data table for the specified BBB instance.\n *\n * @param {String} tableId in which we will display the table\n * @param {String} searchFormId The Id of the relate.\n * @param {object} response The response from the data request\n * @returns {Promise}\n */\nconst setupDatatable = (tableId, searchFormId, response) => {\n if (!response) {\n return Promise.resolve();\n }\n\n if (!response.status) {\n // Something failed. Continue to show the plain output.\n return Promise.resolve();\n }\n\n const recordingData = response.tabledata;\n\n const pendingPromise = new Pending('mod_bigbluebuttonbn/recordings/setupDatatable');\n return Promise.all([getYuiInstance(recordingData.locale), getStringsForYui()])\n .then(([yuiInstance, strings]) => {\n // Here we use a custom formatter for date.\n // See https://clarle.github.io/yui3/yui/docs/api/classes/DataTable.BodyView.Formatters.html\n // Inspired from examples here: https://clarle.github.io/yui3/yui/docs/datatable/\n // Normally formatter have the prototype: (col) => (cell) => , see:\n // https://clarle.github.io/yui3/yui/docs/api/files/datatable_js_formatters.js.html#l100 .\n const dateCustomFormatter = () => (cell) => formatDate(recordingData.locale, cell.value);\n // Add the fetched strings to the YUI Instance.\n yuiInstance.Intl.add('datatable-paginator', yuiInstance.config.lang, {...strings});\n yuiInstance.DataTable.BodyView.Formatters.customDate = dateCustomFormatter;\n return yuiInstance;\n })\n .then(yuiInstance => {\n\n const tableData = getFormattedData(response);\n yuiInstance.RecordsPaginatorView = Y.Base.create('my-paginator-view', yuiInstance.DataTable.Paginator.View, [], {\n _modelChange: function(e) {\n var changed = e.changed,\n totalItems = (changed && changed.totalItems);\n if (totalItems) {\n this._updateControlsUI(e.target.get('page'));\n }\n }\n });\n return new yuiInstance.DataTable({\n paginatorView: \"RecordsPaginatorView\",\n width: \"1195px\",\n columns: recordingData.columns,\n data: tableData,\n rowsPerPage: 10,\n paginatorLocation: ['header', 'footer'],\n autoSync: true\n });\n })\n .then(dataTable => {\n dataTable.render(tableId);\n const {registerEventListeners} = getDataTableFunctions(\n tableId,\n searchFormId,\n dataTable);\n registerEventListeners();\n return dataTable;\n })\n .then(dataTable => {\n pendingPromise.resolve();\n return dataTable;\n });\n};\n\n/**\n * Initialise recordings code.\n *\n * @method init\n * @param {String} tableId in which we will display the table\n * @param {String} searchFormId The Id of the relate.\n */\nexport const init = (tableId, searchFormId) => {\n const pendingPromise = new Pending('mod_bigbluebuttonbn/recordings:init');\n\n fetchRecordingData(tableId)\n .then(response => setupDatatable(tableId, searchFormId, response))\n .then(() => pendingPromise.resolve())\n .catch(displayException);\n};\n"],"names":["stringsWithKeys","first","prev","next","last","goToLabel","goToAction","perPage","showAll","Object","entries","map","entry","getStringsForYui","stringMap","keys","key","component","then","stringArray","assign","index","catch","getFormattedData","response","recordingData","tabledata","JSON","parse","data","getTableNode","tableSelector","document","querySelector","fetchRecordingData","tableNode","Promise","resolve","dataset","importMode","repository","fetchRecordingsToImport","bbbid","bbbSourceInstanceId","bbbSourceCourseId","tools","groupId","fetchRecordings","getDataTableFunctions","tableId","searchFormId","dataTable","updateTableFromResponse","status","get","reset","set","currentFilter","filterByText","refreshTableData","value","dataModel","escapedRegex","replace","rsearch","RegExp","filter","asList","item","name","test","description","recordingConfirmationMessage","async","playbackElement","recordingid","recordingType","imported","confirmation","action","associatedLinkCount","_document$querySelect","_document$querySelect2","links","processAction","e","popoutLink","target","closest","preventDefault","videoPlayer","window","open","opener","location","href","clickedLink","classList","contains","iconPromise","getDOMNode","element","getDataFromAction","dataType","dataElement","elementData","payload","bigbluebuttonbnid","additionaloptions","bbbsourceid","bbbcourseid","sourceid","stringify","requireConfirmation","ModalFactory","create","title","body","type","types","SAVE_CANCEL","modal","setSaveButtonText","getRoot","on","ModalEvents","save","hidden","destroy","show","displayException","proceed","updateRecording","requestAction","processSearchSubmission","searchInput","registerEventListeners","addEventListener","setupDatatable","pendingPromise","Pending","all","lang","locale","YUI","use","Y","_ref","yuiInstance","strings","Intl","add","config","DataTable","BodyView","Formatters","customDate","cell","formatDate","date","Date","toLocaleDateString","weekday","year","month","day","tableData","RecordsPaginatorView","Base","Paginator","View","_modelChange","changed","totalItems","_updateControlsUI","paginatorView","width","columns","rowsPerPage","paginatorLocation","autoSync","render"],"mappings":"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"} \ No newline at end of file diff --git a/mod/bigbluebuttonbn/amd/src/actions.js b/mod/bigbluebuttonbn/amd/src/actions.js index dc0aa27674d..5effcba372a 100644 --- a/mod/bigbluebuttonbn/amd/src/actions.js +++ b/mod/bigbluebuttonbn/amd/src/actions.js @@ -26,7 +26,7 @@ import { saveCancel, } from 'core/notification'; import {notifySessionEnded} from './events'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; const confirmedPromise = (title, question, saveLabel) => new Promise(resolve => { saveCancel(title, question, saveLabel, resolve); diff --git a/mod/bigbluebuttonbn/amd/src/guest_access_modal.js b/mod/bigbluebuttonbn/amd/src/guest_access_modal.js index 13efd6a1dac..e673b7350bc 100644 --- a/mod/bigbluebuttonbn/amd/src/guest_access_modal.js +++ b/mod/bigbluebuttonbn/amd/src/guest_access_modal.js @@ -20,7 +20,7 @@ * @copyright 2022 Blindside Networks Inc * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import ModalForm from 'core_form/modalform'; import {add as toastAdd, addToastRegion} from 'core/toast'; import { diff --git a/mod/bigbluebuttonbn/amd/src/modform.js b/mod/bigbluebuttonbn/amd/src/modform.js index c1a6c9f23cd..9e501be8faa 100644 --- a/mod/bigbluebuttonbn/amd/src/modform.js +++ b/mod/bigbluebuttonbn/amd/src/modform.js @@ -21,7 +21,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Notification from 'core/notification'; import Templates from "core/templates"; diff --git a/mod/bigbluebuttonbn/amd/src/recordings.js b/mod/bigbluebuttonbn/amd/src/recordings.js index 23ba066d8de..49f182f6e39 100644 --- a/mod/bigbluebuttonbn/amd/src/recordings.js +++ b/mod/bigbluebuttonbn/amd/src/recordings.js @@ -24,7 +24,7 @@ import * as repository from './repository'; import {exception as displayException} from 'core/notification'; import {prefetchStrings} from 'core/prefetch'; -import {get_string as getString, get_strings as getStrings} from 'core/str'; +import {getString, getStrings} from 'core/str'; import {addIconToContainerWithPromise} from 'core/loadingicon'; import ModalFactory from 'core/modal_factory'; import ModalEvents from 'core/modal_events'; diff --git a/mod/data/amd/build/deletepreset.min.js b/mod/data/amd/build/deletepreset.min.js index 218c593d9c1..37b337271ab 100644 --- a/mod/data/amd/build/deletepreset.min.js +++ b/mod/data/amd/build/deletepreset.min.js @@ -5,6 +5,6 @@ define("mod_data/deletepreset",["exports","core/notification","core/prefetch","c * @module mod_data/deletepreset * @copyright 2022 Amaia Anabitarte * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_ajax=_interopRequireDefault(_ajax),_url=_interopRequireDefault(_url);const selectors_deletePresetButton='[data-action="deletepreset"]';_exports.init=()=>{(0,_prefetch.prefetchStrings)("mod_data",["deleteconfirm","deletewarning"]),(0,_prefetch.prefetchStrings)("core",["delete"]),registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const deleteOption=event.target.closest(selectors_deletePresetButton);deleteOption&&(event.preventDefault(),deletePresetConfirm(deleteOption))}))},deletePresetConfirm=deleteOption=>{const presetName=deleteOption.getAttribute("data-presetname"),dataId=deleteOption.getAttribute("data-dataid");_notification.default.deleteCancelPromise((0,_str.get_string)("deleteconfirm","mod_data",presetName),(0,_str.get_string)("deletewarning","mod_data")).then((()=>async function(dataId,presetName){var request={methodname:"mod_data_delete_saved_preset",args:{dataid:dataId,presetnames:{presetname:presetName}}};try{await _ajax.default.call([request])[0],window.location.href=_url.default.relativeUrl("mod/data/preset.php",{d:dataId},!1)}catch(error){_notification.default.exception(error)}}(dataId,presetName))).catch((()=>{}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_ajax=_interopRequireDefault(_ajax),_url=_interopRequireDefault(_url);const selectors_deletePresetButton='[data-action="deletepreset"]';_exports.init=()=>{(0,_prefetch.prefetchStrings)("mod_data",["deleteconfirm","deletewarning"]),(0,_prefetch.prefetchStrings)("core",["delete"]),registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const deleteOption=event.target.closest(selectors_deletePresetButton);deleteOption&&(event.preventDefault(),deletePresetConfirm(deleteOption))}))},deletePresetConfirm=deleteOption=>{const presetName=deleteOption.getAttribute("data-presetname"),dataId=deleteOption.getAttribute("data-dataid");_notification.default.deleteCancelPromise((0,_str.getString)("deleteconfirm","mod_data",presetName),(0,_str.getString)("deletewarning","mod_data")).then((()=>async function(dataId,presetName){var request={methodname:"mod_data_delete_saved_preset",args:{dataid:dataId,presetnames:{presetname:presetName}}};try{await _ajax.default.call([request])[0],window.location.href=_url.default.relativeUrl("mod/data/preset.php",{d:dataId},!1)}catch(error){_notification.default.exception(error)}}(dataId,presetName))).catch((()=>{}))}})); //# sourceMappingURL=deletepreset.min.js.map \ No newline at end of file diff --git a/mod/data/amd/build/deletepreset.min.js.map b/mod/data/amd/build/deletepreset.min.js.map index 1e0efdae6c3..dbae42e39a5 100644 --- a/mod/data/amd/build/deletepreset.min.js.map +++ b/mod/data/amd/build/deletepreset.min.js.map @@ -1 +1 @@ -{"version":3,"file":"deletepreset.min.js","sources":["../src/deletepreset.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 * Javascript module for deleting a database as a preset.\n *\n * @module mod_data/deletepreset\n * @copyright 2022 Amaia Anabitarte \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport Ajax from 'core/ajax';\nimport Url from 'core/url';\n\nconst selectors = {\n deletePresetButton: '[data-action=\"deletepreset\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n prefetchStrings('mod_data', [\n 'deleteconfirm',\n 'deletewarning',\n ]);\n prefetchStrings('core', [\n 'delete',\n ]);\n\n registerEventListeners();\n};\n\n/**\n * Register events for delete preset option in action menu.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const deleteOption = event.target.closest(selectors.deletePresetButton);\n if (deleteOption) {\n event.preventDefault();\n deletePresetConfirm(deleteOption);\n }\n });\n};\n\n/**\n * Show the confirmation modal to delete the preset.\n *\n * @param {HTMLElement} deleteOption the element to delete.\n */\nconst deletePresetConfirm = (deleteOption) => {\n const presetName = deleteOption.getAttribute('data-presetname');\n const dataId = deleteOption.getAttribute('data-dataid');\n\n Notification.deleteCancelPromise(\n getString('deleteconfirm', 'mod_data', presetName),\n getString('deletewarning', 'mod_data'),\n ).then(() => {\n return deletePreset(dataId, presetName);\n }).catch(() => {\n return;\n });\n};\n\n/**\n * Delete site user preset.\n *\n * @param {int} dataId The id of the current database activity.\n * @param {string} presetName The preset name to delete.\n * @return {promise} Resolved with the result and warnings of deleting a preset.\n */\nasync function deletePreset(dataId, presetName) {\n var request = {\n methodname: 'mod_data_delete_saved_preset',\n args: {\n dataid: dataId,\n presetnames: {presetname: presetName},\n }\n };\n try {\n await Ajax.call([request])[0];\n window.location.href = Url.relativeUrl(\n 'mod/data/preset.php',\n {\n d: dataId,\n },\n false\n );\n } catch (error) {\n Notification.exception(error);\n }\n}\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","deleteOption","target","closest","preventDefault","deletePresetConfirm","presetName","getAttribute","dataId","deleteCancelPromise","then","request","methodname","args","dataid","presetnames","presetname","Ajax","call","window","location","href","Url","relativeUrl","d","error","exception","deletePreset","catch"],"mappings":";;;;;;;kNA6BMA,6BACkB,6CAMJ,mCACA,WAAY,CACxB,gBACA,gDAEY,OAAQ,CACpB,WAGJC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,aAAeD,MAAME,OAAOC,QAAQP,8BACtCK,eACAD,MAAMI,iBACNC,oBAAoBJ,mBAU1BI,oBAAuBJ,qBACnBK,WAAaL,aAAaM,aAAa,mBACvCC,OAASP,aAAaM,aAAa,qCAE5BE,qBACT,mBAAU,gBAAiB,WAAYH,aACvC,mBAAU,gBAAiB,aAC7BI,MAAK,mBAciBF,OAAQF,gBAC5BK,QAAU,CACVC,WAAY,+BACZC,KAAM,CACFC,OAAQN,OACRO,YAAa,CAACC,WAAYV,wBAIxBW,cAAKC,KAAK,CAACP,UAAU,GAC3BQ,OAAOC,SAASC,KAAOC,aAAIC,YACvB,sBACA,CACIC,EAAGhB,SAEP,GAEN,MAAOiB,6BACQC,UAAUD,QA/BhBE,CAAanB,OAAQF,cAC7BsB,OAAM"} \ No newline at end of file +{"version":3,"file":"deletepreset.min.js","sources":["../src/deletepreset.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 * Javascript module for deleting a database as a preset.\n *\n * @module mod_data/deletepreset\n * @copyright 2022 Amaia Anabitarte \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport Ajax from 'core/ajax';\nimport Url from 'core/url';\n\nconst selectors = {\n deletePresetButton: '[data-action=\"deletepreset\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n prefetchStrings('mod_data', [\n 'deleteconfirm',\n 'deletewarning',\n ]);\n prefetchStrings('core', [\n 'delete',\n ]);\n\n registerEventListeners();\n};\n\n/**\n * Register events for delete preset option in action menu.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const deleteOption = event.target.closest(selectors.deletePresetButton);\n if (deleteOption) {\n event.preventDefault();\n deletePresetConfirm(deleteOption);\n }\n });\n};\n\n/**\n * Show the confirmation modal to delete the preset.\n *\n * @param {HTMLElement} deleteOption the element to delete.\n */\nconst deletePresetConfirm = (deleteOption) => {\n const presetName = deleteOption.getAttribute('data-presetname');\n const dataId = deleteOption.getAttribute('data-dataid');\n\n Notification.deleteCancelPromise(\n getString('deleteconfirm', 'mod_data', presetName),\n getString('deletewarning', 'mod_data'),\n ).then(() => {\n return deletePreset(dataId, presetName);\n }).catch(() => {\n return;\n });\n};\n\n/**\n * Delete site user preset.\n *\n * @param {int} dataId The id of the current database activity.\n * @param {string} presetName The preset name to delete.\n * @return {promise} Resolved with the result and warnings of deleting a preset.\n */\nasync function deletePreset(dataId, presetName) {\n var request = {\n methodname: 'mod_data_delete_saved_preset',\n args: {\n dataid: dataId,\n presetnames: {presetname: presetName},\n }\n };\n try {\n await Ajax.call([request])[0];\n window.location.href = Url.relativeUrl(\n 'mod/data/preset.php',\n {\n d: dataId,\n },\n false\n );\n } catch (error) {\n Notification.exception(error);\n }\n}\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","deleteOption","target","closest","preventDefault","deletePresetConfirm","presetName","getAttribute","dataId","deleteCancelPromise","then","request","methodname","args","dataid","presetnames","presetname","Ajax","call","window","location","href","Url","relativeUrl","d","error","exception","deletePreset","catch"],"mappings":";;;;;;;kNA6BMA,6BACkB,6CAMJ,mCACA,WAAY,CACxB,gBACA,gDAEY,OAAQ,CACpB,WAGJC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,aAAeD,MAAME,OAAOC,QAAQP,8BACtCK,eACAD,MAAMI,iBACNC,oBAAoBJ,mBAU1BI,oBAAuBJ,qBACnBK,WAAaL,aAAaM,aAAa,mBACvCC,OAASP,aAAaM,aAAa,qCAE5BE,qBACT,kBAAU,gBAAiB,WAAYH,aACvC,kBAAU,gBAAiB,aAC7BI,MAAK,mBAciBF,OAAQF,gBAC5BK,QAAU,CACVC,WAAY,+BACZC,KAAM,CACFC,OAAQN,OACRO,YAAa,CAACC,WAAYV,wBAIxBW,cAAKC,KAAK,CAACP,UAAU,GAC3BQ,OAAOC,SAASC,KAAOC,aAAIC,YACvB,sBACA,CACIC,EAAGhB,SAEP,GAEN,MAAOiB,6BACQC,UAAUD,QA/BhBE,CAAanB,OAAQF,cAC7BsB,OAAM"} \ No newline at end of file diff --git a/mod/data/amd/build/editpreset.min.js b/mod/data/amd/build/editpreset.min.js index 0ebc8213530..64a65c37ac3 100644 --- a/mod/data/amd/build/editpreset.min.js +++ b/mod/data/amd/build/editpreset.min.js @@ -5,6 +5,6 @@ define("mod_data/editpreset",["exports","core_form/modalform","core/notification * @module mod_data/editpreset * @copyright 2022 Sara Arjona * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_editPresetButton='[data-action="editpreset"]';_exports.init=()=>{registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const editAction=event.target.closest(selectors_editPresetButton);editAction&&(event.preventDefault(),showEditPresetModal(editAction))}))},showEditPresetModal=editAction=>{const modalForm=new _modalform.default({modalConfig:{title:(0,_str.get_string)("editpreset","mod_data")},formClass:"mod_data\\form\\save_as_preset",args:{d:editAction.getAttribute("data-dataid"),action:editAction.getAttribute("data-action"),presetname:editAction.getAttribute("data-presetname"),presetdescription:editAction.getAttribute("data-presetdescription")},saveButtonText:(0,_str.get_string)("save"),returnFocus:editAction});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.reload():_notification.default.addNotification({type:"error",message:event.detail.errors.join("
")})})),modalForm.show()}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_editPresetButton='[data-action="editpreset"]';_exports.init=()=>{registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const editAction=event.target.closest(selectors_editPresetButton);editAction&&(event.preventDefault(),showEditPresetModal(editAction))}))},showEditPresetModal=editAction=>{const modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("editpreset","mod_data")},formClass:"mod_data\\form\\save_as_preset",args:{d:editAction.getAttribute("data-dataid"),action:editAction.getAttribute("data-action"),presetname:editAction.getAttribute("data-presetname"),presetdescription:editAction.getAttribute("data-presetdescription")},saveButtonText:(0,_str.getString)("save"),returnFocus:editAction});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.reload():_notification.default.addNotification({type:"error",message:event.detail.errors.join("
")})})),modalForm.show()}})); //# sourceMappingURL=editpreset.min.js.map \ No newline at end of file diff --git a/mod/data/amd/build/editpreset.min.js.map b/mod/data/amd/build/editpreset.min.js.map index ea49ea70811..f26f29fba67 100644 --- a/mod/data/amd/build/editpreset.min.js.map +++ b/mod/data/amd/build/editpreset.min.js.map @@ -1 +1 @@ -{"version":3,"file":"editpreset.min.js","sources":["../src/editpreset.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 * Javascript module for editing a database preset.\n *\n * @module mod_data/editpreset\n * @copyright 2022 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\n\nconst selectors = {\n editPresetButton: '[data-action=\"editpreset\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n registerEventListeners();\n};\n\n/**\n * Register events for update/delete links.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const editAction = event.target.closest(selectors.editPresetButton);\n if (editAction) {\n event.preventDefault();\n showEditPresetModal(editAction);\n }\n });\n};\n\n/**\n * Show the edit preset modal.\n *\n * @param {HTMLElement} editAction the edit action element.\n */\nconst showEditPresetModal = (editAction) => {\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('editpreset', 'mod_data'),\n },\n formClass: 'mod_data\\\\form\\\\save_as_preset',\n args: {\n d: editAction.getAttribute('data-dataid'),\n action: editAction.getAttribute('data-action'),\n presetname: editAction.getAttribute('data-presetname'),\n presetdescription: editAction.getAttribute('data-presetdescription')\n },\n saveButtonText: getString('save'),\n returnFocus: editAction,\n });\n\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.reload();\n } else {\n Notification.addNotification({\n type: 'error',\n message: event.detail.errors.join('
')\n });\n }\n });\n\n modalForm.show();\n};\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","editAction","target","closest","preventDefault","showEditPresetModal","modalForm","ModalForm","modalConfig","title","formClass","args","d","getAttribute","action","presetname","presetdescription","saveButtonText","returnFocus","events","FORM_SUBMITTED","detail","result","window","location","reload","addNotification","type","message","errors","join","show"],"mappings":";;;;;;;0LA2BMA,2BACgB,2CAMF,KAChBC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,WAAaD,MAAME,OAAOC,QAAQP,4BACpCK,aACAD,MAAMI,iBACNC,oBAAoBJ,iBAU1BI,oBAAuBJ,mBACnBK,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,mBAAU,aAAc,aAEnCC,UAAW,iCACXC,KAAM,CACFC,EAAGX,WAAWY,aAAa,eAC3BC,OAAQb,WAAWY,aAAa,eAChCE,WAAYd,WAAWY,aAAa,mBACpCG,kBAAmBf,WAAWY,aAAa,2BAE/CI,gBAAgB,mBAAU,QAC1BC,YAAajB,aAGjBK,UAAUP,iBAAiBO,UAAUa,OAAOC,gBAAgBpB,QACpDA,MAAMqB,OAAOC,OACbC,OAAOC,SAASC,+BAEHC,gBAAgB,CACzBC,KAAM,QACNC,QAAS5B,MAAMqB,OAAOQ,OAAOC,KAAK,aAK9CxB,UAAUyB"} \ No newline at end of file +{"version":3,"file":"editpreset.min.js","sources":["../src/editpreset.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 * Javascript module for editing a database preset.\n *\n * @module mod_data/editpreset\n * @copyright 2022 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {getString} from 'core/str';\n\nconst selectors = {\n editPresetButton: '[data-action=\"editpreset\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n registerEventListeners();\n};\n\n/**\n * Register events for update/delete links.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const editAction = event.target.closest(selectors.editPresetButton);\n if (editAction) {\n event.preventDefault();\n showEditPresetModal(editAction);\n }\n });\n};\n\n/**\n * Show the edit preset modal.\n *\n * @param {HTMLElement} editAction the edit action element.\n */\nconst showEditPresetModal = (editAction) => {\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('editpreset', 'mod_data'),\n },\n formClass: 'mod_data\\\\form\\\\save_as_preset',\n args: {\n d: editAction.getAttribute('data-dataid'),\n action: editAction.getAttribute('data-action'),\n presetname: editAction.getAttribute('data-presetname'),\n presetdescription: editAction.getAttribute('data-presetdescription')\n },\n saveButtonText: getString('save'),\n returnFocus: editAction,\n });\n\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.reload();\n } else {\n Notification.addNotification({\n type: 'error',\n message: event.detail.errors.join('
')\n });\n }\n });\n\n modalForm.show();\n};\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","editAction","target","closest","preventDefault","showEditPresetModal","modalForm","ModalForm","modalConfig","title","formClass","args","d","getAttribute","action","presetname","presetdescription","saveButtonText","returnFocus","events","FORM_SUBMITTED","detail","result","window","location","reload","addNotification","type","message","errors","join","show"],"mappings":";;;;;;;0LA2BMA,2BACgB,2CAMF,KAChBC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,WAAaD,MAAME,OAAOC,QAAQP,4BACpCK,aACAD,MAAMI,iBACNC,oBAAoBJ,iBAU1BI,oBAAuBJ,mBACnBK,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,aAAc,aAEnCC,UAAW,iCACXC,KAAM,CACFC,EAAGX,WAAWY,aAAa,eAC3BC,OAAQb,WAAWY,aAAa,eAChCE,WAAYd,WAAWY,aAAa,mBACpCG,kBAAmBf,WAAWY,aAAa,2BAE/CI,gBAAgB,kBAAU,QAC1BC,YAAajB,aAGjBK,UAAUP,iBAAiBO,UAAUa,OAAOC,gBAAgBpB,QACpDA,MAAMqB,OAAOC,OACbC,OAAOC,SAASC,+BAEHC,gBAAgB,CACzBC,KAAM,QACNC,QAAS5B,MAAMqB,OAAOQ,OAAOC,KAAK,aAK9CxB,UAAUyB"} \ No newline at end of file diff --git a/mod/data/amd/build/importmappingdialogue.min.js b/mod/data/amd/build/importmappingdialogue.min.js index cd0f373540b..90e340e67a6 100644 --- a/mod/data/amd/build/importmappingdialogue.min.js +++ b/mod/data/amd/build/importmappingdialogue.min.js @@ -5,6 +5,6 @@ define("mod_data/importmappingdialogue",["exports","core/notification","core/aja * @module mod_data/importmappingdialogue * @copyright 2022 Amaia Anabitarte * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_ajax=_interopRequireDefault(_ajax),_url=_interopRequireDefault(_url),_templates=_interopRequireDefault(_templates),_modal_factory=_interopRequireDefault(_modal_factory),(0,_prefetch.prefetchStrings)("mod_data",["mapping:dialogtitle:usepreset"]);const selectors_selectPreset='[data-action="selectpreset"]';_exports.init=()=>{registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const preset=event.target.closest(selectors_selectPreset);preset&&(event.preventDefault(),showMappingDialogue(preset))}))},showMappingDialogue=usepreset=>{const presetName=usepreset.dataset.presetname,cmId=usepreset.dataset.cmid;getMappingInformation(cmId,presetName).then((result=>(result.data&&result.data.needsmapping?buildModal({title:(0,_str.get_string)("mapping:dialogtitle:usepreset","mod_data",result.data.presetname),body:_templates.default.render("mod_data/fields_mapping_body",result.data),footer:_templates.default.render("mod_data/fields_mapping_footer",getMappingButtons(cmId,presetName)),large:!0}):window.location.href=_url.default.relativeUrl("mod/data/field.php",{id:cmId,mode:"usepreset",fullname:presetName},!1),!0))).catch(_notification.default.exception)},buildModal=params=>_modal_factory.default.create({...params,type:_modal_factory.default.types.DEFAULT}).then((modal=>(modal.show(),modal.showFooter(),modal.registerCloseOnCancel(),modal))).catch(_notification.default.exception),getMappingButtons=(cmId,presetName)=>{const data={};return data.mapfieldsbutton=_url.default.relativeUrl("mod/data/field.php",{id:cmId,fullname:presetName,mode:"usepreset",action:"select"},!1),data.applybutton=_url.default.relativeUrl("mod/data/field.php",{id:cmId,fullname:presetName,mode:"usepreset",action:"notmapping"},!1),data},getMappingInformation=(cmId,presetName)=>{const request={methodname:"mod_data_get_mapping_information",args:{cmid:cmId,importedpreset:presetName}};return _ajax.default.call([request])[0]}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_ajax=_interopRequireDefault(_ajax),_url=_interopRequireDefault(_url),_templates=_interopRequireDefault(_templates),_modal_factory=_interopRequireDefault(_modal_factory),(0,_prefetch.prefetchStrings)("mod_data",["mapping:dialogtitle:usepreset"]);const selectors_selectPreset='[data-action="selectpreset"]';_exports.init=()=>{registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const preset=event.target.closest(selectors_selectPreset);preset&&(event.preventDefault(),showMappingDialogue(preset))}))},showMappingDialogue=usepreset=>{const presetName=usepreset.dataset.presetname,cmId=usepreset.dataset.cmid;getMappingInformation(cmId,presetName).then((result=>(result.data&&result.data.needsmapping?buildModal({title:(0,_str.getString)("mapping:dialogtitle:usepreset","mod_data",result.data.presetname),body:_templates.default.render("mod_data/fields_mapping_body",result.data),footer:_templates.default.render("mod_data/fields_mapping_footer",getMappingButtons(cmId,presetName)),large:!0}):window.location.href=_url.default.relativeUrl("mod/data/field.php",{id:cmId,mode:"usepreset",fullname:presetName},!1),!0))).catch(_notification.default.exception)},buildModal=params=>_modal_factory.default.create({...params,type:_modal_factory.default.types.DEFAULT}).then((modal=>(modal.show(),modal.showFooter(),modal.registerCloseOnCancel(),modal))).catch(_notification.default.exception),getMappingButtons=(cmId,presetName)=>{const data={};return data.mapfieldsbutton=_url.default.relativeUrl("mod/data/field.php",{id:cmId,fullname:presetName,mode:"usepreset",action:"select"},!1),data.applybutton=_url.default.relativeUrl("mod/data/field.php",{id:cmId,fullname:presetName,mode:"usepreset",action:"notmapping"},!1),data},getMappingInformation=(cmId,presetName)=>{const request={methodname:"mod_data_get_mapping_information",args:{cmid:cmId,importedpreset:presetName}};return _ajax.default.call([request])[0]}})); //# sourceMappingURL=importmappingdialogue.min.js.map \ No newline at end of file diff --git a/mod/data/amd/build/importmappingdialogue.min.js.map b/mod/data/amd/build/importmappingdialogue.min.js.map index 60d4235b59a..64408098550 100644 --- a/mod/data/amd/build/importmappingdialogue.min.js.map +++ b/mod/data/amd/build/importmappingdialogue.min.js.map @@ -1 +1 @@ -{"version":3,"file":"importmappingdialogue.min.js","sources":["../src/importmappingdialogue.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 * Javascript module for deleting a database as a preset.\n *\n * @module mod_data/importmappingdialogue\n * @copyright 2022 Amaia Anabitarte \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from 'core/notification';\nimport Ajax from 'core/ajax';\nimport Url from 'core/url';\nimport Templates from 'core/templates';\nimport ModalFactory from 'core/modal_factory';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\n// Load global strings.\nprefetchStrings('mod_data', ['mapping:dialogtitle:usepreset']);\n\nconst selectors = {\n selectPreset: '[data-action=\"selectpreset\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n registerEventListeners();\n};\n\n/**\n * Register events.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const preset = event.target.closest(selectors.selectPreset);\n if (preset) {\n event.preventDefault();\n showMappingDialogue(preset);\n }\n });\n};\n\n/**\n * Show the confirmation modal for uploading a preset.\n *\n * @param {HTMLElement} usepreset the preset to import.\n */\nconst showMappingDialogue = (usepreset) => {\n const presetName = usepreset.dataset.presetname;\n const cmId = usepreset.dataset.cmid;\n\n getMappingInformation(cmId, presetName).then((result) => {\n if (result.data && result.data.needsmapping) {\n buildModal({\n title: getString('mapping:dialogtitle:usepreset', 'mod_data', result.data.presetname),\n body: Templates.render('mod_data/fields_mapping_body', result.data),\n footer: Templates.render('mod_data/fields_mapping_footer', getMappingButtons(cmId, presetName)),\n large: true,\n });\n } else {\n window.location.href = Url.relativeUrl(\n 'mod/data/field.php',\n {\n id: cmId,\n mode: 'usepreset',\n fullname: presetName,\n },\n false\n );\n }\n return true;\n }).catch(Notification.exception);\n};\n\n/**\n * Given an object we want to build a modal ready to show\n *\n * @method buildModal\n * @param {Object} params the modal params\n * @param {Promise} params.title\n * @param {Promise} params.body\n * @param {Promise} params.footer\n * @return {Object} The modal ready to display immediately and render body in later.\n */\nconst buildModal = (params) => {\n return ModalFactory.create({\n ...params,\n type: ModalFactory.types.DEFAULT,\n }).then(modal => {\n modal.show();\n modal.showFooter();\n modal.registerCloseOnCancel();\n return modal;\n }).catch(Notification.exception);\n};\n\n/**\n * Add buttons to render on mapping modal.\n *\n * @param {int} cmId The id of the current database activity.\n * @param {string} presetName The preset name to delete.\n * @return {array} Same data with buttons.\n */\nconst getMappingButtons = (cmId, presetName) => {\n const data = {};\n\n data.mapfieldsbutton = Url.relativeUrl(\n 'mod/data/field.php',\n {\n id: cmId,\n fullname: presetName,\n mode: 'usepreset',\n action: 'select',\n },\n false\n );\n\n data.applybutton = Url.relativeUrl(\n 'mod/data/field.php',\n {\n id: cmId,\n fullname: presetName,\n mode: 'usepreset',\n action: 'notmapping'\n },\n false\n );\n\n return data;\n};\n\n/**\n * Check whether we should show the mapping dialogue or not.\n *\n * @param {int} cmId The id of the current database activity.\n * @param {string} presetName The preset name to delete.\n * @return {promise} Resolved with the result and warnings of deleting a preset.\n */\nconst getMappingInformation = (cmId, presetName) => {\n const request = {\n methodname: 'mod_data_get_mapping_information',\n args: {\n cmid: cmId,\n importedpreset: presetName,\n }\n };\n return Ajax.call([request])[0];\n};\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","preset","target","closest","preventDefault","showMappingDialogue","usepreset","presetName","dataset","presetname","cmId","cmid","getMappingInformation","then","result","data","needsmapping","buildModal","title","body","Templates","render","footer","getMappingButtons","large","window","location","href","Url","relativeUrl","id","mode","fullname","catch","Notification","exception","params","ModalFactory","create","type","types","DEFAULT","modal","show","showFooter","registerCloseOnCancel","mapfieldsbutton","action","applybutton","request","methodname","args","importedpreset","Ajax","call"],"mappings":";;;;;;;8UAgCgB,WAAY,CAAC,wCAEvBA,uBACY,6CAME,KAChBC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,OAASD,MAAME,OAAOC,QAAQP,wBAChCK,SACAD,MAAMI,iBACNC,oBAAoBJ,aAU1BI,oBAAuBC,kBACnBC,WAAaD,UAAUE,QAAQC,WAC/BC,KAAOJ,UAAUE,QAAQG,KAE/BC,sBAAsBF,KAAMH,YAAYM,MAAMC,SACtCA,OAAOC,MAAQD,OAAOC,KAAKC,aAC3BC,WAAW,CACPC,OAAO,mBAAU,gCAAiC,WAAYJ,OAAOC,KAAKN,YAC1EU,KAAMC,mBAAUC,OAAO,+BAAgCP,OAAOC,MAC9DO,OAAQF,mBAAUC,OAAO,iCAAkCE,kBAAkBb,KAAMH,aACnFiB,OAAO,IAGXC,OAAOC,SAASC,KAAOC,aAAIC,YACvB,qBACA,CACIC,GAAIpB,KACJqB,KAAM,YACNC,SAAUzB,aAEd,IAGD,KACR0B,MAAMC,sBAAaC,YAapBlB,WAAcmB,QACTC,uBAAaC,OAAO,IACpBF,OACHG,KAAMF,uBAAaG,MAAMC,UAC1B5B,MAAK6B,QACJA,MAAMC,OACND,MAAME,aACNF,MAAMG,wBACCH,SACRT,MAAMC,sBAAaC,WAUpBZ,kBAAoB,CAACb,KAAMH,oBACvBQ,KAAO,UAEbA,KAAK+B,gBAAkBlB,aAAIC,YACvB,qBACA,CACIC,GAAIpB,KACJsB,SAAUzB,WACVwB,KAAM,YACNgB,OAAQ,WAEZ,GAGJhC,KAAKiC,YAAcpB,aAAIC,YACnB,qBACA,CACIC,GAAIpB,KACJsB,SAAUzB,WACVwB,KAAM,YACNgB,OAAQ,eAEZ,GAGGhC,MAULH,sBAAwB,CAACF,KAAMH,oBAC3B0C,QAAU,CACZC,WAAY,mCACZC,KAAM,CACFxC,KAAMD,KACN0C,eAAgB7C,oBAGjB8C,cAAKC,KAAK,CAACL,UAAU"} \ No newline at end of file +{"version":3,"file":"importmappingdialogue.min.js","sources":["../src/importmappingdialogue.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 * Javascript module for deleting a database as a preset.\n *\n * @module mod_data/importmappingdialogue\n * @copyright 2022 Amaia Anabitarte \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from 'core/notification';\nimport Ajax from 'core/ajax';\nimport Url from 'core/url';\nimport Templates from 'core/templates';\nimport ModalFactory from 'core/modal_factory';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\n\n// Load global strings.\nprefetchStrings('mod_data', ['mapping:dialogtitle:usepreset']);\n\nconst selectors = {\n selectPreset: '[data-action=\"selectpreset\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n registerEventListeners();\n};\n\n/**\n * Register events.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const preset = event.target.closest(selectors.selectPreset);\n if (preset) {\n event.preventDefault();\n showMappingDialogue(preset);\n }\n });\n};\n\n/**\n * Show the confirmation modal for uploading a preset.\n *\n * @param {HTMLElement} usepreset the preset to import.\n */\nconst showMappingDialogue = (usepreset) => {\n const presetName = usepreset.dataset.presetname;\n const cmId = usepreset.dataset.cmid;\n\n getMappingInformation(cmId, presetName).then((result) => {\n if (result.data && result.data.needsmapping) {\n buildModal({\n title: getString('mapping:dialogtitle:usepreset', 'mod_data', result.data.presetname),\n body: Templates.render('mod_data/fields_mapping_body', result.data),\n footer: Templates.render('mod_data/fields_mapping_footer', getMappingButtons(cmId, presetName)),\n large: true,\n });\n } else {\n window.location.href = Url.relativeUrl(\n 'mod/data/field.php',\n {\n id: cmId,\n mode: 'usepreset',\n fullname: presetName,\n },\n false\n );\n }\n return true;\n }).catch(Notification.exception);\n};\n\n/**\n * Given an object we want to build a modal ready to show\n *\n * @method buildModal\n * @param {Object} params the modal params\n * @param {Promise} params.title\n * @param {Promise} params.body\n * @param {Promise} params.footer\n * @return {Object} The modal ready to display immediately and render body in later.\n */\nconst buildModal = (params) => {\n return ModalFactory.create({\n ...params,\n type: ModalFactory.types.DEFAULT,\n }).then(modal => {\n modal.show();\n modal.showFooter();\n modal.registerCloseOnCancel();\n return modal;\n }).catch(Notification.exception);\n};\n\n/**\n * Add buttons to render on mapping modal.\n *\n * @param {int} cmId The id of the current database activity.\n * @param {string} presetName The preset name to delete.\n * @return {array} Same data with buttons.\n */\nconst getMappingButtons = (cmId, presetName) => {\n const data = {};\n\n data.mapfieldsbutton = Url.relativeUrl(\n 'mod/data/field.php',\n {\n id: cmId,\n fullname: presetName,\n mode: 'usepreset',\n action: 'select',\n },\n false\n );\n\n data.applybutton = Url.relativeUrl(\n 'mod/data/field.php',\n {\n id: cmId,\n fullname: presetName,\n mode: 'usepreset',\n action: 'notmapping'\n },\n false\n );\n\n return data;\n};\n\n/**\n * Check whether we should show the mapping dialogue or not.\n *\n * @param {int} cmId The id of the current database activity.\n * @param {string} presetName The preset name to delete.\n * @return {promise} Resolved with the result and warnings of deleting a preset.\n */\nconst getMappingInformation = (cmId, presetName) => {\n const request = {\n methodname: 'mod_data_get_mapping_information',\n args: {\n cmid: cmId,\n importedpreset: presetName,\n }\n };\n return Ajax.call([request])[0];\n};\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","preset","target","closest","preventDefault","showMappingDialogue","usepreset","presetName","dataset","presetname","cmId","cmid","getMappingInformation","then","result","data","needsmapping","buildModal","title","body","Templates","render","footer","getMappingButtons","large","window","location","href","Url","relativeUrl","id","mode","fullname","catch","Notification","exception","params","ModalFactory","create","type","types","DEFAULT","modal","show","showFooter","registerCloseOnCancel","mapfieldsbutton","action","applybutton","request","methodname","args","importedpreset","Ajax","call"],"mappings":";;;;;;;8UAgCgB,WAAY,CAAC,wCAEvBA,uBACY,6CAME,KAChBC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,OAASD,MAAME,OAAOC,QAAQP,wBAChCK,SACAD,MAAMI,iBACNC,oBAAoBJ,aAU1BI,oBAAuBC,kBACnBC,WAAaD,UAAUE,QAAQC,WAC/BC,KAAOJ,UAAUE,QAAQG,KAE/BC,sBAAsBF,KAAMH,YAAYM,MAAMC,SACtCA,OAAOC,MAAQD,OAAOC,KAAKC,aAC3BC,WAAW,CACPC,OAAO,kBAAU,gCAAiC,WAAYJ,OAAOC,KAAKN,YAC1EU,KAAMC,mBAAUC,OAAO,+BAAgCP,OAAOC,MAC9DO,OAAQF,mBAAUC,OAAO,iCAAkCE,kBAAkBb,KAAMH,aACnFiB,OAAO,IAGXC,OAAOC,SAASC,KAAOC,aAAIC,YACvB,qBACA,CACIC,GAAIpB,KACJqB,KAAM,YACNC,SAAUzB,aAEd,IAGD,KACR0B,MAAMC,sBAAaC,YAapBlB,WAAcmB,QACTC,uBAAaC,OAAO,IACpBF,OACHG,KAAMF,uBAAaG,MAAMC,UAC1B5B,MAAK6B,QACJA,MAAMC,OACND,MAAME,aACNF,MAAMG,wBACCH,SACRT,MAAMC,sBAAaC,WAUpBZ,kBAAoB,CAACb,KAAMH,oBACvBQ,KAAO,UAEbA,KAAK+B,gBAAkBlB,aAAIC,YACvB,qBACA,CACIC,GAAIpB,KACJsB,SAAUzB,WACVwB,KAAM,YACNgB,OAAQ,WAEZ,GAGJhC,KAAKiC,YAAcpB,aAAIC,YACnB,qBACA,CACIC,GAAIpB,KACJsB,SAAUzB,WACVwB,KAAM,YACNgB,OAAQ,eAEZ,GAGGhC,MAULH,sBAAwB,CAACF,KAAMH,oBAC3B0C,QAAU,CACZC,WAAY,mCACZC,KAAM,CACFxC,KAAMD,KACN0C,eAAgB7C,oBAGjB8C,cAAKC,KAAK,CAACL,UAAU"} \ No newline at end of file diff --git a/mod/data/amd/build/importpresets.min.js b/mod/data/amd/build/importpresets.min.js index 52ec0f77bc7..839b6d1589d 100644 --- a/mod/data/amd/build/importpresets.min.js +++ b/mod/data/amd/build/importpresets.min.js @@ -5,6 +5,6 @@ define("mod_data/importpresets",["exports","core_form/modalform","core/notificat * @module mod_data/importpresets * @copyright 2022 Laurent David * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_importPresetButton='[data-action="importpresets"]';_exports.init=()=>{document.addEventListener("click",(event=>{const importPresetButton=event.target.closest(selectors_importPresetButton);if(!importPresetButton)return;event.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.get_string)("importpreset","mod_data")},formClass:"mod_data\\form\\import_presets",args:{cmid:importPresetButton.dataset.dataid},saveButtonText:(0,_str.get_string)("importandapply","mod_data")});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.assign(event.detail.url):_notification.default.addNotification({type:"error",message:event.detail.errors.join("
")})})),modalForm.show()}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_importPresetButton='[data-action="importpresets"]';_exports.init=()=>{document.addEventListener("click",(event=>{const importPresetButton=event.target.closest(selectors_importPresetButton);if(!importPresetButton)return;event.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("importpreset","mod_data")},formClass:"mod_data\\form\\import_presets",args:{cmid:importPresetButton.dataset.dataid},saveButtonText:(0,_str.getString)("importandapply","mod_data")});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.assign(event.detail.url):_notification.default.addNotification({type:"error",message:event.detail.errors.join("
")})})),modalForm.show()}))}})); //# sourceMappingURL=importpresets.min.js.map \ No newline at end of file diff --git a/mod/data/amd/build/importpresets.min.js.map b/mod/data/amd/build/importpresets.min.js.map index 01c94d2db23..fccc6e39c5a 100644 --- a/mod/data/amd/build/importpresets.min.js.map +++ b/mod/data/amd/build/importpresets.min.js.map @@ -1 +1 @@ -{"version":3,"file":"importpresets.min.js","sources":["../src/importpresets.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 * Javascript module for importing presets.\n *\n * @module mod_data/importpresets\n * @copyright 2022 Laurent David \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\n\nconst selectors = {\n importPresetButton: '[data-action=\"importpresets\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n document.addEventListener('click', (event) => {\n const importPresetButton = event.target.closest(selectors.importPresetButton);\n\n if (!importPresetButton) {\n return;\n }\n\n event.preventDefault();\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('importpreset', 'mod_data'),\n },\n formClass: 'mod_data\\\\form\\\\import_presets',\n args: {cmid: importPresetButton.dataset.dataid},\n saveButtonText: getString('importandapply', 'mod_data'),\n });\n\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.assign(event.detail.url);\n } else {\n Notification.addNotification({\n type: 'error',\n message: event.detail.errors.join('
')\n });\n }\n });\n modalForm.show();\n });\n};\n"],"names":["selectors","document","addEventListener","event","importPresetButton","target","closest","preventDefault","modalForm","ModalForm","modalConfig","title","formClass","args","cmid","dataset","dataid","saveButtonText","events","FORM_SUBMITTED","detail","result","window","location","assign","url","addNotification","type","message","errors","join","show"],"mappings":";;;;;;;0LA2BMA,6BACkB,8CAMJ,KAChBC,SAASC,iBAAiB,SAAUC,cAC1BC,mBAAqBD,MAAME,OAAOC,QAAQN,kCAE3CI,0BAILD,MAAMI,uBACAC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,mBAAU,eAAgB,aAErCC,UAAW,iCACXC,KAAM,CAACC,KAAMV,mBAAmBW,QAAQC,QACxCC,gBAAgB,mBAAU,iBAAkB,cAGhDT,UAAUN,iBAAiBM,UAAUU,OAAOC,gBAAgBhB,QACpDA,MAAMiB,OAAOC,OACbC,OAAOC,SAASC,OAAOrB,MAAMiB,OAAOK,2BAEvBC,gBAAgB,CACzBC,KAAM,QACNC,QAASzB,MAAMiB,OAAOS,OAAOC,KAAK,aAI9CtB,UAAUuB"} \ No newline at end of file +{"version":3,"file":"importpresets.min.js","sources":["../src/importpresets.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 * Javascript module for importing presets.\n *\n * @module mod_data/importpresets\n * @copyright 2022 Laurent David \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {getString} from 'core/str';\n\nconst selectors = {\n importPresetButton: '[data-action=\"importpresets\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n document.addEventListener('click', (event) => {\n const importPresetButton = event.target.closest(selectors.importPresetButton);\n\n if (!importPresetButton) {\n return;\n }\n\n event.preventDefault();\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('importpreset', 'mod_data'),\n },\n formClass: 'mod_data\\\\form\\\\import_presets',\n args: {cmid: importPresetButton.dataset.dataid},\n saveButtonText: getString('importandapply', 'mod_data'),\n });\n\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.assign(event.detail.url);\n } else {\n Notification.addNotification({\n type: 'error',\n message: event.detail.errors.join('
')\n });\n }\n });\n modalForm.show();\n });\n};\n"],"names":["selectors","document","addEventListener","event","importPresetButton","target","closest","preventDefault","modalForm","ModalForm","modalConfig","title","formClass","args","cmid","dataset","dataid","saveButtonText","events","FORM_SUBMITTED","detail","result","window","location","assign","url","addNotification","type","message","errors","join","show"],"mappings":";;;;;;;0LA2BMA,6BACkB,8CAMJ,KAChBC,SAASC,iBAAiB,SAAUC,cAC1BC,mBAAqBD,MAAME,OAAOC,QAAQN,kCAE3CI,0BAILD,MAAMI,uBACAC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,eAAgB,aAErCC,UAAW,iCACXC,KAAM,CAACC,KAAMV,mBAAmBW,QAAQC,QACxCC,gBAAgB,kBAAU,iBAAkB,cAGhDT,UAAUN,iBAAiBM,UAAUU,OAAOC,gBAAgBhB,QACpDA,MAAMiB,OAAOC,OACbC,OAAOC,SAASC,OAAOrB,MAAMiB,OAAOK,2BAEvBC,gBAAgB,CACzBC,KAAM,QACNC,QAASzB,MAAMiB,OAAOS,OAAOC,KAAK,aAI9CtB,UAAUuB"} \ No newline at end of file diff --git a/mod/data/amd/build/resetalltemplates.min.js b/mod/data/amd/build/resetalltemplates.min.js index 5727092bbaf..0ec39a89cff 100644 --- a/mod/data/amd/build/resetalltemplates.min.js +++ b/mod/data/amd/build/resetalltemplates.min.js @@ -5,6 +5,6 @@ define("mod_data/resetalltemplates",["exports","core/notification","core/prefetc * @module mod_data/resetalltemplates * @copyright 2022 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};const selectors_resetAllTemplatesAction='[data-action="resetalltemplates"]';_exports.init=()=>{(0,_prefetch.prefetchStrings)("mod_data",["resetalltemplatesconfirmtitle","resetalltemplatesconfirm"]),(0,_prefetch.prefetchStrings)("core",["reset"]),registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const actionLink=event.target.closest(selectors_resetAllTemplatesAction);actionLink&&(event.preventDefault(),resetAllTemplatesConfirm(actionLink))}))},resetAllTemplatesConfirm=async actionLink=>{try{await _notification.default.saveCancelPromise((0,_str.get_string)("resetalltemplatesconfirmtitle","mod_data"),(0,_str.get_string)("resetalltemplatesconfirm","mod_data"),(0,_str.get_string)("reset","core")),window.location=actionLink.href}catch(error){return}}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};const selectors_resetAllTemplatesAction='[data-action="resetalltemplates"]';_exports.init=()=>{(0,_prefetch.prefetchStrings)("mod_data",["resetalltemplatesconfirmtitle","resetalltemplatesconfirm"]),(0,_prefetch.prefetchStrings)("core",["reset"]),registerEventListeners()};const registerEventListeners=()=>{document.addEventListener("click",(event=>{const actionLink=event.target.closest(selectors_resetAllTemplatesAction);actionLink&&(event.preventDefault(),resetAllTemplatesConfirm(actionLink))}))},resetAllTemplatesConfirm=async actionLink=>{try{await _notification.default.saveCancelPromise((0,_str.getString)("resetalltemplatesconfirmtitle","mod_data"),(0,_str.getString)("resetalltemplatesconfirm","mod_data"),(0,_str.getString)("reset","core")),window.location=actionLink.href}catch(error){return}}})); //# sourceMappingURL=resetalltemplates.min.js.map \ No newline at end of file diff --git a/mod/data/amd/build/resetalltemplates.min.js.map b/mod/data/amd/build/resetalltemplates.min.js.map index dc98ab5c36f..d7e80031404 100644 --- a/mod/data/amd/build/resetalltemplates.min.js.map +++ b/mod/data/amd/build/resetalltemplates.min.js.map @@ -1 +1 @@ -{"version":3,"file":"resetalltemplates.min.js","sources":["../src/resetalltemplates.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 * Javascript module for reseting all templates.\n *\n * @module mod_data/resetalltemplates\n * @copyright 2022 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\n\nconst selectors = {\n resetAllTemplatesAction: '[data-action=\"resetalltemplates\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n prefetchStrings('mod_data', [\n 'resetalltemplatesconfirmtitle',\n 'resetalltemplatesconfirm',\n ]);\n prefetchStrings('core', [\n 'reset',\n ]);\n registerEventListeners();\n};\n\n/**\n * Register events for option in action menu.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const actionLink = event.target.closest(selectors.resetAllTemplatesAction);\n if (actionLink) {\n event.preventDefault();\n resetAllTemplatesConfirm(actionLink);\n }\n });\n};\n\n/**\n * Show the confirmation modal to reset all the templates.\n *\n * @param {HTMLElement} actionLink the element that triggers the action.\n */\nconst resetAllTemplatesConfirm = async(actionLink) => {\n try {\n await Notification.saveCancelPromise(\n getString('resetalltemplatesconfirmtitle', 'mod_data'),\n getString('resetalltemplatesconfirm', 'mod_data'),\n getString('reset', 'core'),\n );\n window.location = actionLink.href;\n } catch (error) {\n return;\n }\n};\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","actionLink","target","closest","preventDefault","resetAllTemplatesConfirm","async","Notification","saveCancelPromise","window","location","href","error"],"mappings":";;;;;;;4JA2BMA,kCACuB,kDAMT,mCACA,WAAY,CACxB,gCACA,2DAEY,OAAQ,CACpB,UAEJC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,WAAaD,MAAME,OAAOC,QAAQP,mCACpCK,aACAD,MAAMI,iBACNC,yBAAyBJ,iBAU/BI,yBAA2BC,MAAAA,uBAEnBC,sBAAaC,mBACf,mBAAU,gCAAiC,aAC3C,mBAAU,2BAA4B,aACtC,mBAAU,QAAS,SAEvBC,OAAOC,SAAWT,WAAWU,KAC/B,MAAOC"} \ No newline at end of file +{"version":3,"file":"resetalltemplates.min.js","sources":["../src/resetalltemplates.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 * Javascript module for reseting all templates.\n *\n * @module mod_data/resetalltemplates\n * @copyright 2022 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Notification from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\n\nconst selectors = {\n resetAllTemplatesAction: '[data-action=\"resetalltemplates\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n prefetchStrings('mod_data', [\n 'resetalltemplatesconfirmtitle',\n 'resetalltemplatesconfirm',\n ]);\n prefetchStrings('core', [\n 'reset',\n ]);\n registerEventListeners();\n};\n\n/**\n * Register events for option in action menu.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', (event) => {\n const actionLink = event.target.closest(selectors.resetAllTemplatesAction);\n if (actionLink) {\n event.preventDefault();\n resetAllTemplatesConfirm(actionLink);\n }\n });\n};\n\n/**\n * Show the confirmation modal to reset all the templates.\n *\n * @param {HTMLElement} actionLink the element that triggers the action.\n */\nconst resetAllTemplatesConfirm = async(actionLink) => {\n try {\n await Notification.saveCancelPromise(\n getString('resetalltemplatesconfirmtitle', 'mod_data'),\n getString('resetalltemplatesconfirm', 'mod_data'),\n getString('reset', 'core'),\n );\n window.location = actionLink.href;\n } catch (error) {\n return;\n }\n};\n"],"names":["selectors","registerEventListeners","document","addEventListener","event","actionLink","target","closest","preventDefault","resetAllTemplatesConfirm","async","Notification","saveCancelPromise","window","location","href","error"],"mappings":";;;;;;;4JA2BMA,kCACuB,kDAMT,mCACA,WAAY,CACxB,gCACA,2DAEY,OAAQ,CACpB,UAEJC,gCAMEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,cAC1BC,WAAaD,MAAME,OAAOC,QAAQP,mCACpCK,aACAD,MAAMI,iBACNC,yBAAyBJ,iBAU/BI,yBAA2BC,MAAAA,uBAEnBC,sBAAaC,mBACf,kBAAU,gCAAiC,aAC3C,kBAAU,2BAA4B,aACtC,kBAAU,QAAS,SAEvBC,OAAOC,SAAWT,WAAWU,KAC/B,MAAOC"} \ No newline at end of file diff --git a/mod/data/amd/build/saveaspreset.min.js b/mod/data/amd/build/saveaspreset.min.js index 5ca264d3673..fe283a64f6c 100644 --- a/mod/data/amd/build/saveaspreset.min.js +++ b/mod/data/amd/build/saveaspreset.min.js @@ -5,6 +5,6 @@ define("mod_data/saveaspreset",["exports","core_form/modalform","core/notificati * @module mod_data/saveaspreset * @copyright 2021 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_saveAsPresetButton='[data-action="saveaspreset"]';_exports.init=()=>{document.addEventListener("click",(event=>{const saveAsPresetButton=event.target.closest(selectors_saveAsPresetButton);if(!saveAsPresetButton)return;event.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.get_string)("savedataaspreset","mod_data")},formClass:"mod_data\\form\\save_as_preset",args:{d:saveAsPresetButton.dataset.dataid},saveButtonText:(0,_str.get_string)("save"),returnFocus:saveAsPresetButton});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.reload():_notification.default.addNotification({type:"error",message:event.detail.errors.join("
")})})),modalForm.show()}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_saveAsPresetButton='[data-action="saveaspreset"]';_exports.init=()=>{document.addEventListener("click",(event=>{const saveAsPresetButton=event.target.closest(selectors_saveAsPresetButton);if(!saveAsPresetButton)return;event.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("savedataaspreset","mod_data")},formClass:"mod_data\\form\\save_as_preset",args:{d:saveAsPresetButton.dataset.dataid},saveButtonText:(0,_str.getString)("save"),returnFocus:saveAsPresetButton});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.reload():_notification.default.addNotification({type:"error",message:event.detail.errors.join("
")})})),modalForm.show()}))}})); //# sourceMappingURL=saveaspreset.min.js.map \ No newline at end of file diff --git a/mod/data/amd/build/saveaspreset.min.js.map b/mod/data/amd/build/saveaspreset.min.js.map index 51f597941ee..e94b1137ce1 100644 --- a/mod/data/amd/build/saveaspreset.min.js.map +++ b/mod/data/amd/build/saveaspreset.min.js.map @@ -1 +1 @@ -{"version":3,"file":"saveaspreset.min.js","sources":["../src/saveaspreset.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 * Javascript module for saving a database as a preset.\n *\n * @module mod_data/saveaspreset\n * @copyright 2021 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\n\nconst selectors = {\n saveAsPresetButton: '[data-action=\"saveaspreset\"]',\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n\n document.addEventListener('click', (event) => {\n const saveAsPresetButton = event.target.closest(selectors.saveAsPresetButton);\n\n if (!saveAsPresetButton) {\n return;\n }\n\n event.preventDefault();\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('savedataaspreset', 'mod_data'),\n },\n formClass: 'mod_data\\\\form\\\\save_as_preset',\n args: {d: saveAsPresetButton.dataset.dataid},\n saveButtonText: getString('save'),\n returnFocus: saveAsPresetButton,\n });\n\n // Show a toast notification when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.reload();\n } else {\n Notification.addNotification({\n type: 'error',\n message: event.detail.errors.join('
')\n });\n }\n });\n\n modalForm.show();\n });\n};\n"],"names":["selectors","document","addEventListener","event","saveAsPresetButton","target","closest","preventDefault","modalForm","ModalForm","modalConfig","title","formClass","args","d","dataset","dataid","saveButtonText","returnFocus","events","FORM_SUBMITTED","detail","result","window","location","reload","addNotification","type","message","errors","join","show"],"mappings":";;;;;;;0LA2BMA,6BACkB,6CAMJ,KAEhBC,SAASC,iBAAiB,SAAUC,cAC1BC,mBAAqBD,MAAME,OAAOC,QAAQN,kCAE3CI,0BAILD,MAAMI,uBACAC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,mBAAU,mBAAoB,aAEzCC,UAAW,iCACXC,KAAM,CAACC,EAAGV,mBAAmBW,QAAQC,QACrCC,gBAAgB,mBAAU,QAC1BC,YAAad,qBAIjBI,UAAUN,iBAAiBM,UAAUW,OAAOC,gBAAgBjB,QACpDA,MAAMkB,OAAOC,OACbC,OAAOC,SAASC,+BAEHC,gBAAgB,CACzBC,KAAM,QACNC,QAAUzB,MAAMkB,OAAOQ,OAAOC,KAAK,aAK/CtB,UAAUuB"} \ No newline at end of file +{"version":3,"file":"saveaspreset.min.js","sources":["../src/saveaspreset.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 * Javascript module for saving a database as a preset.\n *\n * @module mod_data/saveaspreset\n * @copyright 2021 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {getString} from 'core/str';\n\nconst selectors = {\n saveAsPresetButton: '[data-action=\"saveaspreset\"]',\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n\n document.addEventListener('click', (event) => {\n const saveAsPresetButton = event.target.closest(selectors.saveAsPresetButton);\n\n if (!saveAsPresetButton) {\n return;\n }\n\n event.preventDefault();\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('savedataaspreset', 'mod_data'),\n },\n formClass: 'mod_data\\\\form\\\\save_as_preset',\n args: {d: saveAsPresetButton.dataset.dataid},\n saveButtonText: getString('save'),\n returnFocus: saveAsPresetButton,\n });\n\n // Show a toast notification when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.reload();\n } else {\n Notification.addNotification({\n type: 'error',\n message: event.detail.errors.join('
')\n });\n }\n });\n\n modalForm.show();\n });\n};\n"],"names":["selectors","document","addEventListener","event","saveAsPresetButton","target","closest","preventDefault","modalForm","ModalForm","modalConfig","title","formClass","args","d","dataset","dataid","saveButtonText","returnFocus","events","FORM_SUBMITTED","detail","result","window","location","reload","addNotification","type","message","errors","join","show"],"mappings":";;;;;;;0LA2BMA,6BACkB,6CAMJ,KAEhBC,SAASC,iBAAiB,SAAUC,cAC1BC,mBAAqBD,MAAME,OAAOC,QAAQN,kCAE3CI,0BAILD,MAAMI,uBACAC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,mBAAoB,aAEzCC,UAAW,iCACXC,KAAM,CAACC,EAAGV,mBAAmBW,QAAQC,QACrCC,gBAAgB,kBAAU,QAC1BC,YAAad,qBAIjBI,UAAUN,iBAAiBM,UAAUW,OAAOC,gBAAgBjB,QACpDA,MAAMkB,OAAOC,OACbC,OAAOC,SAASC,+BAEHC,gBAAgB,CACzBC,KAAM,QACNC,QAAUzB,MAAMkB,OAAOQ,OAAOC,KAAK,aAK/CtB,UAAUuB"} \ No newline at end of file diff --git a/mod/data/amd/build/templateseditor.min.js b/mod/data/amd/build/templateseditor.min.js index 1e795ad25a2..a6b4a31a64f 100644 --- a/mod/data/amd/build/templateseditor.min.js +++ b/mod/data/amd/build/templateseditor.min.js @@ -5,6 +5,6 @@ define("mod_data/templateseditor",["exports","core/str","core/prefetch","core/ur * @module mod_data/templateseditor * @copyright 2021 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj},(0,_prefetch.prefetchStrings)("admin",["confirmation"]),(0,_prefetch.prefetchStrings)("mod_data",["resettemplateconfirmtitle","enabletemplateeditorcheck","editorenable"]),(0,_prefetch.prefetchStrings)("core",["reset"]);const selectors_toggleTemplateEditor='input[name="useeditor"]',selectors_resetTemplateAction='[data-action="resettemplate"]',selectors_resetTemplate='input[name="defaultform"]',selectors_resetAllTemplates='input[name="resetall"]',selectors_resetAllCheck='input[name="resetallcheck"]',selectors_editForm="#edittemplateform",registerResetButton=mode=>{const editForm=document.querySelector(selectors_editForm),resetTemplate=document.querySelector(selectors_resetTemplate),resetAllTemplates=document.querySelector(selectors_resetAllTemplates),resetTemplateAction=document.querySelector(selectors_resetTemplateAction);resetTemplateAction&&resetTemplate&&editForm&&((0,_prefetch.prefetchStrings)("mod_data",[mode]),resetTemplateAction.addEventListener("click",(async event=>{event.preventDefault();const params={resetallname:"resetallcheck",templatename:await(0,_str.get_string)(mode,"mod_data")};(0,_notification.saveCancel)((0,_str.get_string)("resettemplateconfirmtitle","mod_data"),_templates.default.render("mod_data/template_editor_resetmodal",params),(0,_str.get_string)("reset","core"),(()=>{resetTemplate.value="true",editForm.submit()}),null,{triggerElement:event.target})})),resetAllTemplates&&document.addEventListener("change",(event=>{event.target.matches(selectors_resetAllCheck)&&(resetAllTemplates.value=event.target.checked?"true":"")})))},registerEditorToggler=(instanceId,mode)=>{const toggleTemplateEditor=document.querySelector(selectors_toggleTemplateEditor);toggleTemplateEditor&&toggleTemplateEditor.addEventListener("click",(async event=>{event.preventDefault();event.target.checked?(0,_notification.saveCancel)((0,_str.get_string)("confirmation","admin"),(0,_str.get_string)("enabletemplateeditorcheck","mod_data"),(0,_str.get_string)("editorenable","mod_data"),(()=>{window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:instanceId,mode:mode,useeditor:!0})}),null,{triggerElement:event.target}):window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:instanceId,mode:mode,useeditor:!1})}))};_exports.init=(instanceId,mode)=>{((instanceId,mode)=>{registerResetButton(mode),registerEditorToggler(instanceId,mode)})(instanceId,mode)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj},(0,_prefetch.prefetchStrings)("admin",["confirmation"]),(0,_prefetch.prefetchStrings)("mod_data",["resettemplateconfirmtitle","enabletemplateeditorcheck","editorenable"]),(0,_prefetch.prefetchStrings)("core",["reset"]);const selectors_toggleTemplateEditor='input[name="useeditor"]',selectors_resetTemplateAction='[data-action="resettemplate"]',selectors_resetTemplate='input[name="defaultform"]',selectors_resetAllTemplates='input[name="resetall"]',selectors_resetAllCheck='input[name="resetallcheck"]',selectors_editForm="#edittemplateform",registerResetButton=mode=>{const editForm=document.querySelector(selectors_editForm),resetTemplate=document.querySelector(selectors_resetTemplate),resetAllTemplates=document.querySelector(selectors_resetAllTemplates),resetTemplateAction=document.querySelector(selectors_resetTemplateAction);resetTemplateAction&&resetTemplate&&editForm&&((0,_prefetch.prefetchStrings)("mod_data",[mode]),resetTemplateAction.addEventListener("click",(async event=>{event.preventDefault();const params={resetallname:"resetallcheck",templatename:await(0,_str.getString)(mode,"mod_data")};(0,_notification.saveCancel)((0,_str.getString)("resettemplateconfirmtitle","mod_data"),_templates.default.render("mod_data/template_editor_resetmodal",params),(0,_str.getString)("reset","core"),(()=>{resetTemplate.value="true",editForm.submit()}),null,{triggerElement:event.target})})),resetAllTemplates&&document.addEventListener("change",(event=>{event.target.matches(selectors_resetAllCheck)&&(resetAllTemplates.value=event.target.checked?"true":"")})))},registerEditorToggler=(instanceId,mode)=>{const toggleTemplateEditor=document.querySelector(selectors_toggleTemplateEditor);toggleTemplateEditor&&toggleTemplateEditor.addEventListener("click",(async event=>{event.preventDefault();event.target.checked?(0,_notification.saveCancel)((0,_str.getString)("confirmation","admin"),(0,_str.getString)("enabletemplateeditorcheck","mod_data"),(0,_str.getString)("editorenable","mod_data"),(()=>{window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:instanceId,mode:mode,useeditor:!0})}),null,{triggerElement:event.target}):window.location=(0,_url.relativeUrl)("/mod/data/templates.php",{d:instanceId,mode:mode,useeditor:!1})}))};_exports.init=(instanceId,mode)=>{((instanceId,mode)=>{registerResetButton(mode),registerEditorToggler(instanceId,mode)})(instanceId,mode)}})); //# sourceMappingURL=templateseditor.min.js.map \ No newline at end of file diff --git a/mod/data/amd/build/templateseditor.min.js.map b/mod/data/amd/build/templateseditor.min.js.map index 685a9ecd459..a798b239780 100644 --- a/mod/data/amd/build/templateseditor.min.js.map +++ b/mod/data/amd/build/templateseditor.min.js.map @@ -1 +1 @@ -{"version":3,"file":"templateseditor.min.js","sources":["../src/templateseditor.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 * Javascript module to control the template editor.\n *\n * @module mod_data/templateseditor\n * @copyright 2021 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString} from 'core/str';\nimport {prefetchStrings} from 'core/prefetch';\nimport {relativeUrl} from 'core/url';\nimport {saveCancel} from 'core/notification';\nimport Templates from 'core/templates';\n\nprefetchStrings('admin', ['confirmation']);\nprefetchStrings('mod_data', [\n 'resettemplateconfirmtitle',\n 'enabletemplateeditorcheck',\n 'editorenable'\n]);\nprefetchStrings('core', [\n 'reset',\n]);\n\n/**\n * Template editor constants.\n */\nconst selectors = {\n toggleTemplateEditor: 'input[name=\"useeditor\"]',\n resetTemplateAction: '[data-action=\"resettemplate\"]',\n resetTemplate: 'input[name=\"defaultform\"]',\n resetAllTemplates: 'input[name=\"resetall\"]',\n resetAllCheck: 'input[name=\"resetallcheck\"]',\n editForm: '#edittemplateform',\n};\n\n/**\n * Register event listeners for the module.\n *\n * @param {Number} instanceId The database ID\n * @param {string} mode The template mode\n */\nconst registerEventListeners = (instanceId, mode) => {\n registerResetButton(mode);\n registerEditorToggler(instanceId, mode);\n};\n\nconst registerResetButton = (mode) => {\n const editForm = document.querySelector(selectors.editForm);\n const resetTemplate = document.querySelector(selectors.resetTemplate);\n const resetAllTemplates = document.querySelector(selectors.resetAllTemplates);\n const resetTemplateAction = document.querySelector(selectors.resetTemplateAction);\n\n if (!resetTemplateAction || !resetTemplate || !editForm) {\n return;\n }\n prefetchStrings('mod_data', [\n mode\n ]);\n resetTemplateAction.addEventListener('click', async(event) => {\n event.preventDefault();\n const params = {\n resetallname: \"resetallcheck\",\n templatename: await getString(mode, 'mod_data'),\n };\n saveCancel(\n getString('resettemplateconfirmtitle', 'mod_data'),\n Templates.render('mod_data/template_editor_resetmodal', params),\n getString('reset', 'core'),\n () => {\n resetTemplate.value = \"true\";\n editForm.submit();\n },\n null,\n {triggerElement: event.target}\n );\n });\n\n // The reset all checkbox is inside a modal so we need to capture at document level.\n if (!resetAllTemplates) {\n return;\n }\n document.addEventListener('change', (event) => {\n if (event.target.matches(selectors.resetAllCheck)) {\n resetAllTemplates.value = (event.target.checked) ? \"true\" : \"\";\n }\n });\n};\n\nconst registerEditorToggler = (instanceId, mode) => {\n const toggleTemplateEditor = document.querySelector(selectors.toggleTemplateEditor);\n\n if (!toggleTemplateEditor) {\n return;\n }\n\n toggleTemplateEditor.addEventListener('click', async(event) => {\n event.preventDefault();\n // Whether the event action attempts to enable or disable the template editor.\n const enableTemplateEditor = event.target.checked;\n\n if (enableTemplateEditor) {\n // Display a confirmation dialog before enabling the template editor.\n saveCancel(\n getString('confirmation', 'admin'),\n getString('enabletemplateeditorcheck', 'mod_data'),\n getString('editorenable', 'mod_data'),\n () => {\n window.location = relativeUrl('/mod/data/templates.php', {d: instanceId, mode: mode, useeditor: true});\n },\n null,\n {triggerElement: event.target}\n );\n } else {\n window.location = relativeUrl('/mod/data/templates.php', {d: instanceId, mode: mode, useeditor: false});\n }\n });\n};\n\n/**\n * Initialize the module.\n *\n * @param {int} instanceId The database ID\n * @param {string} mode The template mode\n */\nexport const init = (instanceId, mode) => {\n registerEventListeners(instanceId, mode);\n};\n"],"names":["selectors","registerResetButton","mode","editForm","document","querySelector","resetTemplate","resetAllTemplates","resetTemplateAction","addEventListener","async","event","preventDefault","params","resetallname","templatename","Templates","render","value","submit","triggerElement","target","matches","checked","registerEditorToggler","instanceId","toggleTemplateEditor","window","location","d","useeditor","registerEventListeners"],"mappings":";;;;;;;8KA6BgB,QAAS,CAAC,+CACV,WAAY,CACxB,4BACA,4BACA,+CAEY,OAAQ,CACpB,gBAMEA,+BACoB,0BADpBA,8BAEmB,gCAFnBA,wBAGa,4BAHbA,4BAIiB,yBAJjBA,wBAKa,8BALbA,mBAMQ,oBAcRC,oBAAuBC,aACnBC,SAAWC,SAASC,cAAcL,oBAClCM,cAAgBF,SAASC,cAAcL,yBACvCO,kBAAoBH,SAASC,cAAcL,6BAC3CQ,oBAAsBJ,SAASC,cAAcL,+BAE9CQ,qBAAwBF,eAAkBH,yCAG/B,WAAY,CACxBD,OAEJM,oBAAoBC,iBAAiB,SAASC,MAAAA,QAC1CC,MAAMC,uBACAC,OAAS,CACXC,aAAc,gBACdC,mBAAoB,mBAAUb,KAAM,2CAGpC,mBAAU,4BAA6B,YACvCc,mBAAUC,OAAO,sCAAuCJ,SACxD,mBAAU,QAAS,SACnB,KACIP,cAAcY,MAAQ,OACtBf,SAASgB,WAEb,KACA,CAACC,eAAgBT,MAAMU,YAK1Bd,mBAGLH,SAASK,iBAAiB,UAAWE,QAC7BA,MAAMU,OAAOC,QAAQtB,2BACrBO,kBAAkBW,MAASP,MAAMU,OAAOE,QAAW,OAAS,SAKlEC,sBAAwB,CAACC,WAAYvB,cACjCwB,qBAAuBtB,SAASC,cAAcL,gCAE/C0B,sBAILA,qBAAqBjB,iBAAiB,SAASC,MAAAA,QAC3CC,MAAMC,iBAEuBD,MAAMU,OAAOE,sCAKlC,mBAAU,eAAgB,UAC1B,mBAAU,4BAA6B,aACvC,mBAAU,eAAgB,aAC1B,KACII,OAAOC,UAAW,oBAAY,0BAA2B,CAACC,EAAGJ,WAAYvB,KAAMA,KAAM4B,WAAW,MAEpG,KACA,CAACV,eAAgBT,MAAMU,SAG3BM,OAAOC,UAAW,oBAAY,0BAA2B,CAACC,EAAGJ,WAAYvB,KAAMA,KAAM4B,WAAW,sBAWxF,CAACL,WAAYvB,QAnFF,EAACuB,WAAYvB,QACxCD,oBAAoBC,MACpBsB,sBAAsBC,WAAYvB,OAkFlC6B,CAAuBN,WAAYvB"} \ No newline at end of file +{"version":3,"file":"templateseditor.min.js","sources":["../src/templateseditor.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 * Javascript module to control the template editor.\n *\n * @module mod_data/templateseditor\n * @copyright 2021 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getString} from 'core/str';\nimport {prefetchStrings} from 'core/prefetch';\nimport {relativeUrl} from 'core/url';\nimport {saveCancel} from 'core/notification';\nimport Templates from 'core/templates';\n\nprefetchStrings('admin', ['confirmation']);\nprefetchStrings('mod_data', [\n 'resettemplateconfirmtitle',\n 'enabletemplateeditorcheck',\n 'editorenable'\n]);\nprefetchStrings('core', [\n 'reset',\n]);\n\n/**\n * Template editor constants.\n */\nconst selectors = {\n toggleTemplateEditor: 'input[name=\"useeditor\"]',\n resetTemplateAction: '[data-action=\"resettemplate\"]',\n resetTemplate: 'input[name=\"defaultform\"]',\n resetAllTemplates: 'input[name=\"resetall\"]',\n resetAllCheck: 'input[name=\"resetallcheck\"]',\n editForm: '#edittemplateform',\n};\n\n/**\n * Register event listeners for the module.\n *\n * @param {Number} instanceId The database ID\n * @param {string} mode The template mode\n */\nconst registerEventListeners = (instanceId, mode) => {\n registerResetButton(mode);\n registerEditorToggler(instanceId, mode);\n};\n\nconst registerResetButton = (mode) => {\n const editForm = document.querySelector(selectors.editForm);\n const resetTemplate = document.querySelector(selectors.resetTemplate);\n const resetAllTemplates = document.querySelector(selectors.resetAllTemplates);\n const resetTemplateAction = document.querySelector(selectors.resetTemplateAction);\n\n if (!resetTemplateAction || !resetTemplate || !editForm) {\n return;\n }\n prefetchStrings('mod_data', [\n mode\n ]);\n resetTemplateAction.addEventListener('click', async(event) => {\n event.preventDefault();\n const params = {\n resetallname: \"resetallcheck\",\n templatename: await getString(mode, 'mod_data'),\n };\n saveCancel(\n getString('resettemplateconfirmtitle', 'mod_data'),\n Templates.render('mod_data/template_editor_resetmodal', params),\n getString('reset', 'core'),\n () => {\n resetTemplate.value = \"true\";\n editForm.submit();\n },\n null,\n {triggerElement: event.target}\n );\n });\n\n // The reset all checkbox is inside a modal so we need to capture at document level.\n if (!resetAllTemplates) {\n return;\n }\n document.addEventListener('change', (event) => {\n if (event.target.matches(selectors.resetAllCheck)) {\n resetAllTemplates.value = (event.target.checked) ? \"true\" : \"\";\n }\n });\n};\n\nconst registerEditorToggler = (instanceId, mode) => {\n const toggleTemplateEditor = document.querySelector(selectors.toggleTemplateEditor);\n\n if (!toggleTemplateEditor) {\n return;\n }\n\n toggleTemplateEditor.addEventListener('click', async(event) => {\n event.preventDefault();\n // Whether the event action attempts to enable or disable the template editor.\n const enableTemplateEditor = event.target.checked;\n\n if (enableTemplateEditor) {\n // Display a confirmation dialog before enabling the template editor.\n saveCancel(\n getString('confirmation', 'admin'),\n getString('enabletemplateeditorcheck', 'mod_data'),\n getString('editorenable', 'mod_data'),\n () => {\n window.location = relativeUrl('/mod/data/templates.php', {d: instanceId, mode: mode, useeditor: true});\n },\n null,\n {triggerElement: event.target}\n );\n } else {\n window.location = relativeUrl('/mod/data/templates.php', {d: instanceId, mode: mode, useeditor: false});\n }\n });\n};\n\n/**\n * Initialize the module.\n *\n * @param {int} instanceId The database ID\n * @param {string} mode The template mode\n */\nexport const init = (instanceId, mode) => {\n registerEventListeners(instanceId, mode);\n};\n"],"names":["selectors","registerResetButton","mode","editForm","document","querySelector","resetTemplate","resetAllTemplates","resetTemplateAction","addEventListener","async","event","preventDefault","params","resetallname","templatename","Templates","render","value","submit","triggerElement","target","matches","checked","registerEditorToggler","instanceId","toggleTemplateEditor","window","location","d","useeditor","registerEventListeners"],"mappings":";;;;;;;8KA6BgB,QAAS,CAAC,+CACV,WAAY,CACxB,4BACA,4BACA,+CAEY,OAAQ,CACpB,gBAMEA,+BACoB,0BADpBA,8BAEmB,gCAFnBA,wBAGa,4BAHbA,4BAIiB,yBAJjBA,wBAKa,8BALbA,mBAMQ,oBAcRC,oBAAuBC,aACnBC,SAAWC,SAASC,cAAcL,oBAClCM,cAAgBF,SAASC,cAAcL,yBACvCO,kBAAoBH,SAASC,cAAcL,6BAC3CQ,oBAAsBJ,SAASC,cAAcL,+BAE9CQ,qBAAwBF,eAAkBH,yCAG/B,WAAY,CACxBD,OAEJM,oBAAoBC,iBAAiB,SAASC,MAAAA,QAC1CC,MAAMC,uBACAC,OAAS,CACXC,aAAc,gBACdC,mBAAoB,kBAAUb,KAAM,2CAGpC,kBAAU,4BAA6B,YACvCc,mBAAUC,OAAO,sCAAuCJ,SACxD,kBAAU,QAAS,SACnB,KACIP,cAAcY,MAAQ,OACtBf,SAASgB,WAEb,KACA,CAACC,eAAgBT,MAAMU,YAK1Bd,mBAGLH,SAASK,iBAAiB,UAAWE,QAC7BA,MAAMU,OAAOC,QAAQtB,2BACrBO,kBAAkBW,MAASP,MAAMU,OAAOE,QAAW,OAAS,SAKlEC,sBAAwB,CAACC,WAAYvB,cACjCwB,qBAAuBtB,SAASC,cAAcL,gCAE/C0B,sBAILA,qBAAqBjB,iBAAiB,SAASC,MAAAA,QAC3CC,MAAMC,iBAEuBD,MAAMU,OAAOE,sCAKlC,kBAAU,eAAgB,UAC1B,kBAAU,4BAA6B,aACvC,kBAAU,eAAgB,aAC1B,KACII,OAAOC,UAAW,oBAAY,0BAA2B,CAACC,EAAGJ,WAAYvB,KAAMA,KAAM4B,WAAW,MAEpG,KACA,CAACV,eAAgBT,MAAMU,SAG3BM,OAAOC,UAAW,oBAAY,0BAA2B,CAACC,EAAGJ,WAAYvB,KAAMA,KAAM4B,WAAW,sBAWxF,CAACL,WAAYvB,QAnFF,EAACuB,WAAYvB,QACxCD,oBAAoBC,MACpBsB,sBAAsBC,WAAYvB,OAkFlC6B,CAAuBN,WAAYvB"} \ No newline at end of file diff --git a/mod/data/amd/src/deletepreset.js b/mod/data/amd/src/deletepreset.js index 5a70db6f5e0..4ec281a0950 100644 --- a/mod/data/amd/src/deletepreset.js +++ b/mod/data/amd/src/deletepreset.js @@ -23,7 +23,7 @@ import Notification from 'core/notification'; import {prefetchStrings} from 'core/prefetch'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Ajax from 'core/ajax'; import Url from 'core/url'; diff --git a/mod/data/amd/src/editpreset.js b/mod/data/amd/src/editpreset.js index b873f91f641..8ca49129cdb 100644 --- a/mod/data/amd/src/editpreset.js +++ b/mod/data/amd/src/editpreset.js @@ -23,7 +23,7 @@ import ModalForm from 'core_form/modalform'; import Notification from 'core/notification'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; const selectors = { editPresetButton: '[data-action="editpreset"]', diff --git a/mod/data/amd/src/importmappingdialogue.js b/mod/data/amd/src/importmappingdialogue.js index bf6d18bfbc3..9f8ea867734 100644 --- a/mod/data/amd/src/importmappingdialogue.js +++ b/mod/data/amd/src/importmappingdialogue.js @@ -27,7 +27,7 @@ import Url from 'core/url'; import Templates from 'core/templates'; import ModalFactory from 'core/modal_factory'; import {prefetchStrings} from 'core/prefetch'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; // Load global strings. prefetchStrings('mod_data', ['mapping:dialogtitle:usepreset']); diff --git a/mod/data/amd/src/importpresets.js b/mod/data/amd/src/importpresets.js index 6d5716128bb..eec317600b2 100644 --- a/mod/data/amd/src/importpresets.js +++ b/mod/data/amd/src/importpresets.js @@ -23,7 +23,7 @@ import ModalForm from 'core_form/modalform'; import Notification from 'core/notification'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; const selectors = { importPresetButton: '[data-action="importpresets"]', diff --git a/mod/data/amd/src/resetalltemplates.js b/mod/data/amd/src/resetalltemplates.js index 8b3c5daabc7..386754b7872 100644 --- a/mod/data/amd/src/resetalltemplates.js +++ b/mod/data/amd/src/resetalltemplates.js @@ -23,7 +23,7 @@ import Notification from 'core/notification'; import {prefetchStrings} from 'core/prefetch'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; const selectors = { resetAllTemplatesAction: '[data-action="resetalltemplates"]', diff --git a/mod/data/amd/src/saveaspreset.js b/mod/data/amd/src/saveaspreset.js index 642eb276753..e6859f522d9 100644 --- a/mod/data/amd/src/saveaspreset.js +++ b/mod/data/amd/src/saveaspreset.js @@ -23,7 +23,7 @@ import ModalForm from 'core_form/modalform'; import Notification from 'core/notification'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; const selectors = { saveAsPresetButton: '[data-action="saveaspreset"]', diff --git a/mod/data/amd/src/templateseditor.js b/mod/data/amd/src/templateseditor.js index ddb9ddc160d..eab9c448d1c 100644 --- a/mod/data/amd/src/templateseditor.js +++ b/mod/data/amd/src/templateseditor.js @@ -21,7 +21,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {prefetchStrings} from 'core/prefetch'; import {relativeUrl} from 'core/url'; import {saveCancel} from 'core/notification'; diff --git a/mod/feedback/amd/build/createtemplate.min.js b/mod/feedback/amd/build/createtemplate.min.js index 707d7f89389..e29f9a9abbb 100644 --- a/mod/feedback/amd/build/createtemplate.min.js +++ b/mod/feedback/amd/build/createtemplate.min.js @@ -5,6 +5,6 @@ define("mod_feedback/createtemplate",["exports","core_form/modalform","core/noti * @module mod_feedback/createtemplate * @copyright 2021 Peter Dias * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_modaltrigger='[data-action="createtemplate"]';_exports.init=()=>{document.querySelector(selectors_modaltrigger).addEventListener("click",(event=>{event.preventDefault();const ele=event.currentTarget,modalForm=new _modalform.default({modalConfig:{title:(0,_str.get_string)("save_as_new_template","mod_feedback")},formClass:"mod_feedback\\form\\create_template_form",args:{id:ele.dataset.dataid},saveButtonText:(0,_str.get_string)("save","core")});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?(0,_str.get_string)("template_saved","feedback").then(_toast.add).catch():(0,_str.get_string)("saving_failed","feedback").then((string=>_notification.default.addNotification({type:"error",message:string}))).catch()})),modalForm.show()}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_modaltrigger='[data-action="createtemplate"]';_exports.init=()=>{document.querySelector(selectors_modaltrigger).addEventListener("click",(event=>{event.preventDefault();const ele=event.currentTarget,modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("save_as_new_template","mod_feedback")},formClass:"mod_feedback\\form\\create_template_form",args:{id:ele.dataset.dataid},saveButtonText:(0,_str.getString)("save","core")});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?(0,_str.getString)("template_saved","feedback").then(_toast.add).catch():(0,_str.getString)("saving_failed","feedback").then((string=>_notification.default.addNotification({type:"error",message:string}))).catch()})),modalForm.show()}))}})); //# sourceMappingURL=createtemplate.min.js.map \ No newline at end of file diff --git a/mod/feedback/amd/build/createtemplate.min.js.map b/mod/feedback/amd/build/createtemplate.min.js.map index 3b17269a452..eb556974ef4 100644 --- a/mod/feedback/amd/build/createtemplate.min.js.map +++ b/mod/feedback/amd/build/createtemplate.min.js.map @@ -1 +1 @@ -{"version":3,"file":"createtemplate.min.js","sources":["../src/createtemplate.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 * Javascript module for saving a new template.\n *\n * @module mod_feedback/createtemplate\n * @copyright 2021 Peter Dias\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\n\nconst selectors = {\n modaltrigger: '[data-action=\"createtemplate\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n const trigger = document.querySelector(selectors.modaltrigger);\n\n trigger.addEventListener('click', event => {\n event.preventDefault();\n const ele = event.currentTarget;\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('save_as_new_template', 'mod_feedback'),\n },\n formClass: 'mod_feedback\\\\form\\\\create_template_form',\n args: {\n id: ele.dataset.dataid\n },\n saveButtonText: getString('save', 'core')\n });\n\n // Show a toast notification when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n getString('template_saved', 'feedback').then(addToast).catch();\n } else {\n getString('saving_failed', 'feedback').then(string => {\n return Notification.addNotification({\n type: 'error',\n message: string\n });\n }).catch();\n }\n });\n\n modalForm.show();\n });\n};\n"],"names":["selectors","document","querySelector","addEventListener","event","preventDefault","ele","currentTarget","modalForm","ModalForm","modalConfig","title","formClass","args","id","dataset","dataid","saveButtonText","events","FORM_SUBMITTED","detail","result","then","addToast","catch","string","Notification","addNotification","type","message","show"],"mappings":";;;;;;;0LA4BMA,uBACY,+CAME,KACAC,SAASC,cAAcF,wBAE/BG,iBAAiB,SAASC,QAC9BA,MAAMC,uBACAC,IAAMF,MAAMG,cAEZC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,mBAAU,uBAAwB,iBAE7CC,UAAW,2CACXC,KAAM,CACFC,GAAIR,IAAIS,QAAQC,QAEpBC,gBAAgB,mBAAU,OAAQ,UAItCT,UAAUL,iBAAiBK,UAAUU,OAAOC,gBAAgBf,QACpDA,MAAMgB,OAAOC,2BACH,iBAAkB,YAAYC,KAAKC,YAAUC,4BAE7C,gBAAiB,YAAYF,MAAKG,QACjCC,sBAAaC,gBAAgB,CAChCC,KAAM,QACNC,QAASJ,WAEdD,WAIXhB,UAAUsB"} \ No newline at end of file +{"version":3,"file":"createtemplate.min.js","sources":["../src/createtemplate.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 * Javascript module for saving a new template.\n *\n * @module mod_feedback/createtemplate\n * @copyright 2021 Peter Dias\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\n\nconst selectors = {\n modaltrigger: '[data-action=\"createtemplate\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n const trigger = document.querySelector(selectors.modaltrigger);\n\n trigger.addEventListener('click', event => {\n event.preventDefault();\n const ele = event.currentTarget;\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('save_as_new_template', 'mod_feedback'),\n },\n formClass: 'mod_feedback\\\\form\\\\create_template_form',\n args: {\n id: ele.dataset.dataid\n },\n saveButtonText: getString('save', 'core')\n });\n\n // Show a toast notification when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n getString('template_saved', 'feedback').then(addToast).catch();\n } else {\n getString('saving_failed', 'feedback').then(string => {\n return Notification.addNotification({\n type: 'error',\n message: string\n });\n }).catch();\n }\n });\n\n modalForm.show();\n });\n};\n"],"names":["selectors","document","querySelector","addEventListener","event","preventDefault","ele","currentTarget","modalForm","ModalForm","modalConfig","title","formClass","args","id","dataset","dataid","saveButtonText","events","FORM_SUBMITTED","detail","result","then","addToast","catch","string","Notification","addNotification","type","message","show"],"mappings":";;;;;;;0LA4BMA,uBACY,+CAME,KACAC,SAASC,cAAcF,wBAE/BG,iBAAiB,SAASC,QAC9BA,MAAMC,uBACAC,IAAMF,MAAMG,cAEZC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,uBAAwB,iBAE7CC,UAAW,2CACXC,KAAM,CACFC,GAAIR,IAAIS,QAAQC,QAEpBC,gBAAgB,kBAAU,OAAQ,UAItCT,UAAUL,iBAAiBK,UAAUU,OAAOC,gBAAgBf,QACpDA,MAAMgB,OAAOC,0BACH,iBAAkB,YAAYC,KAAKC,YAAUC,2BAE7C,gBAAiB,YAAYF,MAAKG,QACjCC,sBAAaC,gBAAgB,CAChCC,KAAM,QACNC,QAASJ,WAEdD,WAIXhB,UAAUsB"} \ No newline at end of file diff --git a/mod/feedback/amd/build/usetemplate.min.js b/mod/feedback/amd/build/usetemplate.min.js index 2f79aef6c73..622370d3ab1 100644 --- a/mod/feedback/amd/build/usetemplate.min.js +++ b/mod/feedback/amd/build/usetemplate.min.js @@ -5,6 +5,6 @@ define("mod_feedback/usetemplate",["exports","core_form/modalform","core/notific * @module mod_feedback/usetemplate * @copyright 2021 Peter Dias * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_modaltrigger='[data-action="usetemplate"]';_exports.init=()=>{const trigger=document.querySelector(selectors_modaltrigger);trigger.addEventListener("click",(event=>{event.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.get_string)("use_this_template","mod_feedback")},formClass:"mod_feedback\\form\\use_template_form",args:{id:trigger.getAttribute("data-dataid"),templateid:trigger.getAttribute("data-templateid")},saveButtonText:(0,_str.get_string)("save","core")});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.assign(event.detail.url):_notification.default.addNotification({type:"error",message:(0,_str.get_string)("saving_failed","mod_feedback")})})),modalForm.show()}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=_interopRequireDefault(_modalform),_notification=_interopRequireDefault(_notification);const selectors_modaltrigger='[data-action="usetemplate"]';_exports.init=()=>{const trigger=document.querySelector(selectors_modaltrigger);trigger.addEventListener("click",(event=>{event.preventDefault();const modalForm=new _modalform.default({modalConfig:{title:(0,_str.getString)("use_this_template","mod_feedback")},formClass:"mod_feedback\\form\\use_template_form",args:{id:trigger.getAttribute("data-dataid"),templateid:trigger.getAttribute("data-templateid")},saveButtonText:(0,_str.getString)("save","core")});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(event=>{event.detail.result?window.location.assign(event.detail.url):_notification.default.addNotification({type:"error",message:(0,_str.getString)("saving_failed","mod_feedback")})})),modalForm.show()}))}})); //# sourceMappingURL=usetemplate.min.js.map \ No newline at end of file diff --git a/mod/feedback/amd/build/usetemplate.min.js.map b/mod/feedback/amd/build/usetemplate.min.js.map index 83899f836b9..da2f9b224bd 100644 --- a/mod/feedback/amd/build/usetemplate.min.js.map +++ b/mod/feedback/amd/build/usetemplate.min.js.map @@ -1 +1 @@ -{"version":3,"file":"usetemplate.min.js","sources":["../src/usetemplate.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 * Javascript module for using an existing template\n *\n * @module mod_feedback/usetemplate\n * @copyright 2021 Peter Dias\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\n\nconst selectors = {\n modaltrigger: '[data-action=\"usetemplate\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n const trigger = document.querySelector(selectors.modaltrigger);\n\n trigger.addEventListener('click', event => {\n event.preventDefault();\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('use_this_template', 'mod_feedback'),\n },\n formClass: 'mod_feedback\\\\form\\\\use_template_form',\n args: {\n id: trigger.getAttribute('data-dataid'),\n templateid: trigger.getAttribute('data-templateid')\n },\n saveButtonText: getString('save', 'core')\n });\n\n // Show a toast notification when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.assign(event.detail.url);\n } else {\n Notification.addNotification({\n type: 'error',\n message: getString('saving_failed', 'mod_feedback')\n });\n }\n });\n\n modalForm.show();\n });\n};\n"],"names":["selectors","trigger","document","querySelector","addEventListener","event","preventDefault","modalForm","ModalForm","modalConfig","title","formClass","args","id","getAttribute","templateid","saveButtonText","events","FORM_SUBMITTED","detail","result","window","location","assign","url","addNotification","type","message","show"],"mappings":";;;;;;;0LA2BMA,uBACY,4CAME,WACVC,QAAUC,SAASC,cAAcH,wBAEvCC,QAAQG,iBAAiB,SAASC,QAC9BA,MAAMC,uBAEAC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,mBAAU,oBAAqB,iBAE1CC,UAAW,wCACXC,KAAM,CACFC,GAAIZ,QAAQa,aAAa,eACzBC,WAAYd,QAAQa,aAAa,oBAErCE,gBAAgB,mBAAU,OAAQ,UAItCT,UAAUH,iBAAiBG,UAAUU,OAAOC,gBAAgBb,QACpDA,MAAMc,OAAOC,OACbC,OAAOC,SAASC,OAAOlB,MAAMc,OAAOK,2BAEvBC,gBAAgB,CACzBC,KAAM,QACNC,SAAU,mBAAU,gBAAiB,qBAKjDpB,UAAUqB"} \ No newline at end of file +{"version":3,"file":"usetemplate.min.js","sources":["../src/usetemplate.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 * Javascript module for using an existing template\n *\n * @module mod_feedback/usetemplate\n * @copyright 2021 Peter Dias\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {getString} from 'core/str';\n\nconst selectors = {\n modaltrigger: '[data-action=\"usetemplate\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n const trigger = document.querySelector(selectors.modaltrigger);\n\n trigger.addEventListener('click', event => {\n event.preventDefault();\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('use_this_template', 'mod_feedback'),\n },\n formClass: 'mod_feedback\\\\form\\\\use_template_form',\n args: {\n id: trigger.getAttribute('data-dataid'),\n templateid: trigger.getAttribute('data-templateid')\n },\n saveButtonText: getString('save', 'core')\n });\n\n // Show a toast notification when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n window.location.assign(event.detail.url);\n } else {\n Notification.addNotification({\n type: 'error',\n message: getString('saving_failed', 'mod_feedback')\n });\n }\n });\n\n modalForm.show();\n });\n};\n"],"names":["selectors","trigger","document","querySelector","addEventListener","event","preventDefault","modalForm","ModalForm","modalConfig","title","formClass","args","id","getAttribute","templateid","saveButtonText","events","FORM_SUBMITTED","detail","result","window","location","assign","url","addNotification","type","message","show"],"mappings":";;;;;;;0LA2BMA,uBACY,4CAME,WACVC,QAAUC,SAASC,cAAcH,wBAEvCC,QAAQG,iBAAiB,SAASC,QAC9BA,MAAMC,uBAEAC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,kBAAU,oBAAqB,iBAE1CC,UAAW,wCACXC,KAAM,CACFC,GAAIZ,QAAQa,aAAa,eACzBC,WAAYd,QAAQa,aAAa,oBAErCE,gBAAgB,kBAAU,OAAQ,UAItCT,UAAUH,iBAAiBG,UAAUU,OAAOC,gBAAgBb,QACpDA,MAAMc,OAAOC,OACbC,OAAOC,SAASC,OAAOlB,MAAMc,OAAOK,2BAEvBC,gBAAgB,CACzBC,KAAM,QACNC,SAAU,kBAAU,gBAAiB,qBAKjDpB,UAAUqB"} \ No newline at end of file diff --git a/mod/feedback/amd/src/createtemplate.js b/mod/feedback/amd/src/createtemplate.js index 97d4c090a6a..5dbc6dba312 100644 --- a/mod/feedback/amd/src/createtemplate.js +++ b/mod/feedback/amd/src/createtemplate.js @@ -23,7 +23,7 @@ import ModalForm from 'core_form/modalform'; import Notification from 'core/notification'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {add as addToast} from 'core/toast'; const selectors = { diff --git a/mod/feedback/amd/src/usetemplate.js b/mod/feedback/amd/src/usetemplate.js index 57d28f3d942..507da7d8764 100644 --- a/mod/feedback/amd/src/usetemplate.js +++ b/mod/feedback/amd/src/usetemplate.js @@ -23,7 +23,7 @@ import ModalForm from 'core_form/modalform'; import Notification from 'core/notification'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; const selectors = { modaltrigger: '[data-action="usetemplate"]', diff --git a/mod/forum/amd/build/local/grades/grader.min.js b/mod/forum/amd/build/local/grades/grader.min.js index 2c5187cd457..9e30619908b 100644 --- a/mod/forum/amd/build/local/grades/grader.min.js +++ b/mod/forum/amd/build/local/grades/grader.min.js @@ -5,6 +5,6 @@ define("mod_forum/local/grades/grader",["exports","core/templates","./local/grad * @module mod_forum/local/grades/grader * @copyright 2019 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),Object.defineProperty(_exports,"getGradingPanelFunctions",{enumerable:!0,get:function(){return _gradingpanel.default}}),_exports.view=_exports.launch=void 0,_templates=_interopRequireDefault(_templates),_selectors=_interopRequireDefault(_selectors),_user_picker=_interopRequireDefault(_user_picker),_gradingpanel=_interopRequireDefault(_gradingpanel),Modal=_interopRequireWildcard(Modal),ModalEvents=_interopRequireWildcard(ModalEvents),_drawer_events=_interopRequireDefault(_drawer_events);const templateNames_grader={app:"mod_forum/local/grades/grader",gradingPanel:{error:"mod_forum/local/grades/local/grader/gradingpanel/error"},searchResults:"mod_forum/local/grades/local/grader/user_picker/user_search",status:"mod_forum/local/grades/local/grader/status"},displayUserPicker=(root,html)=>{const pickerRegion=root.querySelector(_selectors.default.regions.pickerRegion);_templates.default.replaceNodeContents(pickerRegion,html,"")},fetchContentFromRender=(html,js)=>[html,js],getUpdateUserContentFunction=(root,getContentForUser,getGradeForUser,saveGradeForUser)=>{let firstLoad=!0;return async user=>{const spinner=firstLoad?null:(0,_loadingicon.addIconToContainerWithPromise)(root),[[html,js],userGrade]=await Promise.all([getContentForUser(user.id).then(fetchContentFromRender),getGradeForUser(user.id)]);_templates.default.replaceNodeContents(root.querySelector(_selectors.default.regions.moduleReplace),html,js);const[gradingPanelHtml,gradingPanelJS]=await _templates.default.render(userGrade.templatename,userGrade.grade).then(fetchContentFromRender),panelContainer=root.querySelector(_selectors.default.regions.gradingPanelContainer),panel=panelContainer.querySelector(_selectors.default.regions.gradingPanel);_templates.default.replaceNodeContents(panel,gradingPanelHtml,gradingPanelJS);const form=panel.querySelector("form");return(0,_comparison.fillInitialValues)(form),form.addEventListener("submit",(event=>{saveGradeForUser(user),event.preventDefault()})),panelContainer.scrollTop=0,firstLoad=!1,spinner&&spinner.resolve(),userGrade}},hideSearchResultContainer=(bodyContainer,userPickerContainer,searchResultsContainer)=>{bodyContainer.classList.remove("hidden"),userPickerContainer.classList.remove("hidden"),searchResultsContainer.classList.add("hidden")},hideUserSearchInput=(toggleSearchButton,searchContainer,searchInput)=>{searchContainer.classList.add("collapsed"),toggleSearchButton.setAttribute("aria-expanded","false"),toggleSearchButton.classList.add("collapse"),toggleSearchButton.classList.remove("expand"),toggleSearchButton.focus();searchContainer.parentElement.querySelector(_selectors.default.regions.gradingInfoContainer).removeAttribute("aria-hidden");const collapseGradingDrawer=searchContainer.parentElement.querySelector(_selectors.default.buttons.collapseGradingDrawer);collapseGradingDrawer.removeAttribute("aria-hidden"),collapseGradingDrawer.setAttribute("tabindex","0"),searchInput.value=""},renderSearchResults=async(searchResultsContainer,users)=>{const{html:html,js:js}=await _templates.default.renderForPromise(templateNames_grader.searchResults,{users:users});_templates.default.replaceNodeContents(searchResultsContainer,html,js)},registerEventListeners=(graderLayout,userPicker,saveGradeFunction,userList)=>{const graderContainer=graderLayout.getContainer(),toggleSearchButton=graderContainer.querySelector(_selectors.default.buttons.toggleSearch),searchInputContainer=graderContainer.querySelector(_selectors.default.regions.userSearchContainer),searchInput=searchInputContainer.querySelector(_selectors.default.regions.userSearchInput),bodyContainer=graderContainer.querySelector(_selectors.default.regions.bodyContainer),userPickerContainer=graderContainer.querySelector(_selectors.default.regions.pickerRegion),searchResultsContainer=graderContainer.querySelector(_selectors.default.regions.searchResultsContainer);graderContainer.addEventListener("click",(e=>{if(e.target.closest(_selectors.default.buttons.toggleFullscreen))return e.stopImmediatePropagation(),e.preventDefault(),void graderLayout.toggleFullscreen();if(e.target.closest(_selectors.default.buttons.closeGrader))return e.stopImmediatePropagation(),e.preventDefault(),void graderLayout.close();if(e.target.closest(_selectors.default.buttons.saveGrade)&&saveGradeFunction(userPicker.currentUser),e.target.closest(_selectors.default.buttons.toggleSearch))return void("true"===toggleSearchButton.getAttribute("aria-expanded")?(hideUserSearchInput(toggleSearchButton,searchInputContainer,searchInput),hideSearchResultContainer(bodyContainer,userPickerContainer,searchResultsContainer),searchResultsContainer.innerHTML=""):(((toggleSearchButton,searchContainer,searchInput)=>{searchContainer.classList.remove("collapsed"),toggleSearchButton.setAttribute("aria-expanded","true"),toggleSearchButton.classList.add("expand"),toggleSearchButton.classList.remove("collapse"),searchContainer.parentElement.querySelector(_selectors.default.regions.gradingInfoContainer).setAttribute("aria-hidden","true");const collapseGradingDrawer=searchContainer.parentElement.querySelector(_selectors.default.buttons.collapseGradingDrawer);collapseGradingDrawer.setAttribute("aria-hidden","true"),collapseGradingDrawer.setAttribute("tabindex","-1"),searchInput.focus()})(toggleSearchButton,searchInputContainer,searchInput),((bodyContainer,userPickerContainer,searchResultsContainer)=>{bodyContainer.classList.add("hidden"),userPickerContainer.classList.add("hidden"),searchResultsContainer.classList.remove("hidden")})(bodyContainer,userPickerContainer,searchResultsContainer),renderSearchResults(searchResultsContainer,userList)));const selectUserButton=e.target.closest(_selectors.default.buttons.selectUser);if(selectUserButton){const userId=selectUserButton.getAttribute("data-userid"),user=userList.find((user=>user.id==userId));userPicker.setUserId(userId),userPicker.showUser(user),hideUserSearchInput(toggleSearchButton,searchInputContainer,searchInput),hideSearchResultContainer(bodyContainer,userPickerContainer,searchResultsContainer),searchResultsContainer.innerHTML=""}})),searchInput.addEventListener("input",(0,_utils.debounce)((()=>{const users=((userList,searchTerm)=>""===searchTerm?userList:(searchTerm=searchTerm.toLowerCase(),userList.filter((user=>user.fullname.toLowerCase().includes(searchTerm)))))(userList,searchInput.value);renderSearchResults(searchResultsContainer,users)}),300)),(0,_pubsub.subscribe)(_drawer_events.default.DRAWER_HIDDEN,(drawerRoot=>{drawerRoot[0].querySelector(_selectors.default.regions.gradingPanel)&&setContentContainerMargin(graderContainer,0)})),(0,_pubsub.subscribe)(_drawer_events.default.DRAWER_SHOWN,(drawerRoot=>{const gradingPanel=drawerRoot[0];gradingPanel.querySelector(_selectors.default.regions.gradingPanel)&&setContentContainerMargin(graderContainer,gradingPanel.offsetWidth)}))},setContentContainerMargin=(graderContainer,rightMargin)=>{const contentContainer=graderContainer.querySelector(_selectors.default.regions.moduleContainer);contentContainer&&(contentContainer.style.marginRight="".concat(rightMargin,"px"))},getSaveUserGradeFunction=(root,setGradeForUser)=>async user=>{try{root.querySelector(_selectors.default.regions.gradingPanelErrors).innerHTML="";const result=await setGradeForUser(user.id,root.querySelector(_selectors.default.values.sendStudentNotifications).value,root.querySelector(_selectors.default.regions.gradingPanel));return result.success&&(0,_toast.add)(await(0,_str.get_string)("grades:gradesavedfor","mod_forum",user)),result.failed&&displayGradingError(root,user,result.error),result}catch(err){return displayGradingError(root,user,err),(0,_normalise.failedUpdate)(err)}},displayGradingError=async(root,user,err)=>{const[{html:html,js:js},errorString]=await Promise.all([_templates.default.renderForPromise(templateNames_grader.gradingPanel.error,{error:err}),await(0,_str.get_string)("grades:gradesavefailed","mod_forum",{error:err.message,...user})]);_templates.default.replaceNodeContents(root.querySelector(_selectors.default.regions.gradingPanelErrors),html,js),(0,_toast.add)(errorString,{type:"warning"})};_exports.launch=async function(getListOfUsers,getContentForUser,getGradeForUser,setGradeForUser){let{initialUserId:initialUserId=null,moduleName:moduleName,courseName:courseName,courseUrl:courseUrl,sendStudentNotifications:sendStudentNotifications,focusOnClose:focusOnClose=null}=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{};const userList=await getListOfUsers();if(!userList.length)return void(0,_notification.addNotification)({message:await(0,_str.get_string)("nouserstograde","core_grades"),type:"error"});const[graderLayout,{html:html,js:js}]=await Promise.all([(0,_fullscreen.createLayout)({fullscreen:!1,showLoader:!1,focusOnClose:focusOnClose}),_templates.default.renderForPromise(templateNames_grader.app,{moduleName:moduleName,courseName:courseName,courseUrl:courseUrl,drawer:{show:!0},defaultsendnotifications:sendStudentNotifications})]),graderContainer=graderLayout.getContainer(),saveGradeFunction=getSaveUserGradeFunction(graderContainer,setGradeForUser);_templates.default.replaceNodeContents(graderContainer,html,js);const updateUserContent=getUpdateUserContentFunction(graderContainer,getContentForUser,getGradeForUser,saveGradeFunction),userIds=userList.map((user=>user.id)),statusContainer=graderContainer.querySelector(_selectors.default.regions.statusContainer),userPicker=await(0,_user_picker.default)(userList,(async user=>{const renderContext={status:(await updateUserContent(user)).hasgrade,index:userIds.indexOf(user.id)+1,total:userList.length};_templates.default.render(templateNames_grader.status,renderContext).then((html=>(statusContainer.innerHTML=html,html))).catch()}),saveGradeFunction,{initialUserId:initialUserId});registerEventListeners(graderLayout,userPicker,saveGradeFunction,userList),displayUserPicker(graderContainer,userPicker.rootNode)};_exports.view=async function(getGradeForUser,userid,moduleName){let{focusOnClose:focusOnClose=null}=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};const[userGrade,modal]=await Promise.all([getGradeForUser(userid),Modal.create({title:moduleName,large:!0,type:Modal.types.CANCEL})]),spinner=(0,_loadingicon.addIconToContainerWithPromise)(modal.getRoot());modal.getRoot().on(ModalEvents.hidden,(function(){if(modal.destroy(),focusOnClose)try{focusOnClose.focus()}catch(e){}})),modal.show();const output=document.createElement("div"),{html:html,js:js}=await _templates.default.renderForPromise("mod_forum/local/grades/view_grade",userGrade);_templates.default.replaceNodeContents(output,html,js);const[gradeHTML,gradeJS]=await renderGradeTemplate(userGrade),gradeReplace=output.querySelector('[data-region="grade-template"]');_templates.default.replaceNodeContents(gradeReplace,gradeHTML,gradeJS),modal.setBody(output.outerHTML),spinner.resolve()};const renderGradeTemplate=async userGrade=>{const{html:html,js:js}=await _templates.default.renderForPromise(userGrade.templatename,userGrade.grade);return[html,js]}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),Object.defineProperty(_exports,"getGradingPanelFunctions",{enumerable:!0,get:function(){return _gradingpanel.default}}),_exports.view=_exports.launch=void 0,_templates=_interopRequireDefault(_templates),_selectors=_interopRequireDefault(_selectors),_user_picker=_interopRequireDefault(_user_picker),_gradingpanel=_interopRequireDefault(_gradingpanel),Modal=_interopRequireWildcard(Modal),ModalEvents=_interopRequireWildcard(ModalEvents),_drawer_events=_interopRequireDefault(_drawer_events);const templateNames_grader={app:"mod_forum/local/grades/grader",gradingPanel:{error:"mod_forum/local/grades/local/grader/gradingpanel/error"},searchResults:"mod_forum/local/grades/local/grader/user_picker/user_search",status:"mod_forum/local/grades/local/grader/status"},displayUserPicker=(root,html)=>{const pickerRegion=root.querySelector(_selectors.default.regions.pickerRegion);_templates.default.replaceNodeContents(pickerRegion,html,"")},fetchContentFromRender=(html,js)=>[html,js],getUpdateUserContentFunction=(root,getContentForUser,getGradeForUser,saveGradeForUser)=>{let firstLoad=!0;return async user=>{const spinner=firstLoad?null:(0,_loadingicon.addIconToContainerWithPromise)(root),[[html,js],userGrade]=await Promise.all([getContentForUser(user.id).then(fetchContentFromRender),getGradeForUser(user.id)]);_templates.default.replaceNodeContents(root.querySelector(_selectors.default.regions.moduleReplace),html,js);const[gradingPanelHtml,gradingPanelJS]=await _templates.default.render(userGrade.templatename,userGrade.grade).then(fetchContentFromRender),panelContainer=root.querySelector(_selectors.default.regions.gradingPanelContainer),panel=panelContainer.querySelector(_selectors.default.regions.gradingPanel);_templates.default.replaceNodeContents(panel,gradingPanelHtml,gradingPanelJS);const form=panel.querySelector("form");return(0,_comparison.fillInitialValues)(form),form.addEventListener("submit",(event=>{saveGradeForUser(user),event.preventDefault()})),panelContainer.scrollTop=0,firstLoad=!1,spinner&&spinner.resolve(),userGrade}},hideSearchResultContainer=(bodyContainer,userPickerContainer,searchResultsContainer)=>{bodyContainer.classList.remove("hidden"),userPickerContainer.classList.remove("hidden"),searchResultsContainer.classList.add("hidden")},hideUserSearchInput=(toggleSearchButton,searchContainer,searchInput)=>{searchContainer.classList.add("collapsed"),toggleSearchButton.setAttribute("aria-expanded","false"),toggleSearchButton.classList.add("collapse"),toggleSearchButton.classList.remove("expand"),toggleSearchButton.focus();searchContainer.parentElement.querySelector(_selectors.default.regions.gradingInfoContainer).removeAttribute("aria-hidden");const collapseGradingDrawer=searchContainer.parentElement.querySelector(_selectors.default.buttons.collapseGradingDrawer);collapseGradingDrawer.removeAttribute("aria-hidden"),collapseGradingDrawer.setAttribute("tabindex","0"),searchInput.value=""},renderSearchResults=async(searchResultsContainer,users)=>{const{html:html,js:js}=await _templates.default.renderForPromise(templateNames_grader.searchResults,{users:users});_templates.default.replaceNodeContents(searchResultsContainer,html,js)},registerEventListeners=(graderLayout,userPicker,saveGradeFunction,userList)=>{const graderContainer=graderLayout.getContainer(),toggleSearchButton=graderContainer.querySelector(_selectors.default.buttons.toggleSearch),searchInputContainer=graderContainer.querySelector(_selectors.default.regions.userSearchContainer),searchInput=searchInputContainer.querySelector(_selectors.default.regions.userSearchInput),bodyContainer=graderContainer.querySelector(_selectors.default.regions.bodyContainer),userPickerContainer=graderContainer.querySelector(_selectors.default.regions.pickerRegion),searchResultsContainer=graderContainer.querySelector(_selectors.default.regions.searchResultsContainer);graderContainer.addEventListener("click",(e=>{if(e.target.closest(_selectors.default.buttons.toggleFullscreen))return e.stopImmediatePropagation(),e.preventDefault(),void graderLayout.toggleFullscreen();if(e.target.closest(_selectors.default.buttons.closeGrader))return e.stopImmediatePropagation(),e.preventDefault(),void graderLayout.close();if(e.target.closest(_selectors.default.buttons.saveGrade)&&saveGradeFunction(userPicker.currentUser),e.target.closest(_selectors.default.buttons.toggleSearch))return void("true"===toggleSearchButton.getAttribute("aria-expanded")?(hideUserSearchInput(toggleSearchButton,searchInputContainer,searchInput),hideSearchResultContainer(bodyContainer,userPickerContainer,searchResultsContainer),searchResultsContainer.innerHTML=""):(((toggleSearchButton,searchContainer,searchInput)=>{searchContainer.classList.remove("collapsed"),toggleSearchButton.setAttribute("aria-expanded","true"),toggleSearchButton.classList.add("expand"),toggleSearchButton.classList.remove("collapse"),searchContainer.parentElement.querySelector(_selectors.default.regions.gradingInfoContainer).setAttribute("aria-hidden","true");const collapseGradingDrawer=searchContainer.parentElement.querySelector(_selectors.default.buttons.collapseGradingDrawer);collapseGradingDrawer.setAttribute("aria-hidden","true"),collapseGradingDrawer.setAttribute("tabindex","-1"),searchInput.focus()})(toggleSearchButton,searchInputContainer,searchInput),((bodyContainer,userPickerContainer,searchResultsContainer)=>{bodyContainer.classList.add("hidden"),userPickerContainer.classList.add("hidden"),searchResultsContainer.classList.remove("hidden")})(bodyContainer,userPickerContainer,searchResultsContainer),renderSearchResults(searchResultsContainer,userList)));const selectUserButton=e.target.closest(_selectors.default.buttons.selectUser);if(selectUserButton){const userId=selectUserButton.getAttribute("data-userid"),user=userList.find((user=>user.id==userId));userPicker.setUserId(userId),userPicker.showUser(user),hideUserSearchInput(toggleSearchButton,searchInputContainer,searchInput),hideSearchResultContainer(bodyContainer,userPickerContainer,searchResultsContainer),searchResultsContainer.innerHTML=""}})),searchInput.addEventListener("input",(0,_utils.debounce)((()=>{const users=((userList,searchTerm)=>""===searchTerm?userList:(searchTerm=searchTerm.toLowerCase(),userList.filter((user=>user.fullname.toLowerCase().includes(searchTerm)))))(userList,searchInput.value);renderSearchResults(searchResultsContainer,users)}),300)),(0,_pubsub.subscribe)(_drawer_events.default.DRAWER_HIDDEN,(drawerRoot=>{drawerRoot[0].querySelector(_selectors.default.regions.gradingPanel)&&setContentContainerMargin(graderContainer,0)})),(0,_pubsub.subscribe)(_drawer_events.default.DRAWER_SHOWN,(drawerRoot=>{const gradingPanel=drawerRoot[0];gradingPanel.querySelector(_selectors.default.regions.gradingPanel)&&setContentContainerMargin(graderContainer,gradingPanel.offsetWidth)}))},setContentContainerMargin=(graderContainer,rightMargin)=>{const contentContainer=graderContainer.querySelector(_selectors.default.regions.moduleContainer);contentContainer&&(contentContainer.style.marginRight="".concat(rightMargin,"px"))},getSaveUserGradeFunction=(root,setGradeForUser)=>async user=>{try{root.querySelector(_selectors.default.regions.gradingPanelErrors).innerHTML="";const result=await setGradeForUser(user.id,root.querySelector(_selectors.default.values.sendStudentNotifications).value,root.querySelector(_selectors.default.regions.gradingPanel));return result.success&&(0,_toast.add)(await(0,_str.getString)("grades:gradesavedfor","mod_forum",user)),result.failed&&displayGradingError(root,user,result.error),result}catch(err){return displayGradingError(root,user,err),(0,_normalise.failedUpdate)(err)}},displayGradingError=async(root,user,err)=>{const[{html:html,js:js},errorString]=await Promise.all([_templates.default.renderForPromise(templateNames_grader.gradingPanel.error,{error:err}),await(0,_str.getString)("grades:gradesavefailed","mod_forum",{error:err.message,...user})]);_templates.default.replaceNodeContents(root.querySelector(_selectors.default.regions.gradingPanelErrors),html,js),(0,_toast.add)(errorString,{type:"warning"})};_exports.launch=async function(getListOfUsers,getContentForUser,getGradeForUser,setGradeForUser){let{initialUserId:initialUserId=null,moduleName:moduleName,courseName:courseName,courseUrl:courseUrl,sendStudentNotifications:sendStudentNotifications,focusOnClose:focusOnClose=null}=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{};const userList=await getListOfUsers();if(!userList.length)return void(0,_notification.addNotification)({message:await(0,_str.getString)("nouserstograde","core_grades"),type:"error"});const[graderLayout,{html:html,js:js}]=await Promise.all([(0,_fullscreen.createLayout)({fullscreen:!1,showLoader:!1,focusOnClose:focusOnClose}),_templates.default.renderForPromise(templateNames_grader.app,{moduleName:moduleName,courseName:courseName,courseUrl:courseUrl,drawer:{show:!0},defaultsendnotifications:sendStudentNotifications})]),graderContainer=graderLayout.getContainer(),saveGradeFunction=getSaveUserGradeFunction(graderContainer,setGradeForUser);_templates.default.replaceNodeContents(graderContainer,html,js);const updateUserContent=getUpdateUserContentFunction(graderContainer,getContentForUser,getGradeForUser,saveGradeFunction),userIds=userList.map((user=>user.id)),statusContainer=graderContainer.querySelector(_selectors.default.regions.statusContainer),userPicker=await(0,_user_picker.default)(userList,(async user=>{const renderContext={status:(await updateUserContent(user)).hasgrade,index:userIds.indexOf(user.id)+1,total:userList.length};_templates.default.render(templateNames_grader.status,renderContext).then((html=>(statusContainer.innerHTML=html,html))).catch()}),saveGradeFunction,{initialUserId:initialUserId});registerEventListeners(graderLayout,userPicker,saveGradeFunction,userList),displayUserPicker(graderContainer,userPicker.rootNode)};_exports.view=async function(getGradeForUser,userid,moduleName){let{focusOnClose:focusOnClose=null}=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};const[userGrade,modal]=await Promise.all([getGradeForUser(userid),Modal.create({title:moduleName,large:!0,type:Modal.types.CANCEL})]),spinner=(0,_loadingicon.addIconToContainerWithPromise)(modal.getRoot());modal.getRoot().on(ModalEvents.hidden,(function(){if(modal.destroy(),focusOnClose)try{focusOnClose.focus()}catch(e){}})),modal.show();const output=document.createElement("div"),{html:html,js:js}=await _templates.default.renderForPromise("mod_forum/local/grades/view_grade",userGrade);_templates.default.replaceNodeContents(output,html,js);const[gradeHTML,gradeJS]=await renderGradeTemplate(userGrade),gradeReplace=output.querySelector('[data-region="grade-template"]');_templates.default.replaceNodeContents(gradeReplace,gradeHTML,gradeJS),modal.setBody(output.outerHTML),spinner.resolve()};const renderGradeTemplate=async userGrade=>{const{html:html,js:js}=await _templates.default.renderForPromise(userGrade.templatename,userGrade.grade);return[html,js]}})); //# sourceMappingURL=grader.min.js.map \ No newline at end of file diff --git a/mod/forum/amd/build/local/grades/grader.min.js.map b/mod/forum/amd/build/local/grades/grader.min.js.map index 07af24e0758..c843c868353 100644 --- a/mod/forum/amd/build/local/grades/grader.min.js.map +++ b/mod/forum/amd/build/local/grades/grader.min.js.map @@ -1 +1 @@ -{"version":3,"file":"grader.min.js","sources":["../../../src/local/grades/grader.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module will tie together all of the different calls the gradable module will make.\n *\n * @module mod_forum/local/grades/grader\n * @copyright 2019 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Templates from 'core/templates';\nimport Selectors from './local/grader/selectors';\nimport getUserPicker from './local/grader/user_picker';\nimport {createLayout as createFullScreenWindow} from 'mod_forum/local/layout/fullscreen';\nimport getGradingPanelFunctions from './local/grader/gradingpanel';\nimport {add as addToast} from 'core/toast';\nimport {addNotification} from 'core/notification';\nimport {get_string as getString} from 'core/str';\nimport {failedUpdate} from 'core_grades/grades/grader/gradingpanel/normalise';\nimport {addIconToContainerWithPromise} from 'core/loadingicon';\nimport {debounce} from 'core/utils';\nimport {fillInitialValues} from 'core_grades/grades/grader/gradingpanel/comparison';\nimport * as Modal from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {subscribe} from 'core/pubsub';\nimport DrawerEvents from 'core/drawer_events';\n\nconst templateNames = {\n grader: {\n app: 'mod_forum/local/grades/grader',\n gradingPanel: {\n error: 'mod_forum/local/grades/local/grader/gradingpanel/error',\n },\n searchResults: 'mod_forum/local/grades/local/grader/user_picker/user_search',\n status: 'mod_forum/local/grades/local/grader/status',\n },\n};\n\n/**\n * Helper function that replaces the user picker placeholder with what we get back from the user picker class.\n *\n * @param {HTMLElement} root\n * @param {String} html\n */\nconst displayUserPicker = (root, html) => {\n const pickerRegion = root.querySelector(Selectors.regions.pickerRegion);\n Templates.replaceNodeContents(pickerRegion, html, '');\n};\n\n/**\n * To be removed, this is now done as a part of Templates.renderForPromise()\n *\n * @param {String} html\n * @param {String} js\n * @returns {array} An array containing the HTML, and JS.\n */\nconst fetchContentFromRender = (html, js) => {\n return [html, js];\n};\n\n/**\n * Here we build the function that is passed to the user picker that'll handle updating the user content area\n * of the grading interface.\n *\n * @param {HTMLElement} root\n * @param {Function} getContentForUser\n * @param {Function} getGradeForUser\n * @param {Function} saveGradeForUser\n * @return {Function}\n */\nconst getUpdateUserContentFunction = (root, getContentForUser, getGradeForUser, saveGradeForUser) => {\n let firstLoad = true;\n\n return async(user) => {\n const spinner = firstLoad ? null : addIconToContainerWithPromise(root);\n const [\n [html, js],\n userGrade,\n ] = await Promise.all([\n getContentForUser(user.id).then(fetchContentFromRender),\n getGradeForUser(user.id),\n ]);\n Templates.replaceNodeContents(root.querySelector(Selectors.regions.moduleReplace), html, js);\n\n const [\n gradingPanelHtml,\n gradingPanelJS\n ] = await Templates.render(userGrade.templatename, userGrade.grade).then(fetchContentFromRender);\n const panelContainer = root.querySelector(Selectors.regions.gradingPanelContainer);\n const panel = panelContainer.querySelector(Selectors.regions.gradingPanel);\n Templates.replaceNodeContents(panel, gradingPanelHtml, gradingPanelJS);\n\n const form = panel.querySelector('form');\n fillInitialValues(form);\n\n form.addEventListener('submit', event => {\n saveGradeForUser(user);\n event.preventDefault();\n });\n\n panelContainer.scrollTop = 0;\n firstLoad = false;\n\n if (spinner) {\n spinner.resolve();\n }\n return userGrade;\n };\n};\n\n/**\n * Show the search results container and hide the user picker and body content.\n *\n * @param {HTMLElement} bodyContainer The container element for the body content\n * @param {HTMLElement} userPickerContainer The container element for the user picker\n * @param {HTMLElement} searchResultsContainer The container element for the search results\n */\nconst showSearchResultContainer = (bodyContainer, userPickerContainer, searchResultsContainer) => {\n bodyContainer.classList.add('hidden');\n userPickerContainer.classList.add('hidden');\n searchResultsContainer.classList.remove('hidden');\n};\n\n/**\n * Hide the search results container and show the user picker and body content.\n *\n * @param {HTMLElement} bodyContainer The container element for the body content\n * @param {HTMLElement} userPickerContainer The container element for the user picker\n * @param {HTMLElement} searchResultsContainer The container element for the search results\n */\nconst hideSearchResultContainer = (bodyContainer, userPickerContainer, searchResultsContainer) => {\n bodyContainer.classList.remove('hidden');\n userPickerContainer.classList.remove('hidden');\n searchResultsContainer.classList.add('hidden');\n};\n\n/**\n * Toggles the visibility of the user search.\n *\n * @param {HTMLElement} toggleSearchButton The button that toggles the search\n * @param {HTMLElement} searchContainer The container element for the user search\n * @param {HTMLElement} searchInput The input element for searching\n */\nconst showUserSearchInput = (toggleSearchButton, searchContainer, searchInput) => {\n searchContainer.classList.remove('collapsed');\n toggleSearchButton.setAttribute('aria-expanded', 'true');\n toggleSearchButton.classList.add('expand');\n toggleSearchButton.classList.remove('collapse');\n\n // Hide the grading info container from screen reader.\n const gradingInfoContainer = searchContainer.parentElement.querySelector(Selectors.regions.gradingInfoContainer);\n gradingInfoContainer.setAttribute('aria-hidden', 'true');\n\n // Hide the collapse grading drawer button from screen reader.\n const collapseGradingDrawer = searchContainer.parentElement.querySelector(Selectors.buttons.collapseGradingDrawer);\n collapseGradingDrawer.setAttribute('aria-hidden', 'true');\n collapseGradingDrawer.setAttribute('tabindex', '-1');\n\n searchInput.focus();\n};\n\n/**\n * Toggles the visibility of the user search.\n *\n * @param {HTMLElement} toggleSearchButton The button that toggles the search\n * @param {HTMLElement} searchContainer The container element for the user search\n * @param {HTMLElement} searchInput The input element for searching\n */\nconst hideUserSearchInput = (toggleSearchButton, searchContainer, searchInput) => {\n searchContainer.classList.add('collapsed');\n toggleSearchButton.setAttribute('aria-expanded', 'false');\n toggleSearchButton.classList.add('collapse');\n toggleSearchButton.classList.remove('expand');\n toggleSearchButton.focus();\n\n // Show the grading info container to screen reader.\n const gradingInfoContainer = searchContainer.parentElement.querySelector(Selectors.regions.gradingInfoContainer);\n gradingInfoContainer.removeAttribute('aria-hidden');\n\n // Show the collapse grading drawer button from screen reader.\n const collapseGradingDrawer = searchContainer.parentElement.querySelector(Selectors.buttons.collapseGradingDrawer);\n collapseGradingDrawer.removeAttribute('aria-hidden');\n collapseGradingDrawer.setAttribute('tabindex', '0');\n\n searchInput.value = '';\n};\n\n/**\n * Find the list of users who's names include the given search term.\n *\n * @param {Array} userList List of users for the grader\n * @param {String} searchTerm The search term to match\n * @return {Array}\n */\nconst searchForUsers = (userList, searchTerm) => {\n if (searchTerm === '') {\n return userList;\n }\n\n searchTerm = searchTerm.toLowerCase();\n\n return userList.filter((user) => {\n return user.fullname.toLowerCase().includes(searchTerm);\n });\n};\n\n/**\n * Render the list of users in the search results area.\n *\n * @param {HTMLElement} searchResultsContainer The container element for search results\n * @param {Array} users The list of users to display\n */\nconst renderSearchResults = async(searchResultsContainer, users) => {\n const {html, js} = await Templates.renderForPromise(templateNames.grader.searchResults, {users});\n Templates.replaceNodeContents(searchResultsContainer, html, js);\n};\n\n/**\n * Add click handlers to the buttons in the header of the grading interface.\n *\n * @param {HTMLElement} graderLayout\n * @param {Object} userPicker\n * @param {Function} saveGradeFunction\n * @param {Array} userList List of users for the grader.\n */\nconst registerEventListeners = (graderLayout, userPicker, saveGradeFunction, userList) => {\n const graderContainer = graderLayout.getContainer();\n const toggleSearchButton = graderContainer.querySelector(Selectors.buttons.toggleSearch);\n const searchInputContainer = graderContainer.querySelector(Selectors.regions.userSearchContainer);\n const searchInput = searchInputContainer.querySelector(Selectors.regions.userSearchInput);\n const bodyContainer = graderContainer.querySelector(Selectors.regions.bodyContainer);\n const userPickerContainer = graderContainer.querySelector(Selectors.regions.pickerRegion);\n const searchResultsContainer = graderContainer.querySelector(Selectors.regions.searchResultsContainer);\n\n graderContainer.addEventListener('click', (e) => {\n if (e.target.closest(Selectors.buttons.toggleFullscreen)) {\n e.stopImmediatePropagation();\n e.preventDefault();\n graderLayout.toggleFullscreen();\n\n return;\n }\n\n if (e.target.closest(Selectors.buttons.closeGrader)) {\n e.stopImmediatePropagation();\n e.preventDefault();\n\n graderLayout.close();\n\n return;\n }\n\n if (e.target.closest(Selectors.buttons.saveGrade)) {\n saveGradeFunction(userPicker.currentUser);\n }\n\n if (e.target.closest(Selectors.buttons.toggleSearch)) {\n if (toggleSearchButton.getAttribute('aria-expanded') === 'true') {\n // Search is open so let's close it.\n hideUserSearchInput(toggleSearchButton, searchInputContainer, searchInput);\n hideSearchResultContainer(bodyContainer, userPickerContainer, searchResultsContainer);\n searchResultsContainer.innerHTML = '';\n } else {\n // Search is closed so let's open it.\n showUserSearchInput(toggleSearchButton, searchInputContainer, searchInput);\n showSearchResultContainer(bodyContainer, userPickerContainer, searchResultsContainer);\n renderSearchResults(searchResultsContainer, userList);\n }\n\n return;\n }\n\n const selectUserButton = e.target.closest(Selectors.buttons.selectUser);\n if (selectUserButton) {\n const userId = selectUserButton.getAttribute('data-userid');\n const user = userList.find(user => user.id == userId);\n userPicker.setUserId(userId);\n userPicker.showUser(user);\n hideUserSearchInput(toggleSearchButton, searchInputContainer, searchInput);\n hideSearchResultContainer(bodyContainer, userPickerContainer, searchResultsContainer);\n searchResultsContainer.innerHTML = '';\n }\n });\n\n // Debounce the search input so that it only executes 300 milliseconds after the user has finished typing.\n searchInput.addEventListener('input', debounce(() => {\n const users = searchForUsers(userList, searchInput.value);\n renderSearchResults(searchResultsContainer, users);\n }, 300));\n\n // Remove the right margin of the content container when the grading panel is hidden so that it expands to full-width.\n subscribe(DrawerEvents.DRAWER_HIDDEN, (drawerRoot) => {\n const gradingPanel = drawerRoot[0];\n if (gradingPanel.querySelector(Selectors.regions.gradingPanel)) {\n setContentContainerMargin(graderContainer, 0);\n }\n });\n\n // Bring back the right margin of the content container when the grading panel is shown to give space for the grading panel.\n subscribe(DrawerEvents.DRAWER_SHOWN, (drawerRoot) => {\n const gradingPanel = drawerRoot[0];\n if (gradingPanel.querySelector(Selectors.regions.gradingPanel)) {\n setContentContainerMargin(graderContainer, gradingPanel.offsetWidth);\n }\n });\n};\n\n/**\n * Adjusts the right margin of the content container.\n *\n * @param {HTMLElement} graderContainer The container for the grader app.\n * @param {Number} rightMargin The right margin value.\n */\nconst setContentContainerMargin = (graderContainer, rightMargin) => {\n const contentContainer = graderContainer.querySelector(Selectors.regions.moduleContainer);\n if (contentContainer) {\n contentContainer.style.marginRight = `${rightMargin}px`;\n }\n};\n\n/**\n * Get the function used to save a user grade.\n *\n * @param {HTMLElement} root The container for the grader\n * @param {Function} setGradeForUser The function that will be called.\n * @return {Function}\n */\nconst getSaveUserGradeFunction = (root, setGradeForUser) => {\n return async(user) => {\n try {\n root.querySelector(Selectors.regions.gradingPanelErrors).innerHTML = '';\n const result = await setGradeForUser(\n user.id,\n root.querySelector(Selectors.values.sendStudentNotifications).value,\n root.querySelector(Selectors.regions.gradingPanel)\n );\n if (result.success) {\n addToast(await getString('grades:gradesavedfor', 'mod_forum', user));\n }\n if (result.failed) {\n displayGradingError(root, user, result.error);\n }\n\n return result;\n } catch (err) {\n displayGradingError(root, user, err);\n\n return failedUpdate(err);\n }\n };\n};\n\n/**\n * Display a grading error, typically from a failed save.\n *\n * @param {HTMLElement} root The container for the grader\n * @param {Object} user The user who was errored\n * @param {Object} err The details of the error\n */\nconst displayGradingError = async(root, user, err) => {\n const [\n {html, js},\n errorString\n ] = await Promise.all([\n Templates.renderForPromise(templateNames.grader.gradingPanel.error, {error: err}),\n await getString('grades:gradesavefailed', 'mod_forum', {error: err.message, ...user}),\n ]);\n\n Templates.replaceNodeContents(root.querySelector(Selectors.regions.gradingPanelErrors), html, js);\n addToast(errorString, {type: 'warning'});\n};\n\n/**\n * Launch the grader interface with the specified parameters.\n *\n * @param {Function} getListOfUsers A function to get the list of users\n * @param {Function} getContentForUser A function to get the content for a specific user\n * @param {Function} getGradeForUser A function get the grade details for a specific user\n * @param {Function} setGradeForUser A function to set the grade for a specific user\n * @param {Object} preferences Preferences for the launch function\n * @param {Number} preferences.initialUserId\n * @param {string} preferences.moduleName\n * @param {string} preferences.courseName\n * @param {string} preferences.courseUrl\n * @param {boolean} preferences.sendStudentNotifications\n * @param {null|HTMLElement} preferences.focusOnClose\n */\nexport const launch = async(getListOfUsers, getContentForUser, getGradeForUser, setGradeForUser, {\n initialUserId = null,\n moduleName,\n courseName,\n courseUrl,\n sendStudentNotifications,\n focusOnClose = null,\n} = {}) => {\n\n // We need all of these functions to be executed in series, if one step runs before another the interface\n // will not work.\n\n // We need this promise to resolve separately so that we can avoid loading the whole interface if there are no users.\n const userList = await getListOfUsers();\n if (!userList.length) {\n addNotification({\n message: await getString('nouserstograde', 'core_grades'),\n type: \"error\",\n });\n return;\n }\n\n // Now that we have confirmed there are at least some users let's boot up the grader interface.\n const [\n graderLayout,\n {html, js},\n ] = await Promise.all([\n createFullScreenWindow({\n fullscreen: false,\n showLoader: false,\n focusOnClose,\n }),\n Templates.renderForPromise(templateNames.grader.app, {\n moduleName,\n courseName,\n courseUrl,\n drawer: {show: true},\n defaultsendnotifications: sendStudentNotifications,\n }),\n ]);\n\n const graderContainer = graderLayout.getContainer();\n\n const saveGradeFunction = getSaveUserGradeFunction(graderContainer, setGradeForUser);\n\n Templates.replaceNodeContents(graderContainer, html, js);\n const updateUserContent = getUpdateUserContentFunction(graderContainer, getContentForUser, getGradeForUser, saveGradeFunction);\n\n const userIds = userList.map(user => user.id);\n const statusContainer = graderContainer.querySelector(Selectors.regions.statusContainer);\n // Fetch the userpicker for display.\n const userPicker = await getUserPicker(\n userList,\n async(user) => {\n const userGrade = await updateUserContent(user);\n const renderContext = {\n status: userGrade.hasgrade,\n index: userIds.indexOf(user.id) + 1,\n total: userList.length\n };\n Templates.render(templateNames.grader.status, renderContext).then(html => {\n statusContainer.innerHTML = html;\n return html;\n }).catch();\n },\n saveGradeFunction,\n {\n initialUserId,\n },\n );\n\n // Register all event listeners.\n registerEventListeners(graderLayout, userPicker, saveGradeFunction, userList);\n\n // Display the newly created user picker.\n displayUserPicker(graderContainer, userPicker.rootNode);\n};\n\n/**\n * Show the grade for a specific user.\n *\n * @param {Function} getGradeForUser A function get the grade details for a specific user\n * @param {Number} userid The ID of a specific user\n * @param {String} moduleName the name of the module\n * @param {object} param\n * @param {null|HTMLElement} param.focusOnClose\n */\nexport const view = async(getGradeForUser, userid, moduleName, {\n focusOnClose = null,\n} = {}) => {\n\n const [\n userGrade,\n modal,\n ] = await Promise.all([\n getGradeForUser(userid),\n Modal.create({\n title: moduleName,\n large: true,\n type: Modal.types.CANCEL\n }),\n ]);\n\n const spinner = addIconToContainerWithPromise(modal.getRoot());\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n if (focusOnClose) {\n try {\n focusOnClose.focus();\n } catch (e) {\n // eslint-disable-line\n }\n }\n });\n\n modal.show();\n const output = document.createElement('div');\n const {html, js} = await Templates.renderForPromise('mod_forum/local/grades/view_grade', userGrade);\n Templates.replaceNodeContents(output, html, js);\n\n // Note: We do not use await here because it messes with the Modal transitions.\n const [gradeHTML, gradeJS] = await renderGradeTemplate(userGrade);\n const gradeReplace = output.querySelector('[data-region=\"grade-template\"]');\n Templates.replaceNodeContents(gradeReplace, gradeHTML, gradeJS);\n modal.setBody(output.outerHTML);\n spinner.resolve();\n};\n\nconst renderGradeTemplate = async(userGrade) => {\n const {html, js} = await Templates.renderForPromise(userGrade.templatename, userGrade.grade);\n return [html, js];\n};\nexport {getGradingPanelFunctions};\n"],"names":["templateNames","app","gradingPanel","error","searchResults","status","displayUserPicker","root","html","pickerRegion","querySelector","Selectors","regions","replaceNodeContents","fetchContentFromRender","js","getUpdateUserContentFunction","getContentForUser","getGradeForUser","saveGradeForUser","firstLoad","async","spinner","userGrade","Promise","all","user","id","then","moduleReplace","gradingPanelHtml","gradingPanelJS","Templates","render","templatename","grade","panelContainer","gradingPanelContainer","panel","form","addEventListener","event","preventDefault","scrollTop","resolve","hideSearchResultContainer","bodyContainer","userPickerContainer","searchResultsContainer","classList","remove","add","hideUserSearchInput","toggleSearchButton","searchContainer","searchInput","setAttribute","focus","parentElement","gradingInfoContainer","removeAttribute","collapseGradingDrawer","buttons","value","renderSearchResults","users","renderForPromise","registerEventListeners","graderLayout","userPicker","saveGradeFunction","userList","graderContainer","getContainer","toggleSearch","searchInputContainer","userSearchContainer","userSearchInput","e","target","closest","toggleFullscreen","stopImmediatePropagation","closeGrader","close","saveGrade","currentUser","getAttribute","innerHTML","showUserSearchInput","showSearchResultContainer","selectUserButton","selectUser","userId","find","setUserId","showUser","searchTerm","toLowerCase","filter","fullname","includes","searchForUsers","DrawerEvents","DRAWER_HIDDEN","drawerRoot","setContentContainerMargin","DRAWER_SHOWN","offsetWidth","rightMargin","contentContainer","moduleContainer","style","marginRight","getSaveUserGradeFunction","setGradeForUser","gradingPanelErrors","result","values","sendStudentNotifications","success","failed","displayGradingError","err","errorString","message","type","getListOfUsers","initialUserId","moduleName","courseName","courseUrl","focusOnClose","length","fullscreen","showLoader","drawer","show","defaultsendnotifications","updateUserContent","userIds","map","statusContainer","renderContext","hasgrade","index","indexOf","total","catch","rootNode","userid","modal","Modal","create","title","large","types","CANCEL","getRoot","on","ModalEvents","hidden","destroy","output","document","createElement","gradeHTML","gradeJS","renderGradeTemplate","gradeReplace","setBody","outerHTML"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"grader.min.js","sources":["../../../src/local/grades/grader.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module will tie together all of the different calls the gradable module will make.\n *\n * @module mod_forum/local/grades/grader\n * @copyright 2019 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Templates from 'core/templates';\nimport Selectors from './local/grader/selectors';\nimport getUserPicker from './local/grader/user_picker';\nimport {createLayout as createFullScreenWindow} from 'mod_forum/local/layout/fullscreen';\nimport getGradingPanelFunctions from './local/grader/gradingpanel';\nimport {add as addToast} from 'core/toast';\nimport {addNotification} from 'core/notification';\nimport {getString} from 'core/str';\nimport {failedUpdate} from 'core_grades/grades/grader/gradingpanel/normalise';\nimport {addIconToContainerWithPromise} from 'core/loadingicon';\nimport {debounce} from 'core/utils';\nimport {fillInitialValues} from 'core_grades/grades/grader/gradingpanel/comparison';\nimport * as Modal from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {subscribe} from 'core/pubsub';\nimport DrawerEvents from 'core/drawer_events';\n\nconst templateNames = {\n grader: {\n app: 'mod_forum/local/grades/grader',\n gradingPanel: {\n error: 'mod_forum/local/grades/local/grader/gradingpanel/error',\n },\n searchResults: 'mod_forum/local/grades/local/grader/user_picker/user_search',\n status: 'mod_forum/local/grades/local/grader/status',\n },\n};\n\n/**\n * Helper function that replaces the user picker placeholder with what we get back from the user picker class.\n *\n * @param {HTMLElement} root\n * @param {String} html\n */\nconst displayUserPicker = (root, html) => {\n const pickerRegion = root.querySelector(Selectors.regions.pickerRegion);\n Templates.replaceNodeContents(pickerRegion, html, '');\n};\n\n/**\n * To be removed, this is now done as a part of Templates.renderForPromise()\n *\n * @param {String} html\n * @param {String} js\n * @returns {array} An array containing the HTML, and JS.\n */\nconst fetchContentFromRender = (html, js) => {\n return [html, js];\n};\n\n/**\n * Here we build the function that is passed to the user picker that'll handle updating the user content area\n * of the grading interface.\n *\n * @param {HTMLElement} root\n * @param {Function} getContentForUser\n * @param {Function} getGradeForUser\n * @param {Function} saveGradeForUser\n * @return {Function}\n */\nconst getUpdateUserContentFunction = (root, getContentForUser, getGradeForUser, saveGradeForUser) => {\n let firstLoad = true;\n\n return async(user) => {\n const spinner = firstLoad ? null : addIconToContainerWithPromise(root);\n const [\n [html, js],\n userGrade,\n ] = await Promise.all([\n getContentForUser(user.id).then(fetchContentFromRender),\n getGradeForUser(user.id),\n ]);\n Templates.replaceNodeContents(root.querySelector(Selectors.regions.moduleReplace), html, js);\n\n const [\n gradingPanelHtml,\n gradingPanelJS\n ] = await Templates.render(userGrade.templatename, userGrade.grade).then(fetchContentFromRender);\n const panelContainer = root.querySelector(Selectors.regions.gradingPanelContainer);\n const panel = panelContainer.querySelector(Selectors.regions.gradingPanel);\n Templates.replaceNodeContents(panel, gradingPanelHtml, gradingPanelJS);\n\n const form = panel.querySelector('form');\n fillInitialValues(form);\n\n form.addEventListener('submit', event => {\n saveGradeForUser(user);\n event.preventDefault();\n });\n\n panelContainer.scrollTop = 0;\n firstLoad = false;\n\n if (spinner) {\n spinner.resolve();\n }\n return userGrade;\n };\n};\n\n/**\n * Show the search results container and hide the user picker and body content.\n *\n * @param {HTMLElement} bodyContainer The container element for the body content\n * @param {HTMLElement} userPickerContainer The container element for the user picker\n * @param {HTMLElement} searchResultsContainer The container element for the search results\n */\nconst showSearchResultContainer = (bodyContainer, userPickerContainer, searchResultsContainer) => {\n bodyContainer.classList.add('hidden');\n userPickerContainer.classList.add('hidden');\n searchResultsContainer.classList.remove('hidden');\n};\n\n/**\n * Hide the search results container and show the user picker and body content.\n *\n * @param {HTMLElement} bodyContainer The container element for the body content\n * @param {HTMLElement} userPickerContainer The container element for the user picker\n * @param {HTMLElement} searchResultsContainer The container element for the search results\n */\nconst hideSearchResultContainer = (bodyContainer, userPickerContainer, searchResultsContainer) => {\n bodyContainer.classList.remove('hidden');\n userPickerContainer.classList.remove('hidden');\n searchResultsContainer.classList.add('hidden');\n};\n\n/**\n * Toggles the visibility of the user search.\n *\n * @param {HTMLElement} toggleSearchButton The button that toggles the search\n * @param {HTMLElement} searchContainer The container element for the user search\n * @param {HTMLElement} searchInput The input element for searching\n */\nconst showUserSearchInput = (toggleSearchButton, searchContainer, searchInput) => {\n searchContainer.classList.remove('collapsed');\n toggleSearchButton.setAttribute('aria-expanded', 'true');\n toggleSearchButton.classList.add('expand');\n toggleSearchButton.classList.remove('collapse');\n\n // Hide the grading info container from screen reader.\n const gradingInfoContainer = searchContainer.parentElement.querySelector(Selectors.regions.gradingInfoContainer);\n gradingInfoContainer.setAttribute('aria-hidden', 'true');\n\n // Hide the collapse grading drawer button from screen reader.\n const collapseGradingDrawer = searchContainer.parentElement.querySelector(Selectors.buttons.collapseGradingDrawer);\n collapseGradingDrawer.setAttribute('aria-hidden', 'true');\n collapseGradingDrawer.setAttribute('tabindex', '-1');\n\n searchInput.focus();\n};\n\n/**\n * Toggles the visibility of the user search.\n *\n * @param {HTMLElement} toggleSearchButton The button that toggles the search\n * @param {HTMLElement} searchContainer The container element for the user search\n * @param {HTMLElement} searchInput The input element for searching\n */\nconst hideUserSearchInput = (toggleSearchButton, searchContainer, searchInput) => {\n searchContainer.classList.add('collapsed');\n toggleSearchButton.setAttribute('aria-expanded', 'false');\n toggleSearchButton.classList.add('collapse');\n toggleSearchButton.classList.remove('expand');\n toggleSearchButton.focus();\n\n // Show the grading info container to screen reader.\n const gradingInfoContainer = searchContainer.parentElement.querySelector(Selectors.regions.gradingInfoContainer);\n gradingInfoContainer.removeAttribute('aria-hidden');\n\n // Show the collapse grading drawer button from screen reader.\n const collapseGradingDrawer = searchContainer.parentElement.querySelector(Selectors.buttons.collapseGradingDrawer);\n collapseGradingDrawer.removeAttribute('aria-hidden');\n collapseGradingDrawer.setAttribute('tabindex', '0');\n\n searchInput.value = '';\n};\n\n/**\n * Find the list of users who's names include the given search term.\n *\n * @param {Array} userList List of users for the grader\n * @param {String} searchTerm The search term to match\n * @return {Array}\n */\nconst searchForUsers = (userList, searchTerm) => {\n if (searchTerm === '') {\n return userList;\n }\n\n searchTerm = searchTerm.toLowerCase();\n\n return userList.filter((user) => {\n return user.fullname.toLowerCase().includes(searchTerm);\n });\n};\n\n/**\n * Render the list of users in the search results area.\n *\n * @param {HTMLElement} searchResultsContainer The container element for search results\n * @param {Array} users The list of users to display\n */\nconst renderSearchResults = async(searchResultsContainer, users) => {\n const {html, js} = await Templates.renderForPromise(templateNames.grader.searchResults, {users});\n Templates.replaceNodeContents(searchResultsContainer, html, js);\n};\n\n/**\n * Add click handlers to the buttons in the header of the grading interface.\n *\n * @param {HTMLElement} graderLayout\n * @param {Object} userPicker\n * @param {Function} saveGradeFunction\n * @param {Array} userList List of users for the grader.\n */\nconst registerEventListeners = (graderLayout, userPicker, saveGradeFunction, userList) => {\n const graderContainer = graderLayout.getContainer();\n const toggleSearchButton = graderContainer.querySelector(Selectors.buttons.toggleSearch);\n const searchInputContainer = graderContainer.querySelector(Selectors.regions.userSearchContainer);\n const searchInput = searchInputContainer.querySelector(Selectors.regions.userSearchInput);\n const bodyContainer = graderContainer.querySelector(Selectors.regions.bodyContainer);\n const userPickerContainer = graderContainer.querySelector(Selectors.regions.pickerRegion);\n const searchResultsContainer = graderContainer.querySelector(Selectors.regions.searchResultsContainer);\n\n graderContainer.addEventListener('click', (e) => {\n if (e.target.closest(Selectors.buttons.toggleFullscreen)) {\n e.stopImmediatePropagation();\n e.preventDefault();\n graderLayout.toggleFullscreen();\n\n return;\n }\n\n if (e.target.closest(Selectors.buttons.closeGrader)) {\n e.stopImmediatePropagation();\n e.preventDefault();\n\n graderLayout.close();\n\n return;\n }\n\n if (e.target.closest(Selectors.buttons.saveGrade)) {\n saveGradeFunction(userPicker.currentUser);\n }\n\n if (e.target.closest(Selectors.buttons.toggleSearch)) {\n if (toggleSearchButton.getAttribute('aria-expanded') === 'true') {\n // Search is open so let's close it.\n hideUserSearchInput(toggleSearchButton, searchInputContainer, searchInput);\n hideSearchResultContainer(bodyContainer, userPickerContainer, searchResultsContainer);\n searchResultsContainer.innerHTML = '';\n } else {\n // Search is closed so let's open it.\n showUserSearchInput(toggleSearchButton, searchInputContainer, searchInput);\n showSearchResultContainer(bodyContainer, userPickerContainer, searchResultsContainer);\n renderSearchResults(searchResultsContainer, userList);\n }\n\n return;\n }\n\n const selectUserButton = e.target.closest(Selectors.buttons.selectUser);\n if (selectUserButton) {\n const userId = selectUserButton.getAttribute('data-userid');\n const user = userList.find(user => user.id == userId);\n userPicker.setUserId(userId);\n userPicker.showUser(user);\n hideUserSearchInput(toggleSearchButton, searchInputContainer, searchInput);\n hideSearchResultContainer(bodyContainer, userPickerContainer, searchResultsContainer);\n searchResultsContainer.innerHTML = '';\n }\n });\n\n // Debounce the search input so that it only executes 300 milliseconds after the user has finished typing.\n searchInput.addEventListener('input', debounce(() => {\n const users = searchForUsers(userList, searchInput.value);\n renderSearchResults(searchResultsContainer, users);\n }, 300));\n\n // Remove the right margin of the content container when the grading panel is hidden so that it expands to full-width.\n subscribe(DrawerEvents.DRAWER_HIDDEN, (drawerRoot) => {\n const gradingPanel = drawerRoot[0];\n if (gradingPanel.querySelector(Selectors.regions.gradingPanel)) {\n setContentContainerMargin(graderContainer, 0);\n }\n });\n\n // Bring back the right margin of the content container when the grading panel is shown to give space for the grading panel.\n subscribe(DrawerEvents.DRAWER_SHOWN, (drawerRoot) => {\n const gradingPanel = drawerRoot[0];\n if (gradingPanel.querySelector(Selectors.regions.gradingPanel)) {\n setContentContainerMargin(graderContainer, gradingPanel.offsetWidth);\n }\n });\n};\n\n/**\n * Adjusts the right margin of the content container.\n *\n * @param {HTMLElement} graderContainer The container for the grader app.\n * @param {Number} rightMargin The right margin value.\n */\nconst setContentContainerMargin = (graderContainer, rightMargin) => {\n const contentContainer = graderContainer.querySelector(Selectors.regions.moduleContainer);\n if (contentContainer) {\n contentContainer.style.marginRight = `${rightMargin}px`;\n }\n};\n\n/**\n * Get the function used to save a user grade.\n *\n * @param {HTMLElement} root The container for the grader\n * @param {Function} setGradeForUser The function that will be called.\n * @return {Function}\n */\nconst getSaveUserGradeFunction = (root, setGradeForUser) => {\n return async(user) => {\n try {\n root.querySelector(Selectors.regions.gradingPanelErrors).innerHTML = '';\n const result = await setGradeForUser(\n user.id,\n root.querySelector(Selectors.values.sendStudentNotifications).value,\n root.querySelector(Selectors.regions.gradingPanel)\n );\n if (result.success) {\n addToast(await getString('grades:gradesavedfor', 'mod_forum', user));\n }\n if (result.failed) {\n displayGradingError(root, user, result.error);\n }\n\n return result;\n } catch (err) {\n displayGradingError(root, user, err);\n\n return failedUpdate(err);\n }\n };\n};\n\n/**\n * Display a grading error, typically from a failed save.\n *\n * @param {HTMLElement} root The container for the grader\n * @param {Object} user The user who was errored\n * @param {Object} err The details of the error\n */\nconst displayGradingError = async(root, user, err) => {\n const [\n {html, js},\n errorString\n ] = await Promise.all([\n Templates.renderForPromise(templateNames.grader.gradingPanel.error, {error: err}),\n await getString('grades:gradesavefailed', 'mod_forum', {error: err.message, ...user}),\n ]);\n\n Templates.replaceNodeContents(root.querySelector(Selectors.regions.gradingPanelErrors), html, js);\n addToast(errorString, {type: 'warning'});\n};\n\n/**\n * Launch the grader interface with the specified parameters.\n *\n * @param {Function} getListOfUsers A function to get the list of users\n * @param {Function} getContentForUser A function to get the content for a specific user\n * @param {Function} getGradeForUser A function get the grade details for a specific user\n * @param {Function} setGradeForUser A function to set the grade for a specific user\n * @param {Object} preferences Preferences for the launch function\n * @param {Number} preferences.initialUserId\n * @param {string} preferences.moduleName\n * @param {string} preferences.courseName\n * @param {string} preferences.courseUrl\n * @param {boolean} preferences.sendStudentNotifications\n * @param {null|HTMLElement} preferences.focusOnClose\n */\nexport const launch = async(getListOfUsers, getContentForUser, getGradeForUser, setGradeForUser, {\n initialUserId = null,\n moduleName,\n courseName,\n courseUrl,\n sendStudentNotifications,\n focusOnClose = null,\n} = {}) => {\n\n // We need all of these functions to be executed in series, if one step runs before another the interface\n // will not work.\n\n // We need this promise to resolve separately so that we can avoid loading the whole interface if there are no users.\n const userList = await getListOfUsers();\n if (!userList.length) {\n addNotification({\n message: await getString('nouserstograde', 'core_grades'),\n type: \"error\",\n });\n return;\n }\n\n // Now that we have confirmed there are at least some users let's boot up the grader interface.\n const [\n graderLayout,\n {html, js},\n ] = await Promise.all([\n createFullScreenWindow({\n fullscreen: false,\n showLoader: false,\n focusOnClose,\n }),\n Templates.renderForPromise(templateNames.grader.app, {\n moduleName,\n courseName,\n courseUrl,\n drawer: {show: true},\n defaultsendnotifications: sendStudentNotifications,\n }),\n ]);\n\n const graderContainer = graderLayout.getContainer();\n\n const saveGradeFunction = getSaveUserGradeFunction(graderContainer, setGradeForUser);\n\n Templates.replaceNodeContents(graderContainer, html, js);\n const updateUserContent = getUpdateUserContentFunction(graderContainer, getContentForUser, getGradeForUser, saveGradeFunction);\n\n const userIds = userList.map(user => user.id);\n const statusContainer = graderContainer.querySelector(Selectors.regions.statusContainer);\n // Fetch the userpicker for display.\n const userPicker = await getUserPicker(\n userList,\n async(user) => {\n const userGrade = await updateUserContent(user);\n const renderContext = {\n status: userGrade.hasgrade,\n index: userIds.indexOf(user.id) + 1,\n total: userList.length\n };\n Templates.render(templateNames.grader.status, renderContext).then(html => {\n statusContainer.innerHTML = html;\n return html;\n }).catch();\n },\n saveGradeFunction,\n {\n initialUserId,\n },\n );\n\n // Register all event listeners.\n registerEventListeners(graderLayout, userPicker, saveGradeFunction, userList);\n\n // Display the newly created user picker.\n displayUserPicker(graderContainer, userPicker.rootNode);\n};\n\n/**\n * Show the grade for a specific user.\n *\n * @param {Function} getGradeForUser A function get the grade details for a specific user\n * @param {Number} userid The ID of a specific user\n * @param {String} moduleName the name of the module\n * @param {object} param\n * @param {null|HTMLElement} param.focusOnClose\n */\nexport const view = async(getGradeForUser, userid, moduleName, {\n focusOnClose = null,\n} = {}) => {\n\n const [\n userGrade,\n modal,\n ] = await Promise.all([\n getGradeForUser(userid),\n Modal.create({\n title: moduleName,\n large: true,\n type: Modal.types.CANCEL\n }),\n ]);\n\n const spinner = addIconToContainerWithPromise(modal.getRoot());\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n if (focusOnClose) {\n try {\n focusOnClose.focus();\n } catch (e) {\n // eslint-disable-line\n }\n }\n });\n\n modal.show();\n const output = document.createElement('div');\n const {html, js} = await Templates.renderForPromise('mod_forum/local/grades/view_grade', userGrade);\n Templates.replaceNodeContents(output, html, js);\n\n // Note: We do not use await here because it messes with the Modal transitions.\n const [gradeHTML, gradeJS] = await renderGradeTemplate(userGrade);\n const gradeReplace = output.querySelector('[data-region=\"grade-template\"]');\n Templates.replaceNodeContents(gradeReplace, gradeHTML, gradeJS);\n modal.setBody(output.outerHTML);\n spinner.resolve();\n};\n\nconst renderGradeTemplate = async(userGrade) => {\n const {html, js} = await Templates.renderForPromise(userGrade.templatename, userGrade.grade);\n return [html, js];\n};\nexport {getGradingPanelFunctions};\n"],"names":["templateNames","app","gradingPanel","error","searchResults","status","displayUserPicker","root","html","pickerRegion","querySelector","Selectors","regions","replaceNodeContents","fetchContentFromRender","js","getUpdateUserContentFunction","getContentForUser","getGradeForUser","saveGradeForUser","firstLoad","async","spinner","userGrade","Promise","all","user","id","then","moduleReplace","gradingPanelHtml","gradingPanelJS","Templates","render","templatename","grade","panelContainer","gradingPanelContainer","panel","form","addEventListener","event","preventDefault","scrollTop","resolve","hideSearchResultContainer","bodyContainer","userPickerContainer","searchResultsContainer","classList","remove","add","hideUserSearchInput","toggleSearchButton","searchContainer","searchInput","setAttribute","focus","parentElement","gradingInfoContainer","removeAttribute","collapseGradingDrawer","buttons","value","renderSearchResults","users","renderForPromise","registerEventListeners","graderLayout","userPicker","saveGradeFunction","userList","graderContainer","getContainer","toggleSearch","searchInputContainer","userSearchContainer","userSearchInput","e","target","closest","toggleFullscreen","stopImmediatePropagation","closeGrader","close","saveGrade","currentUser","getAttribute","innerHTML","showUserSearchInput","showSearchResultContainer","selectUserButton","selectUser","userId","find","setUserId","showUser","searchTerm","toLowerCase","filter","fullname","includes","searchForUsers","DrawerEvents","DRAWER_HIDDEN","drawerRoot","setContentContainerMargin","DRAWER_SHOWN","offsetWidth","rightMargin","contentContainer","moduleContainer","style","marginRight","getSaveUserGradeFunction","setGradeForUser","gradingPanelErrors","result","values","sendStudentNotifications","success","failed","displayGradingError","err","errorString","message","type","getListOfUsers","initialUserId","moduleName","courseName","courseUrl","focusOnClose","length","fullscreen","showLoader","drawer","show","defaultsendnotifications","updateUserContent","userIds","map","statusContainer","renderContext","hasgrade","index","indexOf","total","catch","rootNode","userid","modal","Modal","create","title","large","types","CANCEL","getRoot","on","ModalEvents","hidden","destroy","output","document","createElement","gradeHTML","gradeJS","renderGradeTemplate","gradeReplace","setBody","outerHTML"],"mappings":"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"} \ No newline at end of file diff --git a/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js b/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js index bf6878192aa..e3f6cf4615c 100644 --- a/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js +++ b/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js @@ -5,6 +5,6 @@ define("mod_forum/local/grades/local/grader/user_picker",["exports","core/templa * @module mod_forum/local/grades/local/grader/user_picker * @copyright 2019 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),_selectors=_interopRequireDefault(_selectors);class UserPicker{constructor(userList,showUserCallback,preChangeUserCallback){this.userList=userList,this.showUserCallback=showUserCallback,this.preChangeUserCallback=preChangeUserCallback,this.currentUserIndex=0,this.render=this.render.bind(this),this.setUserId=this.setUserId.bind(this)}setUserId(userId){const userIndex=this.userList.findIndex((user=>user.id===parseInt(userId)));if(-1===userIndex)throw Error("User with id ".concat(userId," not found"));this.currentUserIndex=userIndex}async render(){this.root=document.createElement("div");const{html:html,js:js}=await this.renderNavigator();_templates.default.replaceNodeContents(this.root,html,js),await this.showUser(this.currentUser),this.registerEventListeners()}renderNavigator(){return _templates.default.renderForPromise("".concat("mod_forum/local/grades/local/grader","/user_picker"),{})}renderUserChange(context){return _templates.default.renderForPromise("".concat("mod_forum/local/grades/local/grader","/user_picker/user"),context)}async showUser(user){const[{html:html,js:js}]=await Promise.all([this.renderUserChange(user),this.showUserCallback(user)]),userRegion=this.root.querySelector(_selectors.default.regions.userRegion);_templates.default.replaceNodeContents(userRegion,html,js);this.root.querySelector(_selectors.default.regions.currentUser).textContent=await(0,_str.get_string)("nowgradinguser","mod_forum",user.fullname)}registerEventListeners(){this.root.addEventListener("click",(async e=>{const button=e.target.closest(_selectors.default.actions.changeUser);if(button){(await this.preChangeUserCallback(this.currentUser)).failed||(this.updateIndex(parseInt(button.dataset.direction)),await this.showUser(this.currentUser))}}))}updateIndex(direction){return this.currentUserIndex+=direction,this.currentUserIndex<0?this.currentUserIndex=this.userList.length-1:this.currentUserIndex>this.userList.length-1&&(this.currentUserIndex=0),this.currentUserIndex}get currentUser(){return{...this.userList[this.currentUserIndex],total:this.userList.length,displayIndex:this.currentUserIndex+1}}get rootNode(){return this.root}}return _exports.default=async function(users,showUserCallback,preChangeUserCallback){let{initialUserId:initialUserId=null}=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};const userPicker=new UserPicker(users,showUserCallback,preChangeUserCallback);return initialUserId&&userPicker.setUserId(initialUserId),await userPicker.render(),userPicker},_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),_selectors=_interopRequireDefault(_selectors);class UserPicker{constructor(userList,showUserCallback,preChangeUserCallback){this.userList=userList,this.showUserCallback=showUserCallback,this.preChangeUserCallback=preChangeUserCallback,this.currentUserIndex=0,this.render=this.render.bind(this),this.setUserId=this.setUserId.bind(this)}setUserId(userId){const userIndex=this.userList.findIndex((user=>user.id===parseInt(userId)));if(-1===userIndex)throw Error("User with id ".concat(userId," not found"));this.currentUserIndex=userIndex}async render(){this.root=document.createElement("div");const{html:html,js:js}=await this.renderNavigator();_templates.default.replaceNodeContents(this.root,html,js),await this.showUser(this.currentUser),this.registerEventListeners()}renderNavigator(){return _templates.default.renderForPromise("".concat("mod_forum/local/grades/local/grader","/user_picker"),{})}renderUserChange(context){return _templates.default.renderForPromise("".concat("mod_forum/local/grades/local/grader","/user_picker/user"),context)}async showUser(user){const[{html:html,js:js}]=await Promise.all([this.renderUserChange(user),this.showUserCallback(user)]),userRegion=this.root.querySelector(_selectors.default.regions.userRegion);_templates.default.replaceNodeContents(userRegion,html,js);this.root.querySelector(_selectors.default.regions.currentUser).textContent=await(0,_str.getString)("nowgradinguser","mod_forum",user.fullname)}registerEventListeners(){this.root.addEventListener("click",(async e=>{const button=e.target.closest(_selectors.default.actions.changeUser);if(button){(await this.preChangeUserCallback(this.currentUser)).failed||(this.updateIndex(parseInt(button.dataset.direction)),await this.showUser(this.currentUser))}}))}updateIndex(direction){return this.currentUserIndex+=direction,this.currentUserIndex<0?this.currentUserIndex=this.userList.length-1:this.currentUserIndex>this.userList.length-1&&(this.currentUserIndex=0),this.currentUserIndex}get currentUser(){return{...this.userList[this.currentUserIndex],total:this.userList.length,displayIndex:this.currentUserIndex+1}}get rootNode(){return this.root}}return _exports.default=async function(users,showUserCallback,preChangeUserCallback){let{initialUserId:initialUserId=null}=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};const userPicker=new UserPicker(users,showUserCallback,preChangeUserCallback);return initialUserId&&userPicker.setUserId(initialUserId),await userPicker.render(),userPicker},_exports.default})); //# sourceMappingURL=user_picker.min.js.map \ No newline at end of file diff --git a/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js.map b/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js.map index 3fb6d6e24b9..a989152612e 100644 --- a/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js.map +++ b/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js.map @@ -1 +1 @@ -{"version":3,"file":"user_picker.min.js","sources":["../../../../../src/local/grades/local/grader/user_picker.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module will tie together all of the different calls the gradable module will make.\n *\n * @module mod_forum/local/grades/local/grader/user_picker\n * @copyright 2019 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport Selectors from './user_picker/selectors';\nimport {get_string as getString} from 'core/str';\n\nconst templatePath = 'mod_forum/local/grades/local/grader';\n\n/**\n * The Grader User Picker.\n *\n * @class mod_forum/local/grades/local/grader/user_picker\n */\nclass UserPicker {\n\n /**\n * Constructor for the User Picker.\n *\n * @constructor mod_forum/local/grades/local/grader/user_picker\n * @param {Array} userList List of users\n * @param {Function} showUserCallback The callback used to display the user\n * @param {Function} preChangeUserCallback The callback to use before changing user\n */\n constructor(userList, showUserCallback, preChangeUserCallback) {\n this.userList = userList;\n this.showUserCallback = showUserCallback;\n this.preChangeUserCallback = preChangeUserCallback;\n this.currentUserIndex = 0;\n\n // Ensure that render is bound correctly.\n this.render = this.render.bind(this);\n this.setUserId = this.setUserId.bind(this);\n }\n\n /**\n * Set the current userid without rendering the change.\n * To show the user, call showUser too.\n *\n * @param {Number} userId\n */\n setUserId(userId) {\n // Determine the current index based on the user ID.\n const userIndex = this.userList.findIndex(user => {\n return user.id === parseInt(userId);\n });\n\n if (userIndex === -1) {\n throw Error(`User with id ${userId} not found`);\n }\n\n this.currentUserIndex = userIndex;\n }\n\n /**\n * Render the user picker.\n */\n async render() {\n // Create the root node.\n this.root = document.createElement('div');\n\n const {html, js} = await this.renderNavigator();\n Templates.replaceNodeContents(this.root, html, js);\n\n // Call the showUser function to show the first user immediately.\n await this.showUser(this.currentUser);\n\n // Ensure that the event listeners are all bound.\n this.registerEventListeners();\n }\n\n /**\n * Render the navigator itself.\n *\n * @returns {Promise}\n */\n renderNavigator() {\n return Templates.renderForPromise(`${templatePath}/user_picker`, {});\n }\n\n /**\n * Render the current user details for the picker.\n *\n * @param {Object} context The data used to render the user picker.\n * @returns {Promise}\n */\n renderUserChange(context) {\n return Templates.renderForPromise(`${templatePath}/user_picker/user`, context);\n }\n\n /**\n * Show the specified user in the picker.\n *\n * @param {Object} user\n */\n async showUser(user) {\n const [{html, js}] = await Promise.all([this.renderUserChange(user), this.showUserCallback(user)]);\n const userRegion = this.root.querySelector(Selectors.regions.userRegion);\n Templates.replaceNodeContents(userRegion, html, js);\n\n // Update the hidden now-grading region so screen readers can announce the user that's currently being graded.\n const currentUserRegion = this.root.querySelector(Selectors.regions.currentUser);\n currentUserRegion.textContent = await getString('nowgradinguser', 'mod_forum', user.fullname);\n }\n\n /**\n * Register the event listeners for the user picker.\n */\n registerEventListeners() {\n this.root.addEventListener('click', async(e) => {\n const button = e.target.closest(Selectors.actions.changeUser);\n\n if (button) {\n const result = await this.preChangeUserCallback(this.currentUser);\n\n if (!result.failed) {\n this.updateIndex(parseInt(button.dataset.direction));\n await this.showUser(this.currentUser);\n }\n }\n });\n }\n\n /**\n * Update the current user index.\n *\n * @param {Number} direction\n * @returns {Number}}\n */\n updateIndex(direction) {\n this.currentUserIndex += direction;\n\n // Loop around the edges.\n if (this.currentUserIndex < 0) {\n this.currentUserIndex = this.userList.length - 1;\n } else if (this.currentUserIndex > this.userList.length - 1) {\n this.currentUserIndex = 0;\n }\n\n return this.currentUserIndex;\n }\n\n /**\n * Get the details of the user currently shown with the total number of users, and the 1-indexed count of the\n * current user.\n *\n * @returns {Object}\n */\n get currentUser() {\n return {\n ...this.userList[this.currentUserIndex],\n total: this.userList.length,\n displayIndex: this.currentUserIndex + 1,\n };\n }\n\n /**\n * Get the root node for the User Picker.\n *\n * @returns {HTMLElement}\n */\n get rootNode() {\n return this.root;\n }\n}\n\n/**\n * Create a new user picker.\n *\n * @param {Array} users The list of users\n * @param {Function} showUserCallback The function to call to show a specific user\n * @param {Function} preChangeUserCallback The fucntion to call to save the grade for the current user\n * @param {Number} [currentUserID] The userid of the current user\n * @returns {UserPicker}\n */\nexport default async(\n users,\n showUserCallback,\n preChangeUserCallback,\n {\n initialUserId = null,\n } = {}\n) => {\n const userPicker = new UserPicker(users, showUserCallback, preChangeUserCallback);\n if (initialUserId) {\n userPicker.setUserId(initialUserId);\n }\n await userPicker.render();\n\n return userPicker;\n};\n"],"names":["UserPicker","constructor","userList","showUserCallback","preChangeUserCallback","currentUserIndex","render","this","bind","setUserId","userId","userIndex","findIndex","user","id","parseInt","Error","root","document","createElement","html","js","renderNavigator","replaceNodeContents","showUser","currentUser","registerEventListeners","Templates","renderForPromise","renderUserChange","context","Promise","all","userRegion","querySelector","Selectors","regions","textContent","fullname","addEventListener","async","button","e","target","closest","actions","changeUser","failed","updateIndex","dataset","direction","length","total","displayIndex","rootNode","users","initialUserId","userPicker"],"mappings":";;;;;;;uLAkCMA,WAUFC,YAAYC,SAAUC,iBAAkBC,4BAC/BF,SAAWA,cACXC,iBAAmBA,sBACnBC,sBAAwBA,2BACxBC,iBAAmB,OAGnBC,OAASC,KAAKD,OAAOE,KAAKD,WAC1BE,UAAYF,KAAKE,UAAUD,KAAKD,MASzCE,UAAUC,cAEAC,UAAYJ,KAAKL,SAASU,WAAUC,MAC/BA,KAAKC,KAAOC,SAASL,cAGb,IAAfC,gBACMK,6BAAsBN,2BAG3BL,iBAAmBM,8BAQnBM,KAAOC,SAASC,cAAc,aAE7BC,KAACA,KAADC,GAAOA,UAAYd,KAAKe,qCACpBC,oBAAoBhB,KAAKU,KAAMG,KAAMC,UAGzCd,KAAKiB,SAASjB,KAAKkB,kBAGpBC,yBAQTJ,yBACWK,mBAAUC,2BAtEJ,sDAsEoD,IASrEC,iBAAiBC,gBACNH,mBAAUC,2BAhFJ,2DAgFyDE,wBAQ3DjB,aACJO,KAACA,KAADC,GAAOA,WAAaU,QAAQC,IAAI,CAACzB,KAAKsB,iBAAiBhB,MAAON,KAAKJ,iBAAiBU,QACrFoB,WAAa1B,KAAKU,KAAKiB,cAAcC,mBAAUC,QAAQH,+BACnDV,oBAAoBU,WAAYb,KAAMC,IAGtBd,KAAKU,KAAKiB,cAAcC,mBAAUC,QAAQX,aAClDY,kBAAoB,mBAAU,iBAAkB,YAAaxB,KAAKyB,UAMxFZ,8BACST,KAAKsB,iBAAiB,SAASC,MAAAA,UAC1BC,OAASC,EAAEC,OAAOC,QAAQT,mBAAUU,QAAQC,eAE9CL,OAAQ,QACalC,KAAKH,sBAAsBG,KAAKkB,cAEzCsB,cACHC,YAAYjC,SAAS0B,OAAOQ,QAAQC,kBACnC3C,KAAKiB,SAASjB,KAAKkB,kBAYzCuB,YAAYE,uBACH7C,kBAAoB6C,UAGrB3C,KAAKF,iBAAmB,OACnBA,iBAAmBE,KAAKL,SAASiD,OAAS,EACxC5C,KAAKF,iBAAmBE,KAAKL,SAASiD,OAAS,SACjD9C,iBAAmB,GAGrBE,KAAKF,iBASZoB,wBACO,IACAlB,KAAKL,SAASK,KAAKF,kBACtB+C,MAAO7C,KAAKL,SAASiD,OACrBE,aAAc9C,KAAKF,iBAAmB,GAS1CiD,sBACO/C,KAAKU,8BAaLuB,eACXe,MACApD,iBACAC,2BACAoD,cACIA,cAAgB,6DAChB,SAEEC,WAAa,IAAIzD,WAAWuD,MAAOpD,iBAAkBC,8BACvDoD,eACAC,WAAWhD,UAAU+C,qBAEnBC,WAAWnD,SAEVmD"} \ No newline at end of file +{"version":3,"file":"user_picker.min.js","sources":["../../../../../src/local/grades/local/grader/user_picker.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module will tie together all of the different calls the gradable module will make.\n *\n * @module mod_forum/local/grades/local/grader/user_picker\n * @copyright 2019 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport Selectors from './user_picker/selectors';\nimport {getString} from 'core/str';\n\nconst templatePath = 'mod_forum/local/grades/local/grader';\n\n/**\n * The Grader User Picker.\n *\n * @class mod_forum/local/grades/local/grader/user_picker\n */\nclass UserPicker {\n\n /**\n * Constructor for the User Picker.\n *\n * @constructor mod_forum/local/grades/local/grader/user_picker\n * @param {Array} userList List of users\n * @param {Function} showUserCallback The callback used to display the user\n * @param {Function} preChangeUserCallback The callback to use before changing user\n */\n constructor(userList, showUserCallback, preChangeUserCallback) {\n this.userList = userList;\n this.showUserCallback = showUserCallback;\n this.preChangeUserCallback = preChangeUserCallback;\n this.currentUserIndex = 0;\n\n // Ensure that render is bound correctly.\n this.render = this.render.bind(this);\n this.setUserId = this.setUserId.bind(this);\n }\n\n /**\n * Set the current userid without rendering the change.\n * To show the user, call showUser too.\n *\n * @param {Number} userId\n */\n setUserId(userId) {\n // Determine the current index based on the user ID.\n const userIndex = this.userList.findIndex(user => {\n return user.id === parseInt(userId);\n });\n\n if (userIndex === -1) {\n throw Error(`User with id ${userId} not found`);\n }\n\n this.currentUserIndex = userIndex;\n }\n\n /**\n * Render the user picker.\n */\n async render() {\n // Create the root node.\n this.root = document.createElement('div');\n\n const {html, js} = await this.renderNavigator();\n Templates.replaceNodeContents(this.root, html, js);\n\n // Call the showUser function to show the first user immediately.\n await this.showUser(this.currentUser);\n\n // Ensure that the event listeners are all bound.\n this.registerEventListeners();\n }\n\n /**\n * Render the navigator itself.\n *\n * @returns {Promise}\n */\n renderNavigator() {\n return Templates.renderForPromise(`${templatePath}/user_picker`, {});\n }\n\n /**\n * Render the current user details for the picker.\n *\n * @param {Object} context The data used to render the user picker.\n * @returns {Promise}\n */\n renderUserChange(context) {\n return Templates.renderForPromise(`${templatePath}/user_picker/user`, context);\n }\n\n /**\n * Show the specified user in the picker.\n *\n * @param {Object} user\n */\n async showUser(user) {\n const [{html, js}] = await Promise.all([this.renderUserChange(user), this.showUserCallback(user)]);\n const userRegion = this.root.querySelector(Selectors.regions.userRegion);\n Templates.replaceNodeContents(userRegion, html, js);\n\n // Update the hidden now-grading region so screen readers can announce the user that's currently being graded.\n const currentUserRegion = this.root.querySelector(Selectors.regions.currentUser);\n currentUserRegion.textContent = await getString('nowgradinguser', 'mod_forum', user.fullname);\n }\n\n /**\n * Register the event listeners for the user picker.\n */\n registerEventListeners() {\n this.root.addEventListener('click', async(e) => {\n const button = e.target.closest(Selectors.actions.changeUser);\n\n if (button) {\n const result = await this.preChangeUserCallback(this.currentUser);\n\n if (!result.failed) {\n this.updateIndex(parseInt(button.dataset.direction));\n await this.showUser(this.currentUser);\n }\n }\n });\n }\n\n /**\n * Update the current user index.\n *\n * @param {Number} direction\n * @returns {Number}}\n */\n updateIndex(direction) {\n this.currentUserIndex += direction;\n\n // Loop around the edges.\n if (this.currentUserIndex < 0) {\n this.currentUserIndex = this.userList.length - 1;\n } else if (this.currentUserIndex > this.userList.length - 1) {\n this.currentUserIndex = 0;\n }\n\n return this.currentUserIndex;\n }\n\n /**\n * Get the details of the user currently shown with the total number of users, and the 1-indexed count of the\n * current user.\n *\n * @returns {Object}\n */\n get currentUser() {\n return {\n ...this.userList[this.currentUserIndex],\n total: this.userList.length,\n displayIndex: this.currentUserIndex + 1,\n };\n }\n\n /**\n * Get the root node for the User Picker.\n *\n * @returns {HTMLElement}\n */\n get rootNode() {\n return this.root;\n }\n}\n\n/**\n * Create a new user picker.\n *\n * @param {Array} users The list of users\n * @param {Function} showUserCallback The function to call to show a specific user\n * @param {Function} preChangeUserCallback The fucntion to call to save the grade for the current user\n * @param {Number} [currentUserID] The userid of the current user\n * @returns {UserPicker}\n */\nexport default async(\n users,\n showUserCallback,\n preChangeUserCallback,\n {\n initialUserId = null,\n } = {}\n) => {\n const userPicker = new UserPicker(users, showUserCallback, preChangeUserCallback);\n if (initialUserId) {\n userPicker.setUserId(initialUserId);\n }\n await userPicker.render();\n\n return userPicker;\n};\n"],"names":["UserPicker","constructor","userList","showUserCallback","preChangeUserCallback","currentUserIndex","render","this","bind","setUserId","userId","userIndex","findIndex","user","id","parseInt","Error","root","document","createElement","html","js","renderNavigator","replaceNodeContents","showUser","currentUser","registerEventListeners","Templates","renderForPromise","renderUserChange","context","Promise","all","userRegion","querySelector","Selectors","regions","textContent","fullname","addEventListener","async","button","e","target","closest","actions","changeUser","failed","updateIndex","dataset","direction","length","total","displayIndex","rootNode","users","initialUserId","userPicker"],"mappings":";;;;;;;uLAkCMA,WAUFC,YAAYC,SAAUC,iBAAkBC,4BAC/BF,SAAWA,cACXC,iBAAmBA,sBACnBC,sBAAwBA,2BACxBC,iBAAmB,OAGnBC,OAASC,KAAKD,OAAOE,KAAKD,WAC1BE,UAAYF,KAAKE,UAAUD,KAAKD,MASzCE,UAAUC,cAEAC,UAAYJ,KAAKL,SAASU,WAAUC,MAC/BA,KAAKC,KAAOC,SAASL,cAGb,IAAfC,gBACMK,6BAAsBN,2BAG3BL,iBAAmBM,8BAQnBM,KAAOC,SAASC,cAAc,aAE7BC,KAACA,KAADC,GAAOA,UAAYd,KAAKe,qCACpBC,oBAAoBhB,KAAKU,KAAMG,KAAMC,UAGzCd,KAAKiB,SAASjB,KAAKkB,kBAGpBC,yBAQTJ,yBACWK,mBAAUC,2BAtEJ,sDAsEoD,IASrEC,iBAAiBC,gBACNH,mBAAUC,2BAhFJ,2DAgFyDE,wBAQ3DjB,aACJO,KAACA,KAADC,GAAOA,WAAaU,QAAQC,IAAI,CAACzB,KAAKsB,iBAAiBhB,MAAON,KAAKJ,iBAAiBU,QACrFoB,WAAa1B,KAAKU,KAAKiB,cAAcC,mBAAUC,QAAQH,+BACnDV,oBAAoBU,WAAYb,KAAMC,IAGtBd,KAAKU,KAAKiB,cAAcC,mBAAUC,QAAQX,aAClDY,kBAAoB,kBAAU,iBAAkB,YAAaxB,KAAKyB,UAMxFZ,8BACST,KAAKsB,iBAAiB,SAASC,MAAAA,UAC1BC,OAASC,EAAEC,OAAOC,QAAQT,mBAAUU,QAAQC,eAE9CL,OAAQ,QACalC,KAAKH,sBAAsBG,KAAKkB,cAEzCsB,cACHC,YAAYjC,SAAS0B,OAAOQ,QAAQC,kBACnC3C,KAAKiB,SAASjB,KAAKkB,kBAYzCuB,YAAYE,uBACH7C,kBAAoB6C,UAGrB3C,KAAKF,iBAAmB,OACnBA,iBAAmBE,KAAKL,SAASiD,OAAS,EACxC5C,KAAKF,iBAAmBE,KAAKL,SAASiD,OAAS,SACjD9C,iBAAmB,GAGrBE,KAAKF,iBASZoB,wBACO,IACAlB,KAAKL,SAASK,KAAKF,kBACtB+C,MAAO7C,KAAKL,SAASiD,OACrBE,aAAc9C,KAAKF,iBAAmB,GAS1CiD,sBACO/C,KAAKU,8BAaLuB,eACXe,MACApD,iBACAC,2BACAoD,cACIA,cAAgB,6DAChB,SAEEC,WAAa,IAAIzD,WAAWuD,MAAOpD,iBAAkBC,8BACvDoD,eACAC,WAAWhD,UAAU+C,qBAEnBC,WAAWnD,SAEVmD"} \ No newline at end of file diff --git a/mod/forum/amd/src/local/grades/grader.js b/mod/forum/amd/src/local/grades/grader.js index 5342da26275..6a763b8feed 100644 --- a/mod/forum/amd/src/local/grades/grader.js +++ b/mod/forum/amd/src/local/grades/grader.js @@ -27,7 +27,7 @@ import {createLayout as createFullScreenWindow} from 'mod_forum/local/layout/ful import getGradingPanelFunctions from './local/grader/gradingpanel'; import {add as addToast} from 'core/toast'; import {addNotification} from 'core/notification'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {failedUpdate} from 'core_grades/grades/grader/gradingpanel/normalise'; import {addIconToContainerWithPromise} from 'core/loadingicon'; import {debounce} from 'core/utils'; diff --git a/mod/forum/amd/src/local/grades/local/grader/user_picker.js b/mod/forum/amd/src/local/grades/local/grader/user_picker.js index 565b60212fa..6ec6fa3bd2b 100644 --- a/mod/forum/amd/src/local/grades/local/grader/user_picker.js +++ b/mod/forum/amd/src/local/grades/local/grader/user_picker.js @@ -23,7 +23,7 @@ import Templates from 'core/templates'; import Selectors from './user_picker/selectors'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; const templatePath = 'mod_forum/local/grades/local/grader'; diff --git a/mod/lti/amd/build/course_tools_list.min.js b/mod/lti/amd/build/course_tools_list.min.js index 097ff13542f..df23cfcb606 100644 --- a/mod/lti/amd/build/course_tools_list.min.js +++ b/mod/lti/amd/build/course_tools_list.min.js @@ -1,3 +1,3 @@ -define("mod_lti/course_tools_list",["exports","core/notification","core/pending","core/ajax","core/toast","core/str","core_table/dynamic","core_table/local/dynamic/selectors"],(function(_exports,_notification,_pending,_ajax,_toast,_str,_dynamic,Selectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_ajax=_interopRequireDefault(_ajax),Selectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Selectors);_exports.init=()=>{document.addEventListener("click",(event=>{const courseToolDelete=event.target.closest('[data-action="course-tool-delete"]');if(courseToolDelete){event.preventDefault();const deleteBodyStringId=courseToolDelete.dataset.courseToolUsage>0?"deletecoursetoolwithusageconfirm":"deletecoursetoolconfirm",requiredStrings=[{key:"deletecoursetool",component:"mod_lti",param:courseToolDelete.dataset.courseToolName},{key:deleteBodyStringId,component:"mod_lti",param:courseToolDelete.dataset.courseToolName},{key:"delete",component:"core",param:courseToolDelete.dataset.courseToolName},{key:"coursetooldeleted",component:"mod_lti",param:courseToolDelete.dataset.courseToolName}],triggerElement=courseToolDelete.closest(".dropdown").querySelector(".dropdown-toggle");(0,_str.get_strings)(requiredStrings).then((_ref=>{let[modalTitle,modalBody,deleteLabel]=_ref;return _notification.default.deleteCancelPromise(modalTitle,modalBody,deleteLabel,{triggerElement:triggerElement})})).then((()=>{const pendingPromise=new _pending.default("mod_lti/course_tools:delete"),request={methodname:"mod_lti_delete_course_tool_type",args:{tooltypeid:courseToolDelete.dataset.courseToolId}};return _ajax.default.call([request])[0].then((0,_toast.add)((0,_str.get_string)("coursetooldeleted","mod_lti",courseToolDelete.dataset.courseToolName))).then((()=>{const tableRoot=triggerElement.closest(Selectors.main.region);return(0,_dynamic.refreshTableContent)(tableRoot)})).then(pendingPromise.resolve).catch(_notification.default.exception)})).catch((()=>{}))}}))}})); +define("mod_lti/course_tools_list",["exports","core/notification","core/pending","core/ajax","core/toast","core/str","core_table/dynamic","core_table/local/dynamic/selectors"],(function(_exports,_notification,_pending,_ajax,_toast,_str,_dynamic,Selectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_ajax=_interopRequireDefault(_ajax),Selectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Selectors);_exports.init=()=>{document.addEventListener("click",(event=>{const courseToolDelete=event.target.closest('[data-action="course-tool-delete"]');if(courseToolDelete){event.preventDefault();const deleteBodyStringId=courseToolDelete.dataset.courseToolUsage>0?"deletecoursetoolwithusageconfirm":"deletecoursetoolconfirm",requiredStrings=[{key:"deletecoursetool",component:"mod_lti",param:courseToolDelete.dataset.courseToolName},{key:deleteBodyStringId,component:"mod_lti",param:courseToolDelete.dataset.courseToolName},{key:"delete",component:"core",param:courseToolDelete.dataset.courseToolName},{key:"coursetooldeleted",component:"mod_lti",param:courseToolDelete.dataset.courseToolName}],triggerElement=courseToolDelete.closest(".dropdown").querySelector(".dropdown-toggle");(0,_str.getStrings)(requiredStrings).then((_ref=>{let[modalTitle,modalBody,deleteLabel]=_ref;return _notification.default.deleteCancelPromise(modalTitle,modalBody,deleteLabel,{triggerElement:triggerElement})})).then((()=>{const pendingPromise=new _pending.default("mod_lti/course_tools:delete"),request={methodname:"mod_lti_delete_course_tool_type",args:{tooltypeid:courseToolDelete.dataset.courseToolId}};return _ajax.default.call([request])[0].then((0,_toast.add)((0,_str.getString)("coursetooldeleted","mod_lti",courseToolDelete.dataset.courseToolName))).then((()=>{const tableRoot=triggerElement.closest(Selectors.main.region);return(0,_dynamic.refreshTableContent)(tableRoot)})).then(pendingPromise.resolve).catch(_notification.default.exception)})).catch((()=>{}))}}))}})); //# sourceMappingURL=course_tools_list.min.js.map \ No newline at end of file diff --git a/mod/lti/amd/build/course_tools_list.min.js.map b/mod/lti/amd/build/course_tools_list.min.js.map index 9a7005049b9..e4ee0474cee 100644 --- a/mod/lti/amd/build/course_tools_list.min.js.map +++ b/mod/lti/amd/build/course_tools_list.min.js.map @@ -1 +1 @@ -{"version":3,"file":"course_tools_list.min.js","sources":["../src/course_tools_list.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 * Course LTI External tools list management.\n *\n * @module mod_lti/course_tools_list\n * @copyright 2023 Jake Dallimore \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Ajax from 'core/ajax';\nimport {add as addToast} from 'core/toast';\nimport {get_string as getString, get_strings as getStrings} from 'core/str';\nimport {refreshTableContent} from 'core_table/dynamic';\nimport * as Selectors from 'core_table/local/dynamic/selectors';\n\n/**\n * Initialise module.\n */\nexport const init = () => {\n document.addEventListener('click', event => {\n\n const courseToolDelete = event.target.closest('[data-action=\"course-tool-delete\"]');\n if (courseToolDelete) {\n event.preventDefault();\n\n // A different message is used in the modal if the tool has usages within the course.\n const usage = courseToolDelete.dataset.courseToolUsage;\n const deleteBodyStringId = usage > 0 ? 'deletecoursetoolwithusageconfirm' : 'deletecoursetoolconfirm';\n const requiredStrings = [\n {key: 'deletecoursetool', component: 'mod_lti', param: courseToolDelete.dataset.courseToolName},\n {key: deleteBodyStringId, component: 'mod_lti', param: courseToolDelete.dataset.courseToolName},\n {key: 'delete', component: 'core', param: courseToolDelete.dataset.courseToolName},\n {key: 'coursetooldeleted', component: 'mod_lti', param: courseToolDelete.dataset.courseToolName}\n ];\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = courseToolDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n\n getStrings(requiredStrings).then(([modalTitle, modalBody, deleteLabel]) => {\n return Notification.deleteCancelPromise(\n modalTitle,\n modalBody,\n deleteLabel,\n {triggerElement});\n }).then(() => {\n const pendingPromise = new Pending('mod_lti/course_tools:delete');\n\n const request = {\n methodname: 'mod_lti_delete_course_tool_type',\n args: {tooltypeid: courseToolDelete.dataset.courseToolId}\n };\n return Ajax.call([request])[0]\n .then(addToast(getString('coursetooldeleted', 'mod_lti', courseToolDelete.dataset.courseToolName)))\n .then(() => {\n const tableRoot = triggerElement.closest(Selectors.main.region);\n return refreshTableContent(tableRoot);\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n};\n"],"names":["document","addEventListener","event","courseToolDelete","target","closest","preventDefault","deleteBodyStringId","dataset","courseToolUsage","requiredStrings","key","component","param","courseToolName","triggerElement","querySelector","then","_ref","modalTitle","modalBody","deleteLabel","Notification","deleteCancelPromise","pendingPromise","Pending","request","methodname","args","tooltypeid","courseToolId","Ajax","call","tableRoot","Selectors","main","region","resolve","catch","exception"],"mappings":"o+CAoCoB,KAChBA,SAASC,iBAAiB,SAASC,cAEzBC,iBAAmBD,MAAME,OAAOC,QAAQ,yCAC1CF,iBAAkB,CAClBD,MAAMI,uBAIAC,mBADQJ,iBAAiBK,QAAQC,gBACJ,EAAI,mCAAqC,0BACtEC,gBAAkB,CACpB,CAACC,IAAK,mBAAoBC,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,gBAChF,CAACH,IAAKJ,mBAAoBK,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,gBAChF,CAACH,IAAK,SAAUC,UAAW,OAAQC,MAAOV,iBAAiBK,QAAQM,gBACnE,CAACH,IAAK,oBAAqBC,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,iBAG/EC,eAAiBZ,iBAAiBE,QAAQ,aAAaW,cAAc,yCAEhEN,iBAAiBO,MAAKC,WAAEC,WAAYC,UAAWC,yBAC/CC,sBAAaC,oBAChBJ,WACAC,UACAC,YACA,CAACN,eAAAA,oBACNE,MAAK,WACEO,eAAiB,IAAIC,iBAAQ,+BAE7BC,QAAU,CACZC,WAAY,kCACZC,KAAM,CAACC,WAAY1B,iBAAiBK,QAAQsB,sBAEzCC,cAAKC,KAAK,CAACN,UAAU,GACvBT,MAAK,eAAS,mBAAU,oBAAqB,UAAWd,iBAAiBK,QAAQM,kBACjFG,MAAK,WACIgB,UAAYlB,eAAeV,QAAQ6B,UAAUC,KAAKC,eACjD,gCAAoBH,cAE9BhB,KAAKO,eAAea,SACpBC,MAAMhB,sBAAaiB,cACzBD,OAAM"} \ No newline at end of file +{"version":3,"file":"course_tools_list.min.js","sources":["../src/course_tools_list.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 * Course LTI External tools list management.\n *\n * @module mod_lti/course_tools_list\n * @copyright 2023 Jake Dallimore \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Ajax from 'core/ajax';\nimport {add as addToast} from 'core/toast';\nimport {getString, getStrings} from 'core/str';\nimport {refreshTableContent} from 'core_table/dynamic';\nimport * as Selectors from 'core_table/local/dynamic/selectors';\n\n/**\n * Initialise module.\n */\nexport const init = () => {\n document.addEventListener('click', event => {\n\n const courseToolDelete = event.target.closest('[data-action=\"course-tool-delete\"]');\n if (courseToolDelete) {\n event.preventDefault();\n\n // A different message is used in the modal if the tool has usages within the course.\n const usage = courseToolDelete.dataset.courseToolUsage;\n const deleteBodyStringId = usage > 0 ? 'deletecoursetoolwithusageconfirm' : 'deletecoursetoolconfirm';\n const requiredStrings = [\n {key: 'deletecoursetool', component: 'mod_lti', param: courseToolDelete.dataset.courseToolName},\n {key: deleteBodyStringId, component: 'mod_lti', param: courseToolDelete.dataset.courseToolName},\n {key: 'delete', component: 'core', param: courseToolDelete.dataset.courseToolName},\n {key: 'coursetooldeleted', component: 'mod_lti', param: courseToolDelete.dataset.courseToolName}\n ];\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = courseToolDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n\n getStrings(requiredStrings).then(([modalTitle, modalBody, deleteLabel]) => {\n return Notification.deleteCancelPromise(\n modalTitle,\n modalBody,\n deleteLabel,\n {triggerElement});\n }).then(() => {\n const pendingPromise = new Pending('mod_lti/course_tools:delete');\n\n const request = {\n methodname: 'mod_lti_delete_course_tool_type',\n args: {tooltypeid: courseToolDelete.dataset.courseToolId}\n };\n return Ajax.call([request])[0]\n .then(addToast(getString('coursetooldeleted', 'mod_lti', courseToolDelete.dataset.courseToolName)))\n .then(() => {\n const tableRoot = triggerElement.closest(Selectors.main.region);\n return refreshTableContent(tableRoot);\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n};\n"],"names":["document","addEventListener","event","courseToolDelete","target","closest","preventDefault","deleteBodyStringId","dataset","courseToolUsage","requiredStrings","key","component","param","courseToolName","triggerElement","querySelector","then","_ref","modalTitle","modalBody","deleteLabel","Notification","deleteCancelPromise","pendingPromise","Pending","request","methodname","args","tooltypeid","courseToolId","Ajax","call","tableRoot","Selectors","main","region","resolve","catch","exception"],"mappings":"o+CAoCoB,KAChBA,SAASC,iBAAiB,SAASC,cAEzBC,iBAAmBD,MAAME,OAAOC,QAAQ,yCAC1CF,iBAAkB,CAClBD,MAAMI,uBAIAC,mBADQJ,iBAAiBK,QAAQC,gBACJ,EAAI,mCAAqC,0BACtEC,gBAAkB,CACpB,CAACC,IAAK,mBAAoBC,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,gBAChF,CAACH,IAAKJ,mBAAoBK,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,gBAChF,CAACH,IAAK,SAAUC,UAAW,OAAQC,MAAOV,iBAAiBK,QAAQM,gBACnE,CAACH,IAAK,oBAAqBC,UAAW,UAAWC,MAAOV,iBAAiBK,QAAQM,iBAG/EC,eAAiBZ,iBAAiBE,QAAQ,aAAaW,cAAc,wCAEhEN,iBAAiBO,MAAKC,WAAEC,WAAYC,UAAWC,yBAC/CC,sBAAaC,oBAChBJ,WACAC,UACAC,YACA,CAACN,eAAAA,oBACNE,MAAK,WACEO,eAAiB,IAAIC,iBAAQ,+BAE7BC,QAAU,CACZC,WAAY,kCACZC,KAAM,CAACC,WAAY1B,iBAAiBK,QAAQsB,sBAEzCC,cAAKC,KAAK,CAACN,UAAU,GACvBT,MAAK,eAAS,kBAAU,oBAAqB,UAAWd,iBAAiBK,QAAQM,kBACjFG,MAAK,WACIgB,UAAYlB,eAAeV,QAAQ6B,UAAUC,KAAKC,eACjD,gCAAoBH,cAE9BhB,KAAKO,eAAea,SACpBC,MAAMhB,sBAAaiB,cACzBD,OAAM"} \ No newline at end of file diff --git a/mod/lti/amd/src/course_tools_list.js b/mod/lti/amd/src/course_tools_list.js index 9c16e2a2ef3..da17abb72b2 100644 --- a/mod/lti/amd/src/course_tools_list.js +++ b/mod/lti/amd/src/course_tools_list.js @@ -27,7 +27,7 @@ import Notification from 'core/notification'; import Pending from 'core/pending'; import Ajax from 'core/ajax'; import {add as addToast} from 'core/toast'; -import {get_string as getString, get_strings as getStrings} from 'core/str'; +import {getString, getStrings} from 'core/str'; import {refreshTableContent} from 'core_table/dynamic'; import * as Selectors from 'core_table/local/dynamic/selectors'; diff --git a/mod/quiz/amd/build/reopen_attempt_ui.min.js b/mod/quiz/amd/build/reopen_attempt_ui.min.js index fa29ac3e053..dd8930cc9f8 100644 --- a/mod/quiz/amd/build/reopen_attempt_ui.min.js +++ b/mod/quiz/amd/build/reopen_attempt_ui.min.js @@ -11,6 +11,6 @@ define("mod_quiz/reopen_attempt_ui",["exports","core/notification","core/ajax"," * @copyright 2023 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -const reopenButtonClicked=async e=>{if(!(e.target instanceof HTMLElement&&e.target.matches('button[data-action="reopen-attempt"]')))return;e.preventDefault();const attemptId=e.target.dataset.attemptId;try{const messages=(0,_ajax.call)([{methodname:"mod_quiz_get_reopen_attempt_confirmation",args:{attemptid:attemptId}}]);await(0,_notification.saveCancelPromise)((0,_str.get_string)("reopenattemptareyousuretitle","mod_quiz"),messages[0],(0,_str.get_string)("reopenattempt","mod_quiz"),{triggerElement:e.target}),await(0,_ajax.call)([{methodname:"mod_quiz_reopen_attempt",args:{attemptid:attemptId}}])[0],window.location=M.cfg.wwwroot+e.target.dataset.afterActionUrl}catch(error){if("modal-save-cancel:cancel"===error.type)return;await(0,_notification.exception)(error)}};_exports.init=()=>{document.addEventListener("click",reopenButtonClicked)}})); +const reopenButtonClicked=async e=>{if(!(e.target instanceof HTMLElement&&e.target.matches('button[data-action="reopen-attempt"]')))return;e.preventDefault();const attemptId=e.target.dataset.attemptId;try{const messages=(0,_ajax.call)([{methodname:"mod_quiz_get_reopen_attempt_confirmation",args:{attemptid:attemptId}}]);await(0,_notification.saveCancelPromise)((0,_str.getString)("reopenattemptareyousuretitle","mod_quiz"),messages[0],(0,_str.getString)("reopenattempt","mod_quiz"),{triggerElement:e.target}),await(0,_ajax.call)([{methodname:"mod_quiz_reopen_attempt",args:{attemptid:attemptId}}])[0],window.location=M.cfg.wwwroot+e.target.dataset.afterActionUrl}catch(error){if("modal-save-cancel:cancel"===error.type)return;await(0,_notification.exception)(error)}};_exports.init=()=>{document.addEventListener("click",reopenButtonClicked)}})); //# sourceMappingURL=reopen_attempt_ui.min.js.map \ No newline at end of file diff --git a/mod/quiz/amd/build/reopen_attempt_ui.min.js.map b/mod/quiz/amd/build/reopen_attempt_ui.min.js.map index 17118d1c422..6c10e1831e0 100644 --- a/mod/quiz/amd/build/reopen_attempt_ui.min.js.map +++ b/mod/quiz/amd/build/reopen_attempt_ui.min.js.map @@ -1 +1 @@ -{"version":3,"file":"reopen_attempt_ui.min.js","sources":["../src/reopen_attempt_ui.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module has the code to make the Re-open attempt button work, if present.\n *\n * That is, it looks for buttons with HTML like\n * <button type=\"button\" data-action=\"reopen-attempt\" data-attempt-id=\"227000\" data-after-action-url=\"/mod/quiz/report.php\">\n * and if that is clicked, it first shows an 'Are you sure' pop-up, and if they are sure,\n * the attempt is re-opened, and then the page reloads.\n *\n * @module mod_quiz/reopen_attempt_ui\n * @copyright 2023 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {exception as displayException} from 'core/notification';\nimport {call as fetchMany} from 'core/ajax';\nimport {get_string as getString} from 'core/str';\nimport {saveCancelPromise} from 'core/notification';\n\n/**\n * Handle a click if it is on one of our buttons.\n *\n * @param {MouseEvent} e the click event.\n */\nconst reopenButtonClicked = async(e) => {\n if (!(e.target instanceof HTMLElement) || !e.target.matches('button[data-action=\"reopen-attempt\"]')) {\n return;\n }\n\n e.preventDefault();\n const attemptId = e.target.dataset.attemptId;\n\n try {\n // We fetch the confirmation message from the server now, so the message is based\n // on the latest state of the attempt, rather than when the containing page loaded.\n const messages = fetchMany([{\n methodname: 'mod_quiz_get_reopen_attempt_confirmation',\n args: {\n \"attemptid\": attemptId,\n },\n }]);\n\n await saveCancelPromise(\n getString('reopenattemptareyousuretitle', 'mod_quiz'),\n messages[0],\n getString('reopenattempt', 'mod_quiz'),\n {triggerElement: e.target},\n );\n\n await (fetchMany([{\n methodname: 'mod_quiz_reopen_attempt',\n args: {\n \"attemptid\": attemptId,\n },\n }])[0]);\n window.location = M.cfg.wwwroot + e.target.dataset.afterActionUrl;\n\n } catch (error) {\n if (error.type === 'modal-save-cancel:cancel') {\n // User clicked Cancel, so do nothing.\n return;\n }\n await displayException(error);\n }\n};\n\nexport const init = () => {\n document.addEventListener('click', reopenButtonClicked);\n};\n"],"names":["reopenButtonClicked","async","e","target","HTMLElement","matches","preventDefault","attemptId","dataset","messages","methodname","args","triggerElement","window","location","M","cfg","wwwroot","afterActionUrl","error","type","document","addEventListener"],"mappings":";;;;;;;;;;;;;MAsCMA,oBAAsBC,MAAAA,SAClBC,EAAEC,kBAAkBC,aAAiBF,EAAEC,OAAOE,QAAQ,gDAI5DH,EAAEI,uBACIC,UAAYL,EAAEC,OAAOK,QAAQD,oBAKzBE,UAAW,cAAU,CAAC,CACxBC,WAAY,2CACZC,KAAM,WACWJ,oBAIf,oCACF,mBAAU,+BAAgC,YAC1CE,SAAS,IACT,mBAAU,gBAAiB,YAC3B,CAACG,eAAgBV,EAAEC,eAGhB,cAAU,CAAC,CACdO,WAAY,0BACZC,KAAM,WACWJ,cAEjB,GACJM,OAAOC,SAAWC,EAAEC,IAAIC,QAAUf,EAAEC,OAAOK,QAAQU,eAErD,MAAOC,UACc,6BAAfA,MAAMC,kBAIJ,2BAAiBD,uBAIX,KAChBE,SAASC,iBAAiB,QAAStB"} \ No newline at end of file +{"version":3,"file":"reopen_attempt_ui.min.js","sources":["../src/reopen_attempt_ui.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module has the code to make the Re-open attempt button work, if present.\n *\n * That is, it looks for buttons with HTML like\n * <button type=\"button\" data-action=\"reopen-attempt\" data-attempt-id=\"227000\" data-after-action-url=\"/mod/quiz/report.php\">\n * and if that is clicked, it first shows an 'Are you sure' pop-up, and if they are sure,\n * the attempt is re-opened, and then the page reloads.\n *\n * @module mod_quiz/reopen_attempt_ui\n * @copyright 2023 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {exception as displayException} from 'core/notification';\nimport {call as fetchMany} from 'core/ajax';\nimport {getString} from 'core/str';\nimport {saveCancelPromise} from 'core/notification';\n\n/**\n * Handle a click if it is on one of our buttons.\n *\n * @param {MouseEvent} e the click event.\n */\nconst reopenButtonClicked = async(e) => {\n if (!(e.target instanceof HTMLElement) || !e.target.matches('button[data-action=\"reopen-attempt\"]')) {\n return;\n }\n\n e.preventDefault();\n const attemptId = e.target.dataset.attemptId;\n\n try {\n // We fetch the confirmation message from the server now, so the message is based\n // on the latest state of the attempt, rather than when the containing page loaded.\n const messages = fetchMany([{\n methodname: 'mod_quiz_get_reopen_attempt_confirmation',\n args: {\n \"attemptid\": attemptId,\n },\n }]);\n\n await saveCancelPromise(\n getString('reopenattemptareyousuretitle', 'mod_quiz'),\n messages[0],\n getString('reopenattempt', 'mod_quiz'),\n {triggerElement: e.target},\n );\n\n await (fetchMany([{\n methodname: 'mod_quiz_reopen_attempt',\n args: {\n \"attemptid\": attemptId,\n },\n }])[0]);\n window.location = M.cfg.wwwroot + e.target.dataset.afterActionUrl;\n\n } catch (error) {\n if (error.type === 'modal-save-cancel:cancel') {\n // User clicked Cancel, so do nothing.\n return;\n }\n await displayException(error);\n }\n};\n\nexport const init = () => {\n document.addEventListener('click', reopenButtonClicked);\n};\n"],"names":["reopenButtonClicked","async","e","target","HTMLElement","matches","preventDefault","attemptId","dataset","messages","methodname","args","triggerElement","window","location","M","cfg","wwwroot","afterActionUrl","error","type","document","addEventListener"],"mappings":";;;;;;;;;;;;;MAsCMA,oBAAsBC,MAAAA,SAClBC,EAAEC,kBAAkBC,aAAiBF,EAAEC,OAAOE,QAAQ,gDAI5DH,EAAEI,uBACIC,UAAYL,EAAEC,OAAOK,QAAQD,oBAKzBE,UAAW,cAAU,CAAC,CACxBC,WAAY,2CACZC,KAAM,WACWJ,oBAIf,oCACF,kBAAU,+BAAgC,YAC1CE,SAAS,IACT,kBAAU,gBAAiB,YAC3B,CAACG,eAAgBV,EAAEC,eAGhB,cAAU,CAAC,CACdO,WAAY,0BACZC,KAAM,WACWJ,cAEjB,GACJM,OAAOC,SAAWC,EAAEC,IAAIC,QAAUf,EAAEC,OAAOK,QAAQU,eAErD,MAAOC,UACc,6BAAfA,MAAMC,kBAIJ,2BAAiBD,uBAIX,KAChBE,SAASC,iBAAiB,QAAStB"} \ No newline at end of file diff --git a/mod/quiz/amd/build/submission_confirmation.min.js b/mod/quiz/amd/build/submission_confirmation.min.js index 6ca80085c7d..8e2b4aa588f 100644 --- a/mod/quiz/amd/build/submission_confirmation.min.js +++ b/mod/quiz/amd/build/submission_confirmation.min.js @@ -6,6 +6,6 @@ define("mod_quiz/submission_confirmation",["exports","core/notification","core/p * @copyright 2022 Huong Nguyen * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 4.1 - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_prefetch=_interopRequireDefault(_prefetch),_templates=_interopRequireDefault(_templates);const SELECTOR_attemptSubmitButton=".path-mod-quiz .btn-finishattempt button",SELECTOR_attemptSubmitForm="form#frm-finishattempt",TEMPLATES_submissionConfirmation="mod_quiz/submission_confirmation";_exports.init=unAnsweredQuestions=>{_prefetch.default.prefetchStrings("core",["submit"]),_prefetch.default.prefetchStrings("core_admin",["confirmation"]),_prefetch.default.prefetchStrings("quiz",["submitallandfinish","submission_confirmation"]),_prefetch.default.prefetchTemplate(TEMPLATES_submissionConfirmation),(unAnsweredQuestions=>{const submitAction=document.querySelector(SELECTOR_attemptSubmitButton);submitAction&&submitAction.addEventListener("click",(async e=>{e.preventDefault();try{await(0,_notification.saveCancelPromise)((0,_str.get_string)("submission_confirmation","quiz"),_templates.default.render(TEMPLATES_submissionConfirmation,{hasunanswered:unAnsweredQuestions>0,totalunanswered:unAnsweredQuestions}),(0,_str.get_string)("submitallandfinish","quiz")),submitAction.closest(SELECTOR_attemptSubmitForm).submit()}catch{return}}))})(unAnsweredQuestions)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_prefetch=_interopRequireDefault(_prefetch),_templates=_interopRequireDefault(_templates);const SELECTOR_attemptSubmitButton=".path-mod-quiz .btn-finishattempt button",SELECTOR_attemptSubmitForm="form#frm-finishattempt",TEMPLATES_submissionConfirmation="mod_quiz/submission_confirmation";_exports.init=unAnsweredQuestions=>{_prefetch.default.prefetchStrings("core",["submit"]),_prefetch.default.prefetchStrings("core_admin",["confirmation"]),_prefetch.default.prefetchStrings("quiz",["submitallandfinish","submission_confirmation"]),_prefetch.default.prefetchTemplate(TEMPLATES_submissionConfirmation),(unAnsweredQuestions=>{const submitAction=document.querySelector(SELECTOR_attemptSubmitButton);submitAction&&submitAction.addEventListener("click",(async e=>{e.preventDefault();try{await(0,_notification.saveCancelPromise)((0,_str.getString)("submission_confirmation","quiz"),_templates.default.render(TEMPLATES_submissionConfirmation,{hasunanswered:unAnsweredQuestions>0,totalunanswered:unAnsweredQuestions}),(0,_str.getString)("submitallandfinish","quiz")),submitAction.closest(SELECTOR_attemptSubmitForm).submit()}catch{return}}))})(unAnsweredQuestions)}})); //# sourceMappingURL=submission_confirmation.min.js.map \ No newline at end of file diff --git a/mod/quiz/amd/build/submission_confirmation.min.js.map b/mod/quiz/amd/build/submission_confirmation.min.js.map index b6573302051..031244a7003 100644 --- a/mod/quiz/amd/build/submission_confirmation.min.js.map +++ b/mod/quiz/amd/build/submission_confirmation.min.js.map @@ -1 +1 @@ -{"version":3,"file":"submission_confirmation.min.js","sources":["../src/submission_confirmation.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 * A javascript module to handle submission confirmation for quiz.\n *\n * @module mod_quiz/submission_confirmation\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.1\n */\n\nimport {saveCancelPromise} from 'core/notification';\nimport Prefetch from 'core/prefetch';\nimport Templates from 'core/templates';\nimport {get_string as getString} from 'core/str';\n\nconst SELECTOR = {\n attemptSubmitButton: '.path-mod-quiz .btn-finishattempt button',\n attemptSubmitForm: 'form#frm-finishattempt',\n};\n\nconst TEMPLATES = {\n submissionConfirmation: 'mod_quiz/submission_confirmation',\n};\n\n/**\n * Register events for attempt submit button.\n * @param {int} unAnsweredQuestions Total number of un-answered questions\n */\nconst registerEventListeners = (unAnsweredQuestions) => {\n const submitAction = document.querySelector(SELECTOR.attemptSubmitButton);\n if (submitAction) {\n submitAction.addEventListener('click', async(e) => {\n e.preventDefault();\n try {\n await saveCancelPromise(\n getString('submission_confirmation', 'quiz'),\n Templates.render(TEMPLATES.submissionConfirmation, {\n hasunanswered: unAnsweredQuestions > 0,\n totalunanswered: unAnsweredQuestions\n }),\n getString('submitallandfinish', 'quiz')\n );\n\n // Save pressed.\n submitAction.closest(SELECTOR.attemptSubmitForm).submit();\n } catch {\n // Cancel pressed.\n return;\n }\n });\n }\n};\n\n/**\n * Initialises.\n * @param {int} unAnsweredQuestions Total number of unanswered questions\n */\nexport const init = (unAnsweredQuestions) => {\n Prefetch.prefetchStrings('core', ['submit']);\n Prefetch.prefetchStrings('core_admin', ['confirmation']);\n Prefetch.prefetchStrings('quiz', ['submitallandfinish', 'submission_confirmation']);\n Prefetch.prefetchTemplate(TEMPLATES.submissionConfirmation);\n registerEventListeners(unAnsweredQuestions);\n};\n"],"names":["SELECTOR","TEMPLATES","unAnsweredQuestions","prefetchStrings","prefetchTemplate","submitAction","document","querySelector","addEventListener","async","e","preventDefault","Templates","render","hasunanswered","totalunanswered","closest","submit","registerEventListeners"],"mappings":";;;;;;;;kLA6BMA,6BACmB,2CADnBA,2BAEiB,yBAGjBC,iCACsB,iDAoCPC,wCACRC,gBAAgB,OAAQ,CAAC,6BACzBA,gBAAgB,aAAc,CAAC,mCAC/BA,gBAAgB,OAAQ,CAAC,qBAAsB,8CAC/CC,iBAAiBH,kCAjCEC,CAAAA,4BACtBG,aAAeC,SAASC,cAAcP,8BACxCK,cACAA,aAAaG,iBAAiB,SAASC,MAAAA,IACnCC,EAAEC,2BAEQ,oCACF,mBAAU,0BAA2B,QACrCC,mBAAUC,OAAOZ,iCAAkC,CAC/Ca,cAAeZ,oBAAsB,EACrCa,gBAAiBb,uBAErB,mBAAU,qBAAsB,SAIpCG,aAAaW,QAAQhB,4BAA4BiB,SACnD,kBAiBVC,CAAuBhB"} \ No newline at end of file +{"version":3,"file":"submission_confirmation.min.js","sources":["../src/submission_confirmation.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 * A javascript module to handle submission confirmation for quiz.\n *\n * @module mod_quiz/submission_confirmation\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.1\n */\n\nimport {saveCancelPromise} from 'core/notification';\nimport Prefetch from 'core/prefetch';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\n\nconst SELECTOR = {\n attemptSubmitButton: '.path-mod-quiz .btn-finishattempt button',\n attemptSubmitForm: 'form#frm-finishattempt',\n};\n\nconst TEMPLATES = {\n submissionConfirmation: 'mod_quiz/submission_confirmation',\n};\n\n/**\n * Register events for attempt submit button.\n * @param {int} unAnsweredQuestions Total number of un-answered questions\n */\nconst registerEventListeners = (unAnsweredQuestions) => {\n const submitAction = document.querySelector(SELECTOR.attemptSubmitButton);\n if (submitAction) {\n submitAction.addEventListener('click', async(e) => {\n e.preventDefault();\n try {\n await saveCancelPromise(\n getString('submission_confirmation', 'quiz'),\n Templates.render(TEMPLATES.submissionConfirmation, {\n hasunanswered: unAnsweredQuestions > 0,\n totalunanswered: unAnsweredQuestions\n }),\n getString('submitallandfinish', 'quiz')\n );\n\n // Save pressed.\n submitAction.closest(SELECTOR.attemptSubmitForm).submit();\n } catch {\n // Cancel pressed.\n return;\n }\n });\n }\n};\n\n/**\n * Initialises.\n * @param {int} unAnsweredQuestions Total number of unanswered questions\n */\nexport const init = (unAnsweredQuestions) => {\n Prefetch.prefetchStrings('core', ['submit']);\n Prefetch.prefetchStrings('core_admin', ['confirmation']);\n Prefetch.prefetchStrings('quiz', ['submitallandfinish', 'submission_confirmation']);\n Prefetch.prefetchTemplate(TEMPLATES.submissionConfirmation);\n registerEventListeners(unAnsweredQuestions);\n};\n"],"names":["SELECTOR","TEMPLATES","unAnsweredQuestions","prefetchStrings","prefetchTemplate","submitAction","document","querySelector","addEventListener","async","e","preventDefault","Templates","render","hasunanswered","totalunanswered","closest","submit","registerEventListeners"],"mappings":";;;;;;;;kLA6BMA,6BACmB,2CADnBA,2BAEiB,yBAGjBC,iCACsB,iDAoCPC,wCACRC,gBAAgB,OAAQ,CAAC,6BACzBA,gBAAgB,aAAc,CAAC,mCAC/BA,gBAAgB,OAAQ,CAAC,qBAAsB,8CAC/CC,iBAAiBH,kCAjCEC,CAAAA,4BACtBG,aAAeC,SAASC,cAAcP,8BACxCK,cACAA,aAAaG,iBAAiB,SAASC,MAAAA,IACnCC,EAAEC,2BAEQ,oCACF,kBAAU,0BAA2B,QACrCC,mBAAUC,OAAOZ,iCAAkC,CAC/Ca,cAAeZ,oBAAsB,EACrCa,gBAAiBb,uBAErB,kBAAU,qBAAsB,SAIpCG,aAAaW,QAAQhB,4BAA4BiB,SACnD,kBAiBVC,CAAuBhB"} \ No newline at end of file diff --git a/mod/quiz/amd/src/reopen_attempt_ui.js b/mod/quiz/amd/src/reopen_attempt_ui.js index a3517564c75..b78f14549cb 100644 --- a/mod/quiz/amd/src/reopen_attempt_ui.js +++ b/mod/quiz/amd/src/reopen_attempt_ui.js @@ -28,7 +28,7 @@ import {exception as displayException} from 'core/notification'; import {call as fetchMany} from 'core/ajax'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {saveCancelPromise} from 'core/notification'; /** diff --git a/mod/quiz/amd/src/submission_confirmation.js b/mod/quiz/amd/src/submission_confirmation.js index 967cf2ef62d..8b23e9135f6 100644 --- a/mod/quiz/amd/src/submission_confirmation.js +++ b/mod/quiz/amd/src/submission_confirmation.js @@ -25,7 +25,7 @@ import {saveCancelPromise} from 'core/notification'; import Prefetch from 'core/prefetch'; import Templates from 'core/templates'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; const SELECTOR = { attemptSubmitButton: '.path-mod-quiz .btn-finishattempt button', diff --git a/payment/amd/build/gateways_modal.min.js b/payment/amd/build/gateways_modal.min.js index d6da95cae7e..153d180d6b3 100644 --- a/payment/amd/build/gateways_modal.min.js +++ b/payment/amd/build/gateways_modal.min.js @@ -1,3 +1,3 @@ -define("core_payment/gateways_modal",["exports","core/modal_factory","core/templates","core/str","./repository","./selectors","core/modal_events","core_payment/events","core/toast","core/notification","./modal_gateways"],(function(_exports,_modal_factory,_templates,_str,_repository,_selectors,_modal_events,_events,_toast,_notification,_modal_gateways){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modal_factory=_interopRequireDefault(_modal_factory),_templates=_interopRequireDefault(_templates),_selectors=_interopRequireDefault(_selectors),_modal_events=_interopRequireDefault(_modal_events),_events=_interopRequireDefault(_events),_notification=_interopRequireDefault(_notification),_modal_gateways=_interopRequireDefault(_modal_gateways);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const show=async function(rootNode){let{focusOnClose:focusOnClose=null}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const modal=await _modal_factory.default.create({type:_modal_gateways.default.TYPE,title:await(0,_str.get_string)("selectpaymenttype","core_payment"),body:await _templates.default.render("core_payment/gateways_modal",{})}),rootElement=modal.getRoot()[0];(0,_toast.addToastRegion)(rootElement),modal.show(),modal.getRoot().on(_modal_events.default.hidden,(()=>{modal.destroy();try{focusOnClose.focus()}catch(e){}})),modal.getRoot().on(_events.default.proceed,(e=>{const gateway=(rootElement.querySelector(_selectors.default.values.gateway)||{value:""}).value;gateway?processPayment(gateway,rootNode.dataset.component,rootNode.dataset.paymentarea,rootNode.dataset.itemid,rootNode.dataset.description).then((message=>(modal.hide(),_notification.default.addNotification({message:message,type:"success"}),location.href=rootNode.dataset.successurl,message))).catch((message=>_notification.default.alert("",message))):(0,_str.get_string)("nogatewayselected","core_payment").then((message=>(0,_toast.add)(message,{type:"warning"}))).catch(),e.preventDefault()})),rootElement.addEventListener("change",(e=>{e.target.matches(_selectors.default.elements.gateways)&&updateCostRegion(rootElement,rootNode.dataset.cost)}));const gateways=await(0,_repository.getAvailableGateways)(rootNode.dataset.component,rootNode.dataset.paymentarea,rootNode.dataset.itemid),context={gateways:gateways},{html:html,js:js}=await _templates.default.renderForPromise("core_payment/gateways",context);_templates.default.replaceNodeContents(rootElement.querySelector(_selectors.default.regions.gatewaysContainer),html,js),selectSingleGateway(rootElement),await updateCostRegion(rootElement,rootNode.dataset.cost)},selectSingleGateway=root=>{const gateways=root.querySelectorAll(_selectors.default.elements.gateways);1==gateways.length&&(gateways[0].checked=!0)},updateCostRegion=async function(root){let defaultCost=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";const gatewayElement=root.querySelector(_selectors.default.values.gateway),surcharge=parseInt((gatewayElement||{dataset:{surcharge:0}}).dataset.surcharge),cost=(gatewayElement||{dataset:{cost:defaultCost}}).dataset.cost,{html:html,js:js}=await _templates.default.renderForPromise("core_payment/fee_breakdown",{fee:cost,surcharge:surcharge});_templates.default.replaceNodeContents(root.querySelector(_selectors.default.regions.costContainer),html,js)},processPayment=async(gateway,component,paymentArea,itemId,description)=>(await("function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require(["paygw_".concat(gateway,"/gateways_modal")],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require("paygw_".concat(gateway,"/gateways_modal"))):Promise.resolve(_systemImportTransformerGlobalIdentifier["paygw_".concat(gateway,"/gateways_modal")]))).process(component,paymentArea,itemId,description),init=()=>{init.initialised||(init.initialised=!0,document.addEventListener("click",(e=>{const gatewayTrigger=e.target.closest('[data-action="core_payment/triggerPayment"]');gatewayTrigger&&(e.preventDefault(),show(gatewayTrigger,{focusOnClose:e.target}))})))};_exports.init=init,init.initialised=!1})); +define("core_payment/gateways_modal",["exports","core/modal_factory","core/templates","core/str","./repository","./selectors","core/modal_events","core_payment/events","core/toast","core/notification","./modal_gateways"],(function(_exports,_modal_factory,_templates,_str,_repository,_selectors,_modal_events,_events,_toast,_notification,_modal_gateways){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modal_factory=_interopRequireDefault(_modal_factory),_templates=_interopRequireDefault(_templates),_selectors=_interopRequireDefault(_selectors),_modal_events=_interopRequireDefault(_modal_events),_events=_interopRequireDefault(_events),_notification=_interopRequireDefault(_notification),_modal_gateways=_interopRequireDefault(_modal_gateways);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const show=async function(rootNode){let{focusOnClose:focusOnClose=null}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const modal=await _modal_factory.default.create({type:_modal_gateways.default.TYPE,title:await(0,_str.getString)("selectpaymenttype","core_payment"),body:await _templates.default.render("core_payment/gateways_modal",{})}),rootElement=modal.getRoot()[0];(0,_toast.addToastRegion)(rootElement),modal.show(),modal.getRoot().on(_modal_events.default.hidden,(()=>{modal.destroy();try{focusOnClose.focus()}catch(e){}})),modal.getRoot().on(_events.default.proceed,(e=>{const gateway=(rootElement.querySelector(_selectors.default.values.gateway)||{value:""}).value;gateway?processPayment(gateway,rootNode.dataset.component,rootNode.dataset.paymentarea,rootNode.dataset.itemid,rootNode.dataset.description).then((message=>(modal.hide(),_notification.default.addNotification({message:message,type:"success"}),location.href=rootNode.dataset.successurl,message))).catch((message=>_notification.default.alert("",message))):(0,_str.getString)("nogatewayselected","core_payment").then((message=>(0,_toast.add)(message,{type:"warning"}))).catch(),e.preventDefault()})),rootElement.addEventListener("change",(e=>{e.target.matches(_selectors.default.elements.gateways)&&updateCostRegion(rootElement,rootNode.dataset.cost)}));const gateways=await(0,_repository.getAvailableGateways)(rootNode.dataset.component,rootNode.dataset.paymentarea,rootNode.dataset.itemid),context={gateways:gateways},{html:html,js:js}=await _templates.default.renderForPromise("core_payment/gateways",context);_templates.default.replaceNodeContents(rootElement.querySelector(_selectors.default.regions.gatewaysContainer),html,js),selectSingleGateway(rootElement),await updateCostRegion(rootElement,rootNode.dataset.cost)},selectSingleGateway=root=>{const gateways=root.querySelectorAll(_selectors.default.elements.gateways);1==gateways.length&&(gateways[0].checked=!0)},updateCostRegion=async function(root){let defaultCost=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";const gatewayElement=root.querySelector(_selectors.default.values.gateway),surcharge=parseInt((gatewayElement||{dataset:{surcharge:0}}).dataset.surcharge),cost=(gatewayElement||{dataset:{cost:defaultCost}}).dataset.cost,{html:html,js:js}=await _templates.default.renderForPromise("core_payment/fee_breakdown",{fee:cost,surcharge:surcharge});_templates.default.replaceNodeContents(root.querySelector(_selectors.default.regions.costContainer),html,js)},processPayment=async(gateway,component,paymentArea,itemId,description)=>(await("function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require(["paygw_".concat(gateway,"/gateways_modal")],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require("paygw_".concat(gateway,"/gateways_modal"))):Promise.resolve(_systemImportTransformerGlobalIdentifier["paygw_".concat(gateway,"/gateways_modal")]))).process(component,paymentArea,itemId,description),init=()=>{init.initialised||(init.initialised=!0,document.addEventListener("click",(e=>{const gatewayTrigger=e.target.closest('[data-action="core_payment/triggerPayment"]');gatewayTrigger&&(e.preventDefault(),show(gatewayTrigger,{focusOnClose:e.target}))})))};_exports.init=init,init.initialised=!1})); //# sourceMappingURL=gateways_modal.min.js.map \ No newline at end of file diff --git a/payment/amd/build/gateways_modal.min.js.map b/payment/amd/build/gateways_modal.min.js.map index 76cb24a06f6..927dcba5f14 100644 --- a/payment/amd/build/gateways_modal.min.js.map +++ b/payment/amd/build/gateways_modal.min.js.map @@ -1 +1 @@ -{"version":3,"file":"gateways_modal.min.js","sources":["../src/gateways_modal.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 * Contain the logic for the gateways modal.\n *\n * @module core_payment/gateways_modal\n * @copyright 2019 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalFactory from 'core/modal_factory';\nimport Templates from 'core/templates';\nimport {get_string as getString} from 'core/str';\nimport {getAvailableGateways} from './repository';\nimport Selectors from './selectors';\nimport ModalEvents from 'core/modal_events';\nimport PaymentEvents from 'core_payment/events';\nimport {add as addToast, addToastRegion} from 'core/toast';\nimport Notification from 'core/notification';\nimport ModalGateways from './modal_gateways';\n\n/**\n * Register event listeners for the module.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', e => {\n const gatewayTrigger = e.target.closest('[data-action=\"core_payment/triggerPayment\"]');\n if (gatewayTrigger) {\n e.preventDefault();\n\n show(gatewayTrigger, {focusOnClose: e.target});\n }\n });\n};\n\n/**\n * Shows the gateway selector modal.\n *\n * @param {HTMLElement} rootNode\n * @param {Object} options - Additional options\n * @param {HTMLElement} options.focusOnClose The element to focus on when the modal is closed.\n */\nconst show = async(rootNode, {\n focusOnClose = null,\n} = {}) => {\n const modal = await ModalFactory.create({\n type: ModalGateways.TYPE,\n title: await getString('selectpaymenttype', 'core_payment'),\n body: await Templates.render('core_payment/gateways_modal', {}),\n });\n\n const rootElement = modal.getRoot()[0];\n addToastRegion(rootElement);\n\n modal.show();\n\n modal.getRoot().on(ModalEvents.hidden, () => {\n // Destroy when hidden.\n modal.destroy();\n try {\n focusOnClose.focus();\n } catch (e) {\n // eslint-disable-line\n }\n });\n\n modal.getRoot().on(PaymentEvents.proceed, (e) => {\n const gateway = (rootElement.querySelector(Selectors.values.gateway) || {value: ''}).value;\n\n if (gateway) {\n processPayment(\n gateway,\n rootNode.dataset.component,\n rootNode.dataset.paymentarea,\n rootNode.dataset.itemid,\n rootNode.dataset.description\n )\n .then(message => {\n modal.hide();\n Notification.addNotification({\n message: message,\n type: 'success',\n });\n location.href = rootNode.dataset.successurl;\n\n // The following return statement is never reached. It is put here just to make eslint happy.\n return message;\n })\n .catch(message => Notification.alert('', message));\n } else {\n // We cannot use await in the following line.\n // The reason is that we are preventing the default action of the save event being triggered,\n // therefore we cannot define the event handler function asynchronous.\n getString('nogatewayselected', 'core_payment').then(message => addToast(message, {type: 'warning'})).catch();\n }\n\n e.preventDefault();\n });\n\n // Re-calculate the cost when gateway is changed.\n rootElement.addEventListener('change', e => {\n if (e.target.matches(Selectors.elements.gateways)) {\n updateCostRegion(rootElement, rootNode.dataset.cost);\n }\n });\n\n const gateways = await getAvailableGateways(rootNode.dataset.component, rootNode.dataset.paymentarea, rootNode.dataset.itemid);\n const context = {\n gateways\n };\n\n const {html, js} = await Templates.renderForPromise('core_payment/gateways', context);\n Templates.replaceNodeContents(rootElement.querySelector(Selectors.regions.gatewaysContainer), html, js);\n selectSingleGateway(rootElement);\n await updateCostRegion(rootElement, rootNode.dataset.cost);\n};\n\n/**\n * Auto-select the gateway if there is only one gateway.\n *\n * @param {HTMLElement} root An HTMLElement that contains the cost region\n */\nconst selectSingleGateway = root => {\n const gateways = root.querySelectorAll(Selectors.elements.gateways);\n\n if (gateways.length == 1) {\n gateways[0].checked = true;\n }\n};\n\n/**\n * Shows the cost of the item the user is purchasing in the cost region.\n *\n * @param {HTMLElement} root An HTMLElement that contains the cost region\n * @param {string} defaultCost The default cost that is going to be displayed if no gateway is selected\n * @returns {Promise}\n */\nconst updateCostRegion = async(root, defaultCost = '') => {\n const gatewayElement = root.querySelector(Selectors.values.gateway);\n const surcharge = parseInt((gatewayElement || {dataset: {surcharge: 0}}).dataset.surcharge);\n const cost = (gatewayElement || {dataset: {cost: defaultCost}}).dataset.cost;\n\n const {html, js} = await Templates.renderForPromise('core_payment/fee_breakdown', {fee: cost, surcharge});\n Templates.replaceNodeContents(root.querySelector(Selectors.regions.costContainer), html, js);\n};\n\n/**\n * Process payment using the selected gateway.\n *\n * @param {string} gateway The gateway to be used for payment\n * @param {string} component Name of the component that the itemId belongs to\n * @param {string} paymentArea Name of the area in the component that the itemId belongs to\n * @param {number} itemId An internal identifier that is used by the component\n * @param {string} description Description of the payment\n * @returns {Promise}\n */\nconst processPayment = async(gateway, component, paymentArea, itemId, description) => {\n const paymentMethod = await import(`paygw_${gateway}/gateways_modal`);\n return paymentMethod.process(component, paymentArea, itemId, description);\n};\n\n/**\n * Set up the payment actions.\n */\nexport const init = () => {\n if (!init.initialised) {\n // Event listeners should only be registered once.\n init.initialised = true;\n registerEventListeners();\n }\n};\n\n/**\n * Whether the init function was called before.\n *\n * @static\n * @type {boolean}\n */\ninit.initialised = false;\n"],"names":["show","async","rootNode","focusOnClose","modal","ModalFactory","create","type","ModalGateways","TYPE","title","body","Templates","render","rootElement","getRoot","on","ModalEvents","hidden","destroy","focus","e","PaymentEvents","proceed","gateway","querySelector","Selectors","values","value","processPayment","dataset","component","paymentarea","itemid","description","then","message","hide","addNotification","location","href","successurl","catch","Notification","alert","preventDefault","addEventListener","target","matches","elements","gateways","updateCostRegion","cost","context","html","js","renderForPromise","replaceNodeContents","regions","gatewaysContainer","selectSingleGateway","root","querySelectorAll","length","checked","defaultCost","gatewayElement","surcharge","parseInt","fee","costContainer","paymentArea","itemId","process","init","initialised","document","gatewayTrigger","closest"],"mappings":"m/BAuDMA,KAAOC,eAAMC,cAAUC,aACzBA,aAAe,6DACf,SACMC,YAAcC,uBAAaC,OAAO,CACpCC,KAAMC,wBAAcC,KACpBC,YAAa,mBAAU,oBAAqB,gBAC5CC,WAAYC,mBAAUC,OAAO,8BAA+B,MAG1DC,YAAcV,MAAMW,UAAU,6BACrBD,aAEfV,MAAMJ,OAENI,MAAMW,UAAUC,GAAGC,sBAAYC,QAAQ,KAEnCd,MAAMe,cAEFhB,aAAaiB,QACf,MAAOC,QAKbjB,MAAMW,UAAUC,GAAGM,gBAAcC,SAAUF,UACjCG,SAAWV,YAAYW,cAAcC,mBAAUC,OAAOH,UAAY,CAACI,MAAO,KAAKA,MAEjFJ,QACAK,eACIL,QACAtB,SAAS4B,QAAQC,UACjB7B,SAAS4B,QAAQE,YACjB9B,SAAS4B,QAAQG,OACjB/B,SAAS4B,QAAQI,aAEpBC,MAAKC,UACFhC,MAAMiC,6BACOC,gBAAgB,CACzBF,QAASA,QACT7B,KAAM,YAEVgC,SAASC,KAAOtC,SAAS4B,QAAQW,WAG1BL,WAEVM,OAAMN,SAAWO,sBAAaC,MAAM,GAAIR,+BAK/B,oBAAqB,gBAAgBD,MAAKC,UAAW,cAASA,QAAS,CAAC7B,KAAM,cAAamC,QAGzGrB,EAAEwB,oBAIN/B,YAAYgC,iBAAiB,UAAUzB,IAC/BA,EAAE0B,OAAOC,QAAQtB,mBAAUuB,SAASC,WACpCC,iBAAiBrC,YAAaZ,SAAS4B,QAAQsB,eAIjDF,eAAiB,oCAAqBhD,SAAS4B,QAAQC,UAAW7B,SAAS4B,QAAQE,YAAa9B,SAAS4B,QAAQG,QACjHoB,QAAU,CACZH,SAAAA,WAGEI,KAACA,KAADC,GAAOA,UAAY3C,mBAAU4C,iBAAiB,wBAAyBH,4BACnEI,oBAAoB3C,YAAYW,cAAcC,mBAAUgC,QAAQC,mBAAoBL,KAAMC,IACpGK,oBAAoB9C,mBACdqC,iBAAiBrC,YAAaZ,SAAS4B,QAAQsB,OAQnDQ,oBAAsBC,aAClBX,SAAWW,KAAKC,iBAAiBpC,mBAAUuB,SAASC,UAEnC,GAAnBA,SAASa,SACTb,SAAS,GAAGc,SAAU,IAWxBb,iBAAmBlD,eAAM4D,UAAMI,mEAAc,SACzCC,eAAiBL,KAAKpC,cAAcC,mBAAUC,OAAOH,SACrD2C,UAAYC,UAAUF,gBAAkB,CAACpC,QAAS,CAACqC,UAAW,KAAKrC,QAAQqC,WAC3Ef,MAAQc,gBAAkB,CAACpC,QAAS,CAACsB,KAAMa,eAAenC,QAAQsB,MAElEE,KAACA,KAADC,GAAOA,UAAY3C,mBAAU4C,iBAAiB,6BAA8B,CAACa,IAAKjB,KAAMe,UAAAA,+BACpFV,oBAAoBI,KAAKpC,cAAcC,mBAAUgC,QAAQY,eAAgBhB,KAAMC,KAavF1B,eAAiB5B,MAAMuB,QAASO,UAAWwC,YAAaC,OAAQtC,qPACtBV,sUAAAA,sGAAAA,+BACvBiD,QAAQ1C,UAAWwC,YAAaC,OAAQtC,aAMpDwC,KAAO,KACXA,KAAKC,cAEND,KAAKC,aAAc,EA9IvBC,SAAS9B,iBAAiB,SAASzB,UACzBwD,eAAiBxD,EAAE0B,OAAO+B,QAAQ,+CACpCD,iBACAxD,EAAEwB,iBAEF7C,KAAK6E,eAAgB,CAAC1E,aAAckB,EAAE0B,kCAoJlD2B,KAAKC,aAAc"} \ No newline at end of file +{"version":3,"file":"gateways_modal.min.js","sources":["../src/gateways_modal.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 * Contain the logic for the gateways modal.\n *\n * @module core_payment/gateways_modal\n * @copyright 2019 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalFactory from 'core/modal_factory';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport {getAvailableGateways} from './repository';\nimport Selectors from './selectors';\nimport ModalEvents from 'core/modal_events';\nimport PaymentEvents from 'core_payment/events';\nimport {add as addToast, addToastRegion} from 'core/toast';\nimport Notification from 'core/notification';\nimport ModalGateways from './modal_gateways';\n\n/**\n * Register event listeners for the module.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', e => {\n const gatewayTrigger = e.target.closest('[data-action=\"core_payment/triggerPayment\"]');\n if (gatewayTrigger) {\n e.preventDefault();\n\n show(gatewayTrigger, {focusOnClose: e.target});\n }\n });\n};\n\n/**\n * Shows the gateway selector modal.\n *\n * @param {HTMLElement} rootNode\n * @param {Object} options - Additional options\n * @param {HTMLElement} options.focusOnClose The element to focus on when the modal is closed.\n */\nconst show = async(rootNode, {\n focusOnClose = null,\n} = {}) => {\n const modal = await ModalFactory.create({\n type: ModalGateways.TYPE,\n title: await getString('selectpaymenttype', 'core_payment'),\n body: await Templates.render('core_payment/gateways_modal', {}),\n });\n\n const rootElement = modal.getRoot()[0];\n addToastRegion(rootElement);\n\n modal.show();\n\n modal.getRoot().on(ModalEvents.hidden, () => {\n // Destroy when hidden.\n modal.destroy();\n try {\n focusOnClose.focus();\n } catch (e) {\n // eslint-disable-line\n }\n });\n\n modal.getRoot().on(PaymentEvents.proceed, (e) => {\n const gateway = (rootElement.querySelector(Selectors.values.gateway) || {value: ''}).value;\n\n if (gateway) {\n processPayment(\n gateway,\n rootNode.dataset.component,\n rootNode.dataset.paymentarea,\n rootNode.dataset.itemid,\n rootNode.dataset.description\n )\n .then(message => {\n modal.hide();\n Notification.addNotification({\n message: message,\n type: 'success',\n });\n location.href = rootNode.dataset.successurl;\n\n // The following return statement is never reached. It is put here just to make eslint happy.\n return message;\n })\n .catch(message => Notification.alert('', message));\n } else {\n // We cannot use await in the following line.\n // The reason is that we are preventing the default action of the save event being triggered,\n // therefore we cannot define the event handler function asynchronous.\n getString('nogatewayselected', 'core_payment').then(message => addToast(message, {type: 'warning'})).catch();\n }\n\n e.preventDefault();\n });\n\n // Re-calculate the cost when gateway is changed.\n rootElement.addEventListener('change', e => {\n if (e.target.matches(Selectors.elements.gateways)) {\n updateCostRegion(rootElement, rootNode.dataset.cost);\n }\n });\n\n const gateways = await getAvailableGateways(rootNode.dataset.component, rootNode.dataset.paymentarea, rootNode.dataset.itemid);\n const context = {\n gateways\n };\n\n const {html, js} = await Templates.renderForPromise('core_payment/gateways', context);\n Templates.replaceNodeContents(rootElement.querySelector(Selectors.regions.gatewaysContainer), html, js);\n selectSingleGateway(rootElement);\n await updateCostRegion(rootElement, rootNode.dataset.cost);\n};\n\n/**\n * Auto-select the gateway if there is only one gateway.\n *\n * @param {HTMLElement} root An HTMLElement that contains the cost region\n */\nconst selectSingleGateway = root => {\n const gateways = root.querySelectorAll(Selectors.elements.gateways);\n\n if (gateways.length == 1) {\n gateways[0].checked = true;\n }\n};\n\n/**\n * Shows the cost of the item the user is purchasing in the cost region.\n *\n * @param {HTMLElement} root An HTMLElement that contains the cost region\n * @param {string} defaultCost The default cost that is going to be displayed if no gateway is selected\n * @returns {Promise}\n */\nconst updateCostRegion = async(root, defaultCost = '') => {\n const gatewayElement = root.querySelector(Selectors.values.gateway);\n const surcharge = parseInt((gatewayElement || {dataset: {surcharge: 0}}).dataset.surcharge);\n const cost = (gatewayElement || {dataset: {cost: defaultCost}}).dataset.cost;\n\n const {html, js} = await Templates.renderForPromise('core_payment/fee_breakdown', {fee: cost, surcharge});\n Templates.replaceNodeContents(root.querySelector(Selectors.regions.costContainer), html, js);\n};\n\n/**\n * Process payment using the selected gateway.\n *\n * @param {string} gateway The gateway to be used for payment\n * @param {string} component Name of the component that the itemId belongs to\n * @param {string} paymentArea Name of the area in the component that the itemId belongs to\n * @param {number} itemId An internal identifier that is used by the component\n * @param {string} description Description of the payment\n * @returns {Promise}\n */\nconst processPayment = async(gateway, component, paymentArea, itemId, description) => {\n const paymentMethod = await import(`paygw_${gateway}/gateways_modal`);\n return paymentMethod.process(component, paymentArea, itemId, description);\n};\n\n/**\n * Set up the payment actions.\n */\nexport const init = () => {\n if (!init.initialised) {\n // Event listeners should only be registered once.\n init.initialised = true;\n registerEventListeners();\n }\n};\n\n/**\n * Whether the init function was called before.\n *\n * @static\n * @type {boolean}\n */\ninit.initialised = false;\n"],"names":["show","async","rootNode","focusOnClose","modal","ModalFactory","create","type","ModalGateways","TYPE","title","body","Templates","render","rootElement","getRoot","on","ModalEvents","hidden","destroy","focus","e","PaymentEvents","proceed","gateway","querySelector","Selectors","values","value","processPayment","dataset","component","paymentarea","itemid","description","then","message","hide","addNotification","location","href","successurl","catch","Notification","alert","preventDefault","addEventListener","target","matches","elements","gateways","updateCostRegion","cost","context","html","js","renderForPromise","replaceNodeContents","regions","gatewaysContainer","selectSingleGateway","root","querySelectorAll","length","checked","defaultCost","gatewayElement","surcharge","parseInt","fee","costContainer","paymentArea","itemId","process","init","initialised","document","gatewayTrigger","closest"],"mappings":"m/BAuDMA,KAAOC,eAAMC,cAAUC,aACzBA,aAAe,6DACf,SACMC,YAAcC,uBAAaC,OAAO,CACpCC,KAAMC,wBAAcC,KACpBC,YAAa,kBAAU,oBAAqB,gBAC5CC,WAAYC,mBAAUC,OAAO,8BAA+B,MAG1DC,YAAcV,MAAMW,UAAU,6BACrBD,aAEfV,MAAMJ,OAENI,MAAMW,UAAUC,GAAGC,sBAAYC,QAAQ,KAEnCd,MAAMe,cAEFhB,aAAaiB,QACf,MAAOC,QAKbjB,MAAMW,UAAUC,GAAGM,gBAAcC,SAAUF,UACjCG,SAAWV,YAAYW,cAAcC,mBAAUC,OAAOH,UAAY,CAACI,MAAO,KAAKA,MAEjFJ,QACAK,eACIL,QACAtB,SAAS4B,QAAQC,UACjB7B,SAAS4B,QAAQE,YACjB9B,SAAS4B,QAAQG,OACjB/B,SAAS4B,QAAQI,aAEpBC,MAAKC,UACFhC,MAAMiC,6BACOC,gBAAgB,CACzBF,QAASA,QACT7B,KAAM,YAEVgC,SAASC,KAAOtC,SAAS4B,QAAQW,WAG1BL,WAEVM,OAAMN,SAAWO,sBAAaC,MAAM,GAAIR,8BAK/B,oBAAqB,gBAAgBD,MAAKC,UAAW,cAASA,QAAS,CAAC7B,KAAM,cAAamC,QAGzGrB,EAAEwB,oBAIN/B,YAAYgC,iBAAiB,UAAUzB,IAC/BA,EAAE0B,OAAOC,QAAQtB,mBAAUuB,SAASC,WACpCC,iBAAiBrC,YAAaZ,SAAS4B,QAAQsB,eAIjDF,eAAiB,oCAAqBhD,SAAS4B,QAAQC,UAAW7B,SAAS4B,QAAQE,YAAa9B,SAAS4B,QAAQG,QACjHoB,QAAU,CACZH,SAAAA,WAGEI,KAACA,KAADC,GAAOA,UAAY3C,mBAAU4C,iBAAiB,wBAAyBH,4BACnEI,oBAAoB3C,YAAYW,cAAcC,mBAAUgC,QAAQC,mBAAoBL,KAAMC,IACpGK,oBAAoB9C,mBACdqC,iBAAiBrC,YAAaZ,SAAS4B,QAAQsB,OAQnDQ,oBAAsBC,aAClBX,SAAWW,KAAKC,iBAAiBpC,mBAAUuB,SAASC,UAEnC,GAAnBA,SAASa,SACTb,SAAS,GAAGc,SAAU,IAWxBb,iBAAmBlD,eAAM4D,UAAMI,mEAAc,SACzCC,eAAiBL,KAAKpC,cAAcC,mBAAUC,OAAOH,SACrD2C,UAAYC,UAAUF,gBAAkB,CAACpC,QAAS,CAACqC,UAAW,KAAKrC,QAAQqC,WAC3Ef,MAAQc,gBAAkB,CAACpC,QAAS,CAACsB,KAAMa,eAAenC,QAAQsB,MAElEE,KAACA,KAADC,GAAOA,UAAY3C,mBAAU4C,iBAAiB,6BAA8B,CAACa,IAAKjB,KAAMe,UAAAA,+BACpFV,oBAAoBI,KAAKpC,cAAcC,mBAAUgC,QAAQY,eAAgBhB,KAAMC,KAavF1B,eAAiB5B,MAAMuB,QAASO,UAAWwC,YAAaC,OAAQtC,qPACtBV,sUAAAA,sGAAAA,+BACvBiD,QAAQ1C,UAAWwC,YAAaC,OAAQtC,aAMpDwC,KAAO,KACXA,KAAKC,cAEND,KAAKC,aAAc,EA9IvBC,SAAS9B,iBAAiB,SAASzB,UACzBwD,eAAiBxD,EAAE0B,OAAO+B,QAAQ,+CACpCD,iBACAxD,EAAEwB,iBAEF7C,KAAK6E,eAAgB,CAAC1E,aAAckB,EAAE0B,kCAoJlD2B,KAAKC,aAAc"} \ No newline at end of file diff --git a/payment/amd/src/gateways_modal.js b/payment/amd/src/gateways_modal.js index 96e51fdc77d..52a52b6da03 100644 --- a/payment/amd/src/gateways_modal.js +++ b/payment/amd/src/gateways_modal.js @@ -23,7 +23,7 @@ import ModalFactory from 'core/modal_factory'; import Templates from 'core/templates'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {getAvailableGateways} from './repository'; import Selectors from './selectors'; import ModalEvents from 'core/modal_events'; diff --git a/payment/gateway/paypal/amd/build/gateways_modal.min.js b/payment/gateway/paypal/amd/build/gateways_modal.min.js index e096c8c6b22..e4a12447137 100644 --- a/payment/gateway/paypal/amd/build/gateways_modal.min.js +++ b/payment/gateway/paypal/amd/build/gateways_modal.min.js @@ -5,6 +5,6 @@ define("paygw_paypal/gateways_modal",["exports","./repository","core/templates", * @module paygw_paypal/gateways_modal * @copyright 2020 Shamim Rezaie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */(Repository),_templates=_interopRequireDefault(_templates),_truncate=_interopRequireDefault(_truncate),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events);const showModalWithPlaceholder=async()=>{const modal=await _modal_factory.default.create({body:await _templates.default.render("paygw_paypal/paypal_button_placeholder",{})});return modal.show(),modal};_exports.process=(component,paymentArea,itemId,description)=>Promise.all([showModalWithPlaceholder(),Repository.getConfigForJs(component,paymentArea,itemId)]).then((_ref=>{let[modal,paypalConfig]=_ref;return modal.getRoot().on(_modal_events.default.hidden,(()=>{modal.destroy()})),Promise.all([modal,paypalConfig,switchSdk(paypalConfig.clientid,paypalConfig.currency)])})).then((_ref2=>{let[modal,paypalConfig]=_ref2;return modal.setBody(""),new Promise((resolve=>{window.paypal.Buttons({createOrder:function(data,actions){return actions.order.create({purchase_units:[{amount:{currency_code:paypalConfig.currency_code,value:paypalConfig.cost},description:_truncate.default.truncate(description,{length:127,stripTags:!0})}],application_context:{shipping_preference:"NO_SHIPPING",brand_name:_truncate.default.truncate(paypalConfig.brandname,{length:127,stripTags:!0})}})},onApprove:function(data){modal.getRoot().on(_modal_events.default.outsideClick,(e=>{e.preventDefault()})),modal.setBody((0,_str.get_string)("authorising","paygw_paypal")),Repository.markTransactionComplete(component,paymentArea,itemId,data.orderID).then((res=>(modal.hide(),res))).then(resolve)}}).render(modal.getBody()[0])}))})).then((res=>res.success?Promise.resolve(res.message):Promise.reject(res.message)));const switchSdk=(clientId,currency)=>{const sdkUrl="https://www.paypal.com/sdk/js?client-id=".concat(clientId,"¤cy=").concat(currency);if(switchSdk.currentlyloaded===sdkUrl)return Promise.resolve();if(switchSdk.currentlyloaded){const suspectedScript=document.querySelector('script[src="'.concat(switchSdk.currentlyloaded,'"]'));suspectedScript&&suspectedScript.parentNode.removeChild(suspectedScript)}const script=document.createElement("script");return new Promise((resolve=>{script.readyState?script.onreadystatechange=function(){"complete"!=this.readyState&&"loaded"!=this.readyState||(this.onreadystatechange=null,resolve())}:script.onload=function(){resolve()},script.setAttribute("src",sdkUrl),document.head.appendChild(script),switchSdk.currentlyloaded=sdkUrl}))};switchSdk.currentlyloaded=""})); + */(Repository),_templates=_interopRequireDefault(_templates),_truncate=_interopRequireDefault(_truncate),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events);const showModalWithPlaceholder=async()=>{const modal=await _modal_factory.default.create({body:await _templates.default.render("paygw_paypal/paypal_button_placeholder",{})});return modal.show(),modal};_exports.process=(component,paymentArea,itemId,description)=>Promise.all([showModalWithPlaceholder(),Repository.getConfigForJs(component,paymentArea,itemId)]).then((_ref=>{let[modal,paypalConfig]=_ref;return modal.getRoot().on(_modal_events.default.hidden,(()=>{modal.destroy()})),Promise.all([modal,paypalConfig,switchSdk(paypalConfig.clientid,paypalConfig.currency)])})).then((_ref2=>{let[modal,paypalConfig]=_ref2;return modal.setBody(""),new Promise((resolve=>{window.paypal.Buttons({createOrder:function(data,actions){return actions.order.create({purchase_units:[{amount:{currency_code:paypalConfig.currency_code,value:paypalConfig.cost},description:_truncate.default.truncate(description,{length:127,stripTags:!0})}],application_context:{shipping_preference:"NO_SHIPPING",brand_name:_truncate.default.truncate(paypalConfig.brandname,{length:127,stripTags:!0})}})},onApprove:function(data){modal.getRoot().on(_modal_events.default.outsideClick,(e=>{e.preventDefault()})),modal.setBody((0,_str.getString)("authorising","paygw_paypal")),Repository.markTransactionComplete(component,paymentArea,itemId,data.orderID).then((res=>(modal.hide(),res))).then(resolve)}}).render(modal.getBody()[0])}))})).then((res=>res.success?Promise.resolve(res.message):Promise.reject(res.message)));const switchSdk=(clientId,currency)=>{const sdkUrl="https://www.paypal.com/sdk/js?client-id=".concat(clientId,"¤cy=").concat(currency);if(switchSdk.currentlyloaded===sdkUrl)return Promise.resolve();if(switchSdk.currentlyloaded){const suspectedScript=document.querySelector('script[src="'.concat(switchSdk.currentlyloaded,'"]'));suspectedScript&&suspectedScript.parentNode.removeChild(suspectedScript)}const script=document.createElement("script");return new Promise((resolve=>{script.readyState?script.onreadystatechange=function(){"complete"!=this.readyState&&"loaded"!=this.readyState||(this.onreadystatechange=null,resolve())}:script.onload=function(){resolve()},script.setAttribute("src",sdkUrl),document.head.appendChild(script),switchSdk.currentlyloaded=sdkUrl}))};switchSdk.currentlyloaded=""})); //# sourceMappingURL=gateways_modal.min.js.map \ No newline at end of file diff --git a/payment/gateway/paypal/amd/build/gateways_modal.min.js.map b/payment/gateway/paypal/amd/build/gateways_modal.min.js.map index ffdb674eb0c..1c0846c4ccc 100644 --- a/payment/gateway/paypal/amd/build/gateways_modal.min.js.map +++ b/payment/gateway/paypal/amd/build/gateways_modal.min.js.map @@ -1 +1 @@ -{"version":3,"file":"gateways_modal.min.js","sources":["../src/gateways_modal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module is responsible for PayPal content in the gateways modal.\n *\n * @module paygw_paypal/gateways_modal\n * @copyright 2020 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as Repository from './repository';\nimport Templates from 'core/templates';\nimport Truncate from 'core/truncate';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Creates and shows a modal that contains a placeholder.\n *\n * @returns {Promise}\n */\nconst showModalWithPlaceholder = async() => {\n const modal = await ModalFactory.create({\n body: await Templates.render('paygw_paypal/paypal_button_placeholder', {})\n });\n modal.show();\n return modal;\n};\n\n/**\n * Process the payment.\n *\n * @param {string} component Name of the component that the itemId belongs to\n * @param {string} paymentArea The area of the component that the itemId belongs to\n * @param {number} itemId An internal identifier that is used by the component\n * @param {string} description Description of the payment\n * @returns {Promise}\n */\nexport const process = (component, paymentArea, itemId, description) => {\n return Promise.all([\n showModalWithPlaceholder(),\n Repository.getConfigForJs(component, paymentArea, itemId),\n ])\n .then(([modal, paypalConfig]) => {\n modal.getRoot().on(ModalEvents.hidden, () => {\n // Destroy when hidden.\n modal.destroy();\n });\n\n return Promise.all([\n modal,\n paypalConfig,\n switchSdk(paypalConfig.clientid, paypalConfig.currency),\n ]);\n })\n .then(([modal, paypalConfig]) => {\n // We have to clear the body. The render method in paypal.Buttons will render everything.\n modal.setBody('');\n\n return new Promise(resolve => {\n window.paypal.Buttons({\n // Set up the transaction.\n createOrder: function(data, actions) {\n return actions.order.create({\n purchase_units: [{ // eslint-disable-line\n amount: {\n currency_code: paypalConfig.currency_code, // eslint-disable-line\n value: paypalConfig.cost,\n },\n description: Truncate.truncate(description, {length: 127, stripTags: true}),\n }],\n application_context: { // eslint-disable-line\n shipping_preference: 'NO_SHIPPING', // eslint-disable-line\n brand_name: Truncate.truncate(paypalConfig.brandname, {length: 127, stripTags: true}), // eslint-disable-line\n },\n });\n },\n // Finalise the transaction.\n onApprove: function(data) {\n modal.getRoot().on(ModalEvents.outsideClick, (e) => {\n // Prevent closing the modal when clicking outside of it.\n e.preventDefault();\n });\n\n modal.setBody(getString('authorising', 'paygw_paypal'));\n\n Repository.markTransactionComplete(component, paymentArea, itemId, data.orderID)\n .then(res => {\n modal.hide();\n return res;\n })\n .then(resolve);\n }\n }).render(modal.getBody()[0]);\n });\n })\n .then(res => {\n if (res.success) {\n return Promise.resolve(res.message);\n }\n\n return Promise.reject(res.message);\n });\n};\n\n/**\n * Unloads the previously loaded PayPal JavaScript SDK, and loads a new one.\n *\n * @param {string} clientId PayPal client ID\n * @param {string} currency The currency\n * @returns {Promise}\n */\nconst switchSdk = (clientId, currency) => {\n const sdkUrl = `https://www.paypal.com/sdk/js?client-id=${clientId}¤cy=${currency}`;\n\n // Check to see if this file has already been loaded. If so just go straight to the func.\n if (switchSdk.currentlyloaded === sdkUrl) {\n return Promise.resolve();\n }\n\n // PayPal can only work with one currency at the same time. We have to unload the previously loaded script\n // if it was loaded for a different currency. Weird way indeed, but the only way.\n // See: https://github.com/paypal/paypal-checkout-components/issues/1180\n if (switchSdk.currentlyloaded) {\n const suspectedScript = document.querySelector(`script[src=\"${switchSdk.currentlyloaded}\"]`);\n if (suspectedScript) {\n suspectedScript.parentNode.removeChild(suspectedScript);\n }\n }\n\n const script = document.createElement('script');\n\n return new Promise(resolve => {\n if (script.readyState) {\n script.onreadystatechange = function() {\n if (this.readyState == 'complete' || this.readyState == 'loaded') {\n this.onreadystatechange = null;\n resolve();\n }\n };\n } else {\n script.onload = function() {\n resolve();\n };\n }\n\n script.setAttribute('src', sdkUrl);\n document.head.appendChild(script);\n\n switchSdk.currentlyloaded = sdkUrl;\n });\n};\n\n/**\n * Holds the full url of loaded PayPal JavaScript SDK.\n *\n * @static\n * @type {string}\n */\nswitchSdk.currentlyloaded = '';\n"],"names":["showModalWithPlaceholder","async","modal","ModalFactory","create","body","Templates","render","show","component","paymentArea","itemId","description","Promise","all","Repository","getConfigForJs","then","_ref","paypalConfig","getRoot","on","ModalEvents","hidden","destroy","switchSdk","clientid","currency","_ref2","setBody","resolve","window","paypal","Buttons","createOrder","data","actions","order","purchase_units","amount","currency_code","value","cost","Truncate","truncate","length","stripTags","application_context","shipping_preference","brand_name","brandname","onApprove","outsideClick","e","preventDefault","markTransactionComplete","orderID","res","hide","getBody","success","message","reject","clientId","sdkUrl","currentlyloaded","suspectedScript","document","querySelector","parentNode","removeChild","script","createElement","readyState","onreadystatechange","this","onload","setAttribute","head","appendChild"],"mappings":";;;;;;;4NAmCMA,yBAA2BC,gBACvBC,YAAcC,uBAAaC,OAAO,CACpCC,WAAYC,mBAAUC,OAAO,yCAA0C,aAE3EL,MAAMM,OACCN,wBAYY,CAACO,UAAWC,YAAaC,OAAQC,cAC7CC,QAAQC,IAAI,CACfd,2BACAe,WAAWC,eAAeP,UAAWC,YAAaC,UAErDM,MAAKC,WAAEhB,MAAOiB,0BACXjB,MAAMkB,UAAUC,GAAGC,sBAAYC,QAAQ,KAEnCrB,MAAMsB,aAGHX,QAAQC,IAAI,CACfZ,MACAiB,aACAM,UAAUN,aAAaO,SAAUP,aAAaQ,eAGrDV,MAAKW,YAAE1B,MAAOiB,2BAEXjB,MAAM2B,QAAQ,IAEP,IAAIhB,SAAQiB,UACfC,OAAOC,OAAOC,QAAQ,CAElBC,YAAa,SAASC,KAAMC,gBACjBA,QAAQC,MAAMjC,OAAO,CACxBkC,eAAgB,CAAC,CACbC,OAAQ,CACJC,cAAerB,aAAaqB,cAC5BC,MAAOtB,aAAauB,MAExB9B,YAAa+B,kBAASC,SAAShC,YAAa,CAACiC,OAAQ,IAAKC,WAAW,MAEzEC,oBAAqB,CACjBC,oBAAqB,cACrBC,WAAYN,kBAASC,SAASzB,aAAa+B,UAAW,CAACL,OAAQ,IAAKC,WAAW,QAK3FK,UAAW,SAAShB,MAChBjC,MAAMkB,UAAUC,GAAGC,sBAAY8B,cAAeC,IAE1CA,EAAEC,oBAGNpD,MAAM2B,SAAQ,mBAAU,cAAe,iBAEvCd,WAAWwC,wBAAwB9C,UAAWC,YAAaC,OAAQwB,KAAKqB,SACvEvC,MAAKwC,MACFvD,MAAMwD,OACCD,OAEVxC,KAAKa,YAEXvB,OAAOL,MAAMyD,UAAU,UAGjC1C,MAAKwC,KACEA,IAAIG,QACG/C,QAAQiB,QAAQ2B,IAAII,SAGxBhD,QAAQiD,OAAOL,IAAII,iBAW5BpC,UAAY,CAACsC,SAAUpC,kBACnBqC,yDAAoDD,8BAAqBpC,aAG3EF,UAAUwC,kBAAoBD,cACvBnD,QAAQiB,aAMfL,UAAUwC,gBAAiB,OACrBC,gBAAkBC,SAASC,oCAA6B3C,UAAUwC,uBACpEC,iBACAA,gBAAgBG,WAAWC,YAAYJ,uBAIzCK,OAASJ,SAASK,cAAc,iBAE/B,IAAI3D,SAAQiB,UACXyC,OAAOE,WACPF,OAAOG,mBAAqB,WACD,YAAnBC,KAAKF,YAA+C,UAAnBE,KAAKF,kBACjCC,mBAAqB,KAC1B5C,YAIRyC,OAAOK,OAAS,WACZ9C,WAIRyC,OAAOM,aAAa,MAAOb,QAC3BG,SAASW,KAAKC,YAAYR,QAE1B9C,UAAUwC,gBAAkBD,WAUpCvC,UAAUwC,gBAAkB"} \ No newline at end of file +{"version":3,"file":"gateways_modal.min.js","sources":["../src/gateways_modal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module is responsible for PayPal content in the gateways modal.\n *\n * @module paygw_paypal/gateways_modal\n * @copyright 2020 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as Repository from './repository';\nimport Templates from 'core/templates';\nimport Truncate from 'core/truncate';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport {getString} from 'core/str';\n\n/**\n * Creates and shows a modal that contains a placeholder.\n *\n * @returns {Promise}\n */\nconst showModalWithPlaceholder = async() => {\n const modal = await ModalFactory.create({\n body: await Templates.render('paygw_paypal/paypal_button_placeholder', {})\n });\n modal.show();\n return modal;\n};\n\n/**\n * Process the payment.\n *\n * @param {string} component Name of the component that the itemId belongs to\n * @param {string} paymentArea The area of the component that the itemId belongs to\n * @param {number} itemId An internal identifier that is used by the component\n * @param {string} description Description of the payment\n * @returns {Promise}\n */\nexport const process = (component, paymentArea, itemId, description) => {\n return Promise.all([\n showModalWithPlaceholder(),\n Repository.getConfigForJs(component, paymentArea, itemId),\n ])\n .then(([modal, paypalConfig]) => {\n modal.getRoot().on(ModalEvents.hidden, () => {\n // Destroy when hidden.\n modal.destroy();\n });\n\n return Promise.all([\n modal,\n paypalConfig,\n switchSdk(paypalConfig.clientid, paypalConfig.currency),\n ]);\n })\n .then(([modal, paypalConfig]) => {\n // We have to clear the body. The render method in paypal.Buttons will render everything.\n modal.setBody('');\n\n return new Promise(resolve => {\n window.paypal.Buttons({\n // Set up the transaction.\n createOrder: function(data, actions) {\n return actions.order.create({\n purchase_units: [{ // eslint-disable-line\n amount: {\n currency_code: paypalConfig.currency_code, // eslint-disable-line\n value: paypalConfig.cost,\n },\n description: Truncate.truncate(description, {length: 127, stripTags: true}),\n }],\n application_context: { // eslint-disable-line\n shipping_preference: 'NO_SHIPPING', // eslint-disable-line\n brand_name: Truncate.truncate(paypalConfig.brandname, {length: 127, stripTags: true}), // eslint-disable-line\n },\n });\n },\n // Finalise the transaction.\n onApprove: function(data) {\n modal.getRoot().on(ModalEvents.outsideClick, (e) => {\n // Prevent closing the modal when clicking outside of it.\n e.preventDefault();\n });\n\n modal.setBody(getString('authorising', 'paygw_paypal'));\n\n Repository.markTransactionComplete(component, paymentArea, itemId, data.orderID)\n .then(res => {\n modal.hide();\n return res;\n })\n .then(resolve);\n }\n }).render(modal.getBody()[0]);\n });\n })\n .then(res => {\n if (res.success) {\n return Promise.resolve(res.message);\n }\n\n return Promise.reject(res.message);\n });\n};\n\n/**\n * Unloads the previously loaded PayPal JavaScript SDK, and loads a new one.\n *\n * @param {string} clientId PayPal client ID\n * @param {string} currency The currency\n * @returns {Promise}\n */\nconst switchSdk = (clientId, currency) => {\n const sdkUrl = `https://www.paypal.com/sdk/js?client-id=${clientId}¤cy=${currency}`;\n\n // Check to see if this file has already been loaded. If so just go straight to the func.\n if (switchSdk.currentlyloaded === sdkUrl) {\n return Promise.resolve();\n }\n\n // PayPal can only work with one currency at the same time. We have to unload the previously loaded script\n // if it was loaded for a different currency. Weird way indeed, but the only way.\n // See: https://github.com/paypal/paypal-checkout-components/issues/1180\n if (switchSdk.currentlyloaded) {\n const suspectedScript = document.querySelector(`script[src=\"${switchSdk.currentlyloaded}\"]`);\n if (suspectedScript) {\n suspectedScript.parentNode.removeChild(suspectedScript);\n }\n }\n\n const script = document.createElement('script');\n\n return new Promise(resolve => {\n if (script.readyState) {\n script.onreadystatechange = function() {\n if (this.readyState == 'complete' || this.readyState == 'loaded') {\n this.onreadystatechange = null;\n resolve();\n }\n };\n } else {\n script.onload = function() {\n resolve();\n };\n }\n\n script.setAttribute('src', sdkUrl);\n document.head.appendChild(script);\n\n switchSdk.currentlyloaded = sdkUrl;\n });\n};\n\n/**\n * Holds the full url of loaded PayPal JavaScript SDK.\n *\n * @static\n * @type {string}\n */\nswitchSdk.currentlyloaded = '';\n"],"names":["showModalWithPlaceholder","async","modal","ModalFactory","create","body","Templates","render","show","component","paymentArea","itemId","description","Promise","all","Repository","getConfigForJs","then","_ref","paypalConfig","getRoot","on","ModalEvents","hidden","destroy","switchSdk","clientid","currency","_ref2","setBody","resolve","window","paypal","Buttons","createOrder","data","actions","order","purchase_units","amount","currency_code","value","cost","Truncate","truncate","length","stripTags","application_context","shipping_preference","brand_name","brandname","onApprove","outsideClick","e","preventDefault","markTransactionComplete","orderID","res","hide","getBody","success","message","reject","clientId","sdkUrl","currentlyloaded","suspectedScript","document","querySelector","parentNode","removeChild","script","createElement","readyState","onreadystatechange","this","onload","setAttribute","head","appendChild"],"mappings":";;;;;;;4NAmCMA,yBAA2BC,gBACvBC,YAAcC,uBAAaC,OAAO,CACpCC,WAAYC,mBAAUC,OAAO,yCAA0C,aAE3EL,MAAMM,OACCN,wBAYY,CAACO,UAAWC,YAAaC,OAAQC,cAC7CC,QAAQC,IAAI,CACfd,2BACAe,WAAWC,eAAeP,UAAWC,YAAaC,UAErDM,MAAKC,WAAEhB,MAAOiB,0BACXjB,MAAMkB,UAAUC,GAAGC,sBAAYC,QAAQ,KAEnCrB,MAAMsB,aAGHX,QAAQC,IAAI,CACfZ,MACAiB,aACAM,UAAUN,aAAaO,SAAUP,aAAaQ,eAGrDV,MAAKW,YAAE1B,MAAOiB,2BAEXjB,MAAM2B,QAAQ,IAEP,IAAIhB,SAAQiB,UACfC,OAAOC,OAAOC,QAAQ,CAElBC,YAAa,SAASC,KAAMC,gBACjBA,QAAQC,MAAMjC,OAAO,CACxBkC,eAAgB,CAAC,CACbC,OAAQ,CACJC,cAAerB,aAAaqB,cAC5BC,MAAOtB,aAAauB,MAExB9B,YAAa+B,kBAASC,SAAShC,YAAa,CAACiC,OAAQ,IAAKC,WAAW,MAEzEC,oBAAqB,CACjBC,oBAAqB,cACrBC,WAAYN,kBAASC,SAASzB,aAAa+B,UAAW,CAACL,OAAQ,IAAKC,WAAW,QAK3FK,UAAW,SAAShB,MAChBjC,MAAMkB,UAAUC,GAAGC,sBAAY8B,cAAeC,IAE1CA,EAAEC,oBAGNpD,MAAM2B,SAAQ,kBAAU,cAAe,iBAEvCd,WAAWwC,wBAAwB9C,UAAWC,YAAaC,OAAQwB,KAAKqB,SACvEvC,MAAKwC,MACFvD,MAAMwD,OACCD,OAEVxC,KAAKa,YAEXvB,OAAOL,MAAMyD,UAAU,UAGjC1C,MAAKwC,KACEA,IAAIG,QACG/C,QAAQiB,QAAQ2B,IAAII,SAGxBhD,QAAQiD,OAAOL,IAAII,iBAW5BpC,UAAY,CAACsC,SAAUpC,kBACnBqC,yDAAoDD,8BAAqBpC,aAG3EF,UAAUwC,kBAAoBD,cACvBnD,QAAQiB,aAMfL,UAAUwC,gBAAiB,OACrBC,gBAAkBC,SAASC,oCAA6B3C,UAAUwC,uBACpEC,iBACAA,gBAAgBG,WAAWC,YAAYJ,uBAIzCK,OAASJ,SAASK,cAAc,iBAE/B,IAAI3D,SAAQiB,UACXyC,OAAOE,WACPF,OAAOG,mBAAqB,WACD,YAAnBC,KAAKF,YAA+C,UAAnBE,KAAKF,kBACjCC,mBAAqB,KAC1B5C,YAIRyC,OAAOK,OAAS,WACZ9C,WAIRyC,OAAOM,aAAa,MAAOb,QAC3BG,SAASW,KAAKC,YAAYR,QAE1B9C,UAAUwC,gBAAkBD,WAUpCvC,UAAUwC,gBAAkB"} \ No newline at end of file diff --git a/payment/gateway/paypal/amd/src/gateways_modal.js b/payment/gateway/paypal/amd/src/gateways_modal.js index 7ca5f45ebf1..cb006e10747 100644 --- a/payment/gateway/paypal/amd/src/gateways_modal.js +++ b/payment/gateway/paypal/amd/src/gateways_modal.js @@ -26,7 +26,7 @@ import Templates from 'core/templates'; import Truncate from 'core/truncate'; import ModalFactory from 'core/modal_factory'; import ModalEvents from 'core/modal_events'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; /** * Creates and shows a modal that contains a placeholder. diff --git a/reportbuilder/amd/build/audience.min.js b/reportbuilder/amd/build/audience.min.js index 9ed6ebf86f2..013289ce61c 100644 --- a/reportbuilder/amd/build/audience.min.js +++ b/reportbuilder/amd/build/audience.min.js @@ -1,3 +1,3 @@ -define("core_reportbuilder/audience",["exports","core/inplace_editable","core/templates","core/notification","core/pending","core/prefetch","core/str","core_form/dynamicform","core/toast","core_reportbuilder/local/repository/audiences","core_reportbuilder/local/selectors","core/fragment","core_form/changechecker"],(function(_exports,_inplace_editable,_templates,_notification,_pending,_prefetch,_str,_dynamicform,_toast,_audiences,reportSelectors,_fragment,_changechecker){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_dynamicform=_interopRequireDefault(_dynamicform),reportSelectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(reportSelectors);let reportId=0,contextId=0;const initAudienceCardForm=audienceCard=>{const audienceFormContainer=audienceCard.querySelector(reportSelectors.regions.audienceFormContainer),audienceForm=new _dynamicform.default(audienceFormContainer,"\\core_reportbuilder\\form\\audience");return audienceForm.addEventListener(audienceForm.events.FORM_SUBMITTED,(data=>{const audienceHeading=audienceCard.querySelector(reportSelectors.regions.audienceHeading),audienceDescription=audienceCard.querySelector(reportSelectors.regions.audienceDescription);return audienceCard.dataset.audienceId=data.detail.instanceid,audienceHeading.innerHTML=data.detail.heading,audienceDescription.innerHTML=data.detail.description,closeAudienceCardForm(audienceCard),(0,_str.get_string)("audiencesaved","core_reportbuilder").then(_toast.add)})),audienceForm.addEventListener(audienceForm.events.FORM_CANCELLED,(()=>{audienceCard.dataset.audienceId>0?closeAudienceCardForm(audienceCard):removeAudienceCard(audienceCard)})),audienceForm},closeAudienceCardForm=audienceCard=>{const audienceFormContainer=audienceCard.querySelector(reportSelectors.regions.audienceFormContainer),NewAudienceFormContainer=audienceFormContainer.cloneNode(!1);audienceCard.querySelector(reportSelectors.regions.audienceForm).replaceChild(NewAudienceFormContainer,audienceFormContainer),audienceCard.querySelector(reportSelectors.regions.audienceDescription).classList.remove("hidden"),audienceCard.querySelector(reportSelectors.actions.audienceEdit).disabled=!1,audienceCard.querySelector(reportSelectors.actions.audienceDelete).disabled=!1},removeAudienceCard=audienceCard=>{audienceCard.remove();const audienceCards=document.querySelector(reportSelectors.regions.audiencesContainer).querySelectorAll(reportSelectors.regions.audienceCard);if(0===audienceCards.length){document.querySelector(reportSelectors.regions.audienceEmptyMessage).classList.remove("hidden")}else{const audienceFirstCardSeparator=audienceCards[0].querySelector(".audience-separator");null==audienceFirstCardSeparator||audienceFirstCardSeparator.remove()}};let initialized=!1;_exports.init=(id,contextid)=>{(0,_prefetch.prefetchStrings)("core_reportbuilder",["audienceadded","audiencedeleted","audiencesaved","audienceusedbyschedule","deleteaudience","deleteaudienceconfirm"]),(0,_prefetch.prefetchStrings)("core",["delete"]),reportId=id,contextId=contextid,initialized||(document.addEventListener("click",(event=>{const audienceAdd=event.target.closest(reportSelectors.actions.audienceAdd);audienceAdd&&(event.preventDefault(),((className,title)=>{const pendingPromise=new _pending.default("core_reportbuilder/audience:add"),audiencesContainer=document.querySelector(reportSelectors.regions.audiencesContainer),audienceCardLength=audiencesContainer.querySelectorAll(reportSelectors.regions.audienceCard).length,params={classname:className,reportid:reportId,showormessage:audienceCardLength>0,title:title};(0,_fragment.loadFragment)("core_reportbuilder","audience_form",contextId,params).then(((html,js)=>{const audienceCard=_templates.default.appendNodeContents(audiencesContainer,html,js)[0],audienceEmptyMessage=audiencesContainer.querySelector(reportSelectors.regions.audienceEmptyMessage),audienceForm=initAudienceCardForm(audienceCard);return(0,_changechecker.markFormAsDirty)(audienceForm.getFormNode()),audienceEmptyMessage.classList.add("hidden"),(0,_str.get_string)("audienceadded","core_reportbuilder",title)})).then(_toast.add).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)})(audienceAdd.dataset.uniqueIdentifier,audienceAdd.dataset.name));const audienceEdit=event.target.closest(reportSelectors.actions.audienceEdit);if(audienceEdit){const audienceEditCard=audienceEdit.closest(reportSelectors.regions.audienceCard);event.preventDefault(),(audienceCard=>{const pendingPromise=new _pending.default("core_reportbuilder/audience:edit");initAudienceCardForm(audienceCard).load({id:audienceCard.dataset.audienceId}).then((()=>{const audienceFormContainer=audienceCard.querySelector(reportSelectors.regions.audienceFormContainer),audienceDescription=audienceCard.querySelector(reportSelectors.regions.audienceDescription),audienceEdit=audienceCard.querySelector(reportSelectors.actions.audienceEdit);return audienceFormContainer.classList.remove("hidden"),audienceDescription.classList.add("hidden"),audienceEdit.disabled=!0,pendingPromise.resolve()})).catch(_notification.default.exception)})(audienceEditCard)}const audienceDelete=event.target.closest(reportSelectors.actions.audienceDelete);audienceDelete&&(event.preventDefault(),(audienceDelete=>{const audienceCard=audienceDelete.closest(reportSelectors.regions.audienceCard),{audienceId:audienceId,audienceTitle:audienceTitle,audienceEditWarning:audienceEditWarning=!1}=audienceCard.dataset,audienceDeleteConfirmation=audienceEditWarning?"audienceusedbyschedule":"deleteaudienceconfirm";_notification.default.saveCancelPromise((0,_str.get_string)("deleteaudience","core_reportbuilder",audienceTitle),(0,_str.get_string)(audienceDeleteConfirmation,"core_reportbuilder",audienceTitle),(0,_str.get_string)("delete","core"),{triggerElement:audienceDelete}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/audience:delete");return(0,_audiences.deleteAudience)(reportId,audienceId).then((()=>(0,_toast.add)((0,_str.get_string)("audiencedeleted","core_reportbuilder",audienceTitle)))).then((()=>(removeAudienceCard(audienceCard),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))})(audienceDelete))})),initialized=!0)}})); +define("core_reportbuilder/audience",["exports","core/inplace_editable","core/templates","core/notification","core/pending","core/prefetch","core/str","core_form/dynamicform","core/toast","core_reportbuilder/local/repository/audiences","core_reportbuilder/local/selectors","core/fragment","core_form/changechecker"],(function(_exports,_inplace_editable,_templates,_notification,_pending,_prefetch,_str,_dynamicform,_toast,_audiences,reportSelectors,_fragment,_changechecker){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_dynamicform=_interopRequireDefault(_dynamicform),reportSelectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(reportSelectors);let reportId=0,contextId=0;const initAudienceCardForm=audienceCard=>{const audienceFormContainer=audienceCard.querySelector(reportSelectors.regions.audienceFormContainer),audienceForm=new _dynamicform.default(audienceFormContainer,"\\core_reportbuilder\\form\\audience");return audienceForm.addEventListener(audienceForm.events.FORM_SUBMITTED,(data=>{const audienceHeading=audienceCard.querySelector(reportSelectors.regions.audienceHeading),audienceDescription=audienceCard.querySelector(reportSelectors.regions.audienceDescription);return audienceCard.dataset.audienceId=data.detail.instanceid,audienceHeading.innerHTML=data.detail.heading,audienceDescription.innerHTML=data.detail.description,closeAudienceCardForm(audienceCard),(0,_str.getString)("audiencesaved","core_reportbuilder").then(_toast.add)})),audienceForm.addEventListener(audienceForm.events.FORM_CANCELLED,(()=>{audienceCard.dataset.audienceId>0?closeAudienceCardForm(audienceCard):removeAudienceCard(audienceCard)})),audienceForm},closeAudienceCardForm=audienceCard=>{const audienceFormContainer=audienceCard.querySelector(reportSelectors.regions.audienceFormContainer),NewAudienceFormContainer=audienceFormContainer.cloneNode(!1);audienceCard.querySelector(reportSelectors.regions.audienceForm).replaceChild(NewAudienceFormContainer,audienceFormContainer),audienceCard.querySelector(reportSelectors.regions.audienceDescription).classList.remove("hidden"),audienceCard.querySelector(reportSelectors.actions.audienceEdit).disabled=!1,audienceCard.querySelector(reportSelectors.actions.audienceDelete).disabled=!1},removeAudienceCard=audienceCard=>{audienceCard.remove();const audienceCards=document.querySelector(reportSelectors.regions.audiencesContainer).querySelectorAll(reportSelectors.regions.audienceCard);if(0===audienceCards.length){document.querySelector(reportSelectors.regions.audienceEmptyMessage).classList.remove("hidden")}else{const audienceFirstCardSeparator=audienceCards[0].querySelector(".audience-separator");null==audienceFirstCardSeparator||audienceFirstCardSeparator.remove()}};let initialized=!1;_exports.init=(id,contextid)=>{(0,_prefetch.prefetchStrings)("core_reportbuilder",["audienceadded","audiencedeleted","audiencesaved","audienceusedbyschedule","deleteaudience","deleteaudienceconfirm"]),(0,_prefetch.prefetchStrings)("core",["delete"]),reportId=id,contextId=contextid,initialized||(document.addEventListener("click",(event=>{const audienceAdd=event.target.closest(reportSelectors.actions.audienceAdd);audienceAdd&&(event.preventDefault(),((className,title)=>{const pendingPromise=new _pending.default("core_reportbuilder/audience:add"),audiencesContainer=document.querySelector(reportSelectors.regions.audiencesContainer),audienceCardLength=audiencesContainer.querySelectorAll(reportSelectors.regions.audienceCard).length,params={classname:className,reportid:reportId,showormessage:audienceCardLength>0,title:title};(0,_fragment.loadFragment)("core_reportbuilder","audience_form",contextId,params).then(((html,js)=>{const audienceCard=_templates.default.appendNodeContents(audiencesContainer,html,js)[0],audienceEmptyMessage=audiencesContainer.querySelector(reportSelectors.regions.audienceEmptyMessage),audienceForm=initAudienceCardForm(audienceCard);return(0,_changechecker.markFormAsDirty)(audienceForm.getFormNode()),audienceEmptyMessage.classList.add("hidden"),(0,_str.getString)("audienceadded","core_reportbuilder",title)})).then(_toast.add).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)})(audienceAdd.dataset.uniqueIdentifier,audienceAdd.dataset.name));const audienceEdit=event.target.closest(reportSelectors.actions.audienceEdit);if(audienceEdit){const audienceEditCard=audienceEdit.closest(reportSelectors.regions.audienceCard);event.preventDefault(),(audienceCard=>{const pendingPromise=new _pending.default("core_reportbuilder/audience:edit");initAudienceCardForm(audienceCard).load({id:audienceCard.dataset.audienceId}).then((()=>{const audienceFormContainer=audienceCard.querySelector(reportSelectors.regions.audienceFormContainer),audienceDescription=audienceCard.querySelector(reportSelectors.regions.audienceDescription),audienceEdit=audienceCard.querySelector(reportSelectors.actions.audienceEdit);return audienceFormContainer.classList.remove("hidden"),audienceDescription.classList.add("hidden"),audienceEdit.disabled=!0,pendingPromise.resolve()})).catch(_notification.default.exception)})(audienceEditCard)}const audienceDelete=event.target.closest(reportSelectors.actions.audienceDelete);audienceDelete&&(event.preventDefault(),(audienceDelete=>{const audienceCard=audienceDelete.closest(reportSelectors.regions.audienceCard),{audienceId:audienceId,audienceTitle:audienceTitle,audienceEditWarning:audienceEditWarning=!1}=audienceCard.dataset,audienceDeleteConfirmation=audienceEditWarning?"audienceusedbyschedule":"deleteaudienceconfirm";_notification.default.saveCancelPromise((0,_str.getString)("deleteaudience","core_reportbuilder",audienceTitle),(0,_str.getString)(audienceDeleteConfirmation,"core_reportbuilder",audienceTitle),(0,_str.getString)("delete","core"),{triggerElement:audienceDelete}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/audience:delete");return(0,_audiences.deleteAudience)(reportId,audienceId).then((()=>(0,_toast.add)((0,_str.getString)("audiencedeleted","core_reportbuilder",audienceTitle)))).then((()=>(removeAudienceCard(audienceCard),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))})(audienceDelete))})),initialized=!0)}})); //# sourceMappingURL=audience.min.js.map \ No newline at end of file diff --git a/reportbuilder/amd/build/audience.min.js.map b/reportbuilder/amd/build/audience.min.js.map index 2a4a030e72d..ab3e17e96a0 100644 --- a/reportbuilder/amd/build/audience.min.js.map +++ b/reportbuilder/amd/build/audience.min.js.map @@ -1 +1 @@ -{"version":3,"file":"audience.min.js","sources":["../src/audience.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 * Report builder audiences\n *\n * @module core_reportbuilder/audience\n * @copyright 2021 David Matamoros \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport 'core/inplace_editable';\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport DynamicForm from 'core_form/dynamicform';\nimport {add as addToast} from 'core/toast';\nimport {deleteAudience} from 'core_reportbuilder/local/repository/audiences';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {loadFragment} from 'core/fragment';\nimport {markFormAsDirty} from 'core_form/changechecker';\n\nlet reportId = 0;\nlet contextId = 0;\n\n/**\n * Add audience card\n *\n * @param {String} className\n * @param {String} title\n */\nconst addAudienceCard = (className, title) => {\n const pendingPromise = new Pending('core_reportbuilder/audience:add');\n\n const audiencesContainer = document.querySelector(reportSelectors.regions.audiencesContainer);\n const audienceCardLength = audiencesContainer.querySelectorAll(reportSelectors.regions.audienceCard).length;\n\n const params = {\n classname: className,\n reportid: reportId,\n showormessage: (audienceCardLength > 0),\n title: title,\n };\n\n // Load audience card fragment, render and then initialise the form within.\n loadFragment('core_reportbuilder', 'audience_form', contextId, params)\n .then((html, js) => {\n const audienceCard = Templates.appendNodeContents(audiencesContainer, html, js)[0];\n const audienceEmptyMessage = audiencesContainer.querySelector(reportSelectors.regions.audienceEmptyMessage);\n\n const audienceForm = initAudienceCardForm(audienceCard);\n // Mark as dirty new audience form created to prevent users leaving the page without saving it.\n markFormAsDirty(audienceForm.getFormNode());\n audienceEmptyMessage.classList.add('hidden');\n\n return getString('audienceadded', 'core_reportbuilder', title);\n })\n .then(addToast)\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n};\n\n/**\n * Edit audience card\n *\n * @param {Element} audienceCard\n */\nconst editAudienceCard = audienceCard => {\n const pendingPromise = new Pending('core_reportbuilder/audience:edit');\n\n // Load audience form with data for editing, then toggle visible controls in the card.\n const audienceForm = initAudienceCardForm(audienceCard);\n audienceForm.load({id: audienceCard.dataset.audienceId})\n .then(() => {\n const audienceFormContainer = audienceCard.querySelector(reportSelectors.regions.audienceFormContainer);\n const audienceDescription = audienceCard.querySelector(reportSelectors.regions.audienceDescription);\n const audienceEdit = audienceCard.querySelector(reportSelectors.actions.audienceEdit);\n\n audienceFormContainer.classList.remove('hidden');\n audienceDescription.classList.add('hidden');\n audienceEdit.disabled = true;\n\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n};\n\n/**\n * Initialise dynamic form within given audience card\n *\n * @param {Element} audienceCard\n * @return {DynamicForm}\n */\nconst initAudienceCardForm = audienceCard => {\n const audienceFormContainer = audienceCard.querySelector(reportSelectors.regions.audienceFormContainer);\n const audienceForm = new DynamicForm(audienceFormContainer, '\\\\core_reportbuilder\\\\form\\\\audience');\n\n // After submitting the form, update the card instance and description properties.\n audienceForm.addEventListener(audienceForm.events.FORM_SUBMITTED, data => {\n const audienceHeading = audienceCard.querySelector(reportSelectors.regions.audienceHeading);\n const audienceDescription = audienceCard.querySelector(reportSelectors.regions.audienceDescription);\n\n audienceCard.dataset.audienceId = data.detail.instanceid;\n\n audienceHeading.innerHTML = data.detail.heading;\n audienceDescription.innerHTML = data.detail.description;\n\n closeAudienceCardForm(audienceCard);\n\n return getString('audiencesaved', 'core_reportbuilder')\n .then(addToast);\n });\n\n // If cancelling the form, close the card or remove it if it was never created.\n audienceForm.addEventListener(audienceForm.events.FORM_CANCELLED, () => {\n if (audienceCard.dataset.audienceId > 0) {\n closeAudienceCardForm(audienceCard);\n } else {\n removeAudienceCard(audienceCard);\n }\n });\n\n return audienceForm;\n};\n\n/**\n * Delete audience card\n *\n * @param {Element} audienceDelete\n */\nconst deleteAudienceCard = audienceDelete => {\n const audienceCard = audienceDelete.closest(reportSelectors.regions.audienceCard);\n const {audienceId, audienceTitle, audienceEditWarning = false} = audienceCard.dataset;\n\n // The edit warning indicates the audience is in use in a report schedule.\n const audienceDeleteConfirmation = audienceEditWarning ? 'audienceusedbyschedule' : 'deleteaudienceconfirm';\n\n Notification.saveCancelPromise(\n getString('deleteaudience', 'core_reportbuilder', audienceTitle),\n getString(audienceDeleteConfirmation, 'core_reportbuilder', audienceTitle),\n getString('delete', 'core'),\n {triggerElement: audienceDelete}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/audience:delete');\n\n return deleteAudience(reportId, audienceId)\n .then(() => addToast(getString('audiencedeleted', 'core_reportbuilder', audienceTitle)))\n .then(() => {\n removeAudienceCard(audienceCard);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n};\n\n/**\n * Close audience card form\n *\n * @param {Element} audienceCard\n */\nconst closeAudienceCardForm = audienceCard => {\n // Remove the [data-region=\"audience-form-container\"] (with all the event listeners attached to it), and create it again.\n const audienceFormContainer = audienceCard.querySelector(reportSelectors.regions.audienceFormContainer);\n const NewAudienceFormContainer = audienceFormContainer.cloneNode(false);\n audienceCard.querySelector(reportSelectors.regions.audienceForm).replaceChild(NewAudienceFormContainer, audienceFormContainer);\n // Show the description container and enable the action buttons.\n audienceCard.querySelector(reportSelectors.regions.audienceDescription).classList.remove('hidden');\n audienceCard.querySelector(reportSelectors.actions.audienceEdit).disabled = false;\n audienceCard.querySelector(reportSelectors.actions.audienceDelete).disabled = false;\n};\n\n/**\n * Remove audience card\n *\n * @param {Element} audienceCard\n */\nconst removeAudienceCard = audienceCard => {\n audienceCard.remove();\n\n const audiencesContainer = document.querySelector(reportSelectors.regions.audiencesContainer);\n const audienceCards = audiencesContainer.querySelectorAll(reportSelectors.regions.audienceCard);\n\n // Show message if there are no cards remaining, ensure first card's separator is not present.\n if (audienceCards.length === 0) {\n const audienceEmptyMessage = document.querySelector(reportSelectors.regions.audienceEmptyMessage);\n audienceEmptyMessage.classList.remove('hidden');\n } else {\n const audienceFirstCardSeparator = audienceCards[0].querySelector('.audience-separator');\n audienceFirstCardSeparator?.remove();\n }\n};\n\nlet initialized = false;\n\n/**\n * Initialise audiences tab.\n *\n * @param {Number} id\n * @param {Number} contextid\n */\nexport const init = (id, contextid) => {\n prefetchStrings('core_reportbuilder', [\n 'audienceadded',\n 'audiencedeleted',\n 'audiencesaved',\n 'audienceusedbyschedule',\n 'deleteaudience',\n 'deleteaudienceconfirm',\n ]);\n\n prefetchStrings('core', [\n 'delete',\n ]);\n\n reportId = id;\n contextId = contextid;\n\n if (initialized) {\n // We already added the event listeners (can be called multiple times by mustache template).\n return;\n }\n\n document.addEventListener('click', event => {\n\n // Add instance.\n const audienceAdd = event.target.closest(reportSelectors.actions.audienceAdd);\n if (audienceAdd) {\n event.preventDefault();\n addAudienceCard(audienceAdd.dataset.uniqueIdentifier, audienceAdd.dataset.name);\n }\n\n // Edit instance.\n const audienceEdit = event.target.closest(reportSelectors.actions.audienceEdit);\n if (audienceEdit) {\n const audienceEditCard = audienceEdit.closest(reportSelectors.regions.audienceCard);\n\n event.preventDefault();\n editAudienceCard(audienceEditCard);\n }\n\n // Delete instance.\n const audienceDelete = event.target.closest(reportSelectors.actions.audienceDelete);\n if (audienceDelete) {\n event.preventDefault();\n deleteAudienceCard(audienceDelete);\n }\n });\n\n initialized = true;\n};\n"],"names":["reportId","contextId","initAudienceCardForm","audienceCard","audienceFormContainer","querySelector","reportSelectors","regions","audienceForm","DynamicForm","addEventListener","events","FORM_SUBMITTED","data","audienceHeading","audienceDescription","dataset","audienceId","detail","instanceid","innerHTML","heading","description","closeAudienceCardForm","then","addToast","FORM_CANCELLED","removeAudienceCard","NewAudienceFormContainer","cloneNode","replaceChild","classList","remove","actions","audienceEdit","disabled","audienceDelete","audienceCards","document","audiencesContainer","querySelectorAll","length","audienceEmptyMessage","audienceFirstCardSeparator","initialized","id","contextid","event","audienceAdd","target","closest","preventDefault","className","title","pendingPromise","Pending","audienceCardLength","params","classname","reportid","showormessage","html","js","Templates","appendNodeContents","getFormNode","add","resolve","catch","Notification","exception","addAudienceCard","uniqueIdentifier","name","audienceEditCard","load","editAudienceCard","audienceTitle","audienceEditWarning","audienceDeleteConfirmation","saveCancelPromise","triggerElement","deleteAudienceCard"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"audience.min.js","sources":["../src/audience.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 * Report builder audiences\n *\n * @module core_reportbuilder/audience\n * @copyright 2021 David Matamoros \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport 'core/inplace_editable';\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport DynamicForm from 'core_form/dynamicform';\nimport {add as addToast} from 'core/toast';\nimport {deleteAudience} from 'core_reportbuilder/local/repository/audiences';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {loadFragment} from 'core/fragment';\nimport {markFormAsDirty} from 'core_form/changechecker';\n\nlet reportId = 0;\nlet contextId = 0;\n\n/**\n * Add audience card\n *\n * @param {String} className\n * @param {String} title\n */\nconst addAudienceCard = (className, title) => {\n const pendingPromise = new Pending('core_reportbuilder/audience:add');\n\n const audiencesContainer = document.querySelector(reportSelectors.regions.audiencesContainer);\n const audienceCardLength = audiencesContainer.querySelectorAll(reportSelectors.regions.audienceCard).length;\n\n const params = {\n classname: className,\n reportid: reportId,\n showormessage: (audienceCardLength > 0),\n title: title,\n };\n\n // Load audience card fragment, render and then initialise the form within.\n loadFragment('core_reportbuilder', 'audience_form', contextId, params)\n .then((html, js) => {\n const audienceCard = Templates.appendNodeContents(audiencesContainer, html, js)[0];\n const audienceEmptyMessage = audiencesContainer.querySelector(reportSelectors.regions.audienceEmptyMessage);\n\n const audienceForm = initAudienceCardForm(audienceCard);\n // Mark as dirty new audience form created to prevent users leaving the page without saving it.\n markFormAsDirty(audienceForm.getFormNode());\n audienceEmptyMessage.classList.add('hidden');\n\n return getString('audienceadded', 'core_reportbuilder', title);\n })\n .then(addToast)\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n};\n\n/**\n * Edit audience card\n *\n * @param {Element} audienceCard\n */\nconst editAudienceCard = audienceCard => {\n const pendingPromise = new Pending('core_reportbuilder/audience:edit');\n\n // Load audience form with data for editing, then toggle visible controls in the card.\n const audienceForm = initAudienceCardForm(audienceCard);\n audienceForm.load({id: audienceCard.dataset.audienceId})\n .then(() => {\n const audienceFormContainer = audienceCard.querySelector(reportSelectors.regions.audienceFormContainer);\n const audienceDescription = audienceCard.querySelector(reportSelectors.regions.audienceDescription);\n const audienceEdit = audienceCard.querySelector(reportSelectors.actions.audienceEdit);\n\n audienceFormContainer.classList.remove('hidden');\n audienceDescription.classList.add('hidden');\n audienceEdit.disabled = true;\n\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n};\n\n/**\n * Initialise dynamic form within given audience card\n *\n * @param {Element} audienceCard\n * @return {DynamicForm}\n */\nconst initAudienceCardForm = audienceCard => {\n const audienceFormContainer = audienceCard.querySelector(reportSelectors.regions.audienceFormContainer);\n const audienceForm = new DynamicForm(audienceFormContainer, '\\\\core_reportbuilder\\\\form\\\\audience');\n\n // After submitting the form, update the card instance and description properties.\n audienceForm.addEventListener(audienceForm.events.FORM_SUBMITTED, data => {\n const audienceHeading = audienceCard.querySelector(reportSelectors.regions.audienceHeading);\n const audienceDescription = audienceCard.querySelector(reportSelectors.regions.audienceDescription);\n\n audienceCard.dataset.audienceId = data.detail.instanceid;\n\n audienceHeading.innerHTML = data.detail.heading;\n audienceDescription.innerHTML = data.detail.description;\n\n closeAudienceCardForm(audienceCard);\n\n return getString('audiencesaved', 'core_reportbuilder')\n .then(addToast);\n });\n\n // If cancelling the form, close the card or remove it if it was never created.\n audienceForm.addEventListener(audienceForm.events.FORM_CANCELLED, () => {\n if (audienceCard.dataset.audienceId > 0) {\n closeAudienceCardForm(audienceCard);\n } else {\n removeAudienceCard(audienceCard);\n }\n });\n\n return audienceForm;\n};\n\n/**\n * Delete audience card\n *\n * @param {Element} audienceDelete\n */\nconst deleteAudienceCard = audienceDelete => {\n const audienceCard = audienceDelete.closest(reportSelectors.regions.audienceCard);\n const {audienceId, audienceTitle, audienceEditWarning = false} = audienceCard.dataset;\n\n // The edit warning indicates the audience is in use in a report schedule.\n const audienceDeleteConfirmation = audienceEditWarning ? 'audienceusedbyschedule' : 'deleteaudienceconfirm';\n\n Notification.saveCancelPromise(\n getString('deleteaudience', 'core_reportbuilder', audienceTitle),\n getString(audienceDeleteConfirmation, 'core_reportbuilder', audienceTitle),\n getString('delete', 'core'),\n {triggerElement: audienceDelete}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/audience:delete');\n\n return deleteAudience(reportId, audienceId)\n .then(() => addToast(getString('audiencedeleted', 'core_reportbuilder', audienceTitle)))\n .then(() => {\n removeAudienceCard(audienceCard);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n};\n\n/**\n * Close audience card form\n *\n * @param {Element} audienceCard\n */\nconst closeAudienceCardForm = audienceCard => {\n // Remove the [data-region=\"audience-form-container\"] (with all the event listeners attached to it), and create it again.\n const audienceFormContainer = audienceCard.querySelector(reportSelectors.regions.audienceFormContainer);\n const NewAudienceFormContainer = audienceFormContainer.cloneNode(false);\n audienceCard.querySelector(reportSelectors.regions.audienceForm).replaceChild(NewAudienceFormContainer, audienceFormContainer);\n // Show the description container and enable the action buttons.\n audienceCard.querySelector(reportSelectors.regions.audienceDescription).classList.remove('hidden');\n audienceCard.querySelector(reportSelectors.actions.audienceEdit).disabled = false;\n audienceCard.querySelector(reportSelectors.actions.audienceDelete).disabled = false;\n};\n\n/**\n * Remove audience card\n *\n * @param {Element} audienceCard\n */\nconst removeAudienceCard = audienceCard => {\n audienceCard.remove();\n\n const audiencesContainer = document.querySelector(reportSelectors.regions.audiencesContainer);\n const audienceCards = audiencesContainer.querySelectorAll(reportSelectors.regions.audienceCard);\n\n // Show message if there are no cards remaining, ensure first card's separator is not present.\n if (audienceCards.length === 0) {\n const audienceEmptyMessage = document.querySelector(reportSelectors.regions.audienceEmptyMessage);\n audienceEmptyMessage.classList.remove('hidden');\n } else {\n const audienceFirstCardSeparator = audienceCards[0].querySelector('.audience-separator');\n audienceFirstCardSeparator?.remove();\n }\n};\n\nlet initialized = false;\n\n/**\n * Initialise audiences tab.\n *\n * @param {Number} id\n * @param {Number} contextid\n */\nexport const init = (id, contextid) => {\n prefetchStrings('core_reportbuilder', [\n 'audienceadded',\n 'audiencedeleted',\n 'audiencesaved',\n 'audienceusedbyschedule',\n 'deleteaudience',\n 'deleteaudienceconfirm',\n ]);\n\n prefetchStrings('core', [\n 'delete',\n ]);\n\n reportId = id;\n contextId = contextid;\n\n if (initialized) {\n // We already added the event listeners (can be called multiple times by mustache template).\n return;\n }\n\n document.addEventListener('click', event => {\n\n // Add instance.\n const audienceAdd = event.target.closest(reportSelectors.actions.audienceAdd);\n if (audienceAdd) {\n event.preventDefault();\n addAudienceCard(audienceAdd.dataset.uniqueIdentifier, audienceAdd.dataset.name);\n }\n\n // Edit instance.\n const audienceEdit = event.target.closest(reportSelectors.actions.audienceEdit);\n if (audienceEdit) {\n const audienceEditCard = audienceEdit.closest(reportSelectors.regions.audienceCard);\n\n event.preventDefault();\n editAudienceCard(audienceEditCard);\n }\n\n // Delete instance.\n const audienceDelete = event.target.closest(reportSelectors.actions.audienceDelete);\n if (audienceDelete) {\n event.preventDefault();\n deleteAudienceCard(audienceDelete);\n }\n });\n\n initialized = true;\n};\n"],"names":["reportId","contextId","initAudienceCardForm","audienceCard","audienceFormContainer","querySelector","reportSelectors","regions","audienceForm","DynamicForm","addEventListener","events","FORM_SUBMITTED","data","audienceHeading","audienceDescription","dataset","audienceId","detail","instanceid","innerHTML","heading","description","closeAudienceCardForm","then","addToast","FORM_CANCELLED","removeAudienceCard","NewAudienceFormContainer","cloneNode","replaceChild","classList","remove","actions","audienceEdit","disabled","audienceDelete","audienceCards","document","audiencesContainer","querySelectorAll","length","audienceEmptyMessage","audienceFirstCardSeparator","initialized","id","contextid","event","audienceAdd","target","closest","preventDefault","className","title","pendingPromise","Pending","audienceCardLength","params","classname","reportid","showormessage","html","js","Templates","appendNodeContents","getFormNode","add","resolve","catch","Notification","exception","addAudienceCard","uniqueIdentifier","name","audienceEditCard","load","editAudienceCard","audienceTitle","audienceEditWarning","audienceDeleteConfirmation","saveCancelPromise","triggerElement","deleteAudienceCard"],"mappings":"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"} \ No newline at end of file diff --git a/reportbuilder/amd/build/editor.min.js b/reportbuilder/amd/build/editor.min.js index a56734144fc..87dc787c749 100644 --- a/reportbuilder/amd/build/editor.min.js +++ b/reportbuilder/amd/build/editor.min.js @@ -1,3 +1,3 @@ -define("core_reportbuilder/editor",["exports","jquery","core/inplace_editable","core/loadingicon","core/notification","core/pending","core/templates","core/str","core/toast","core_reportbuilder/local/selectors","core_reportbuilder/local/editor/columns","core_reportbuilder/local/editor/conditions","core_reportbuilder/local/editor/filters","core_reportbuilder/local/editor/sorting","core_reportbuilder/local/editor/card_view","core_reportbuilder/local/repository/reports","core_reportbuilder/local/repository/modals"],(function(_exports,_jquery,_inplace_editable,_loadingicon,_notification,_pending,_templates,_str,_toast,reportSelectors,_columns,_conditions,_filters,_sorting,_card_view,_reports,_modals){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_templates=_interopRequireDefault(_templates),reportSelectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(reportSelectors);let initialized=!1;_exports.init=()=>{(0,_columns.init)(initialized),(0,_conditions.init)(initialized),(0,_filters.init)(initialized),(0,_sorting.init)(initialized),(0,_card_view.init)(initialized),initialized||(document.addEventListener("click",(event=>{const toggleEditViewMode=event.target.closest(reportSelectors.actions.toggleEditPreview);if(toggleEditViewMode){event.preventDefault();const reportElement=event.target.closest(reportSelectors.regions.report),pendingPromise=new _pending.default("core_reportbuilder/reports:get"),toggledEditMode="1"!==toggleEditViewMode.dataset.editMode;(0,_loadingicon.addIconToContainer)(toggleEditViewMode).then((()=>(0,_reports.getReport)(reportElement.dataset.reportId,toggledEditMode))).then((response=>Promise.all([_jquery.default.parseHTML(response.javascript,null,!0).map((node=>node.innerHTML)).join("\n"),_templates.default.renderForPromise("core_reportbuilder/local/dynamictabs/editor",response)]))).then((_ref=>{let[responseJs,{html:html,js:js}]=_ref;return _templates.default.replaceNode(reportElement,html,js+responseJs)})).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)}const reportEdit=event.target.closest(reportSelectors.actions.reportEdit);if(reportEdit){event.preventDefault();const reportModal=(0,_modals.createReportModal)(event.target,(0,_str.get_string)("editreportdetails","core_reportbuilder"),reportEdit.dataset.reportId);reportModal.addEventListener(reportModal.events.FORM_SUBMITTED,(()=>{(0,_str.get_string)("reportupdated","core_reportbuilder").then(_toast.add).then((()=>window.location.reload())).catch(_notification.default.exception)})),reportModal.show()}})),initialized=!0)}})); +define("core_reportbuilder/editor",["exports","jquery","core/inplace_editable","core/loadingicon","core/notification","core/pending","core/templates","core/str","core/toast","core_reportbuilder/local/selectors","core_reportbuilder/local/editor/columns","core_reportbuilder/local/editor/conditions","core_reportbuilder/local/editor/filters","core_reportbuilder/local/editor/sorting","core_reportbuilder/local/editor/card_view","core_reportbuilder/local/repository/reports","core_reportbuilder/local/repository/modals"],(function(_exports,_jquery,_inplace_editable,_loadingicon,_notification,_pending,_templates,_str,_toast,reportSelectors,_columns,_conditions,_filters,_sorting,_card_view,_reports,_modals){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_templates=_interopRequireDefault(_templates),reportSelectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(reportSelectors);let initialized=!1;_exports.init=()=>{(0,_columns.init)(initialized),(0,_conditions.init)(initialized),(0,_filters.init)(initialized),(0,_sorting.init)(initialized),(0,_card_view.init)(initialized),initialized||(document.addEventListener("click",(event=>{const toggleEditViewMode=event.target.closest(reportSelectors.actions.toggleEditPreview);if(toggleEditViewMode){event.preventDefault();const reportElement=event.target.closest(reportSelectors.regions.report),pendingPromise=new _pending.default("core_reportbuilder/reports:get"),toggledEditMode="1"!==toggleEditViewMode.dataset.editMode;(0,_loadingicon.addIconToContainer)(toggleEditViewMode).then((()=>(0,_reports.getReport)(reportElement.dataset.reportId,toggledEditMode))).then((response=>Promise.all([_jquery.default.parseHTML(response.javascript,null,!0).map((node=>node.innerHTML)).join("\n"),_templates.default.renderForPromise("core_reportbuilder/local/dynamictabs/editor",response)]))).then((_ref=>{let[responseJs,{html:html,js:js}]=_ref;return _templates.default.replaceNode(reportElement,html,js+responseJs)})).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)}const reportEdit=event.target.closest(reportSelectors.actions.reportEdit);if(reportEdit){event.preventDefault();const reportModal=(0,_modals.createReportModal)(event.target,(0,_str.getString)("editreportdetails","core_reportbuilder"),reportEdit.dataset.reportId);reportModal.addEventListener(reportModal.events.FORM_SUBMITTED,(()=>{(0,_str.getString)("reportupdated","core_reportbuilder").then(_toast.add).then((()=>window.location.reload())).catch(_notification.default.exception)})),reportModal.show()}})),initialized=!0)}})); //# sourceMappingURL=editor.min.js.map \ No newline at end of file diff --git a/reportbuilder/amd/build/editor.min.js.map b/reportbuilder/amd/build/editor.min.js.map index 488e7a52b0d..d8511b95c62 100644 --- a/reportbuilder/amd/build/editor.min.js.map +++ b/reportbuilder/amd/build/editor.min.js.map @@ -1 +1 @@ -{"version":3,"file":"editor.min.js","sources":["../src/editor.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 * Report builder editor\n *\n * @module core_reportbuilder/editor\n * @copyright 2021 David Matamoros \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport $ from 'jquery';\nimport 'core/inplace_editable';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Templates from 'core/templates';\nimport {get_string as getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {init as columnsEditorInit} from 'core_reportbuilder/local/editor/columns';\nimport {init as conditionsEditorInit} from 'core_reportbuilder/local/editor/conditions';\nimport {init as filtersEditorInit} from 'core_reportbuilder/local/editor/filters';\nimport {init as sortingEditorInit} from 'core_reportbuilder/local/editor/sorting';\nimport {init as cardviewEditorInit} from 'core_reportbuilder/local/editor/card_view';\nimport {getReport} from 'core_reportbuilder/local/repository/reports';\nimport {createReportModal} from 'core_reportbuilder/local/repository/modals';\n\nlet initialized = false;\n\n/**\n * Initialise editor and all it's modules\n */\nexport const init = () => {\n columnsEditorInit(initialized);\n conditionsEditorInit(initialized);\n filtersEditorInit(initialized);\n sortingEditorInit(initialized);\n cardviewEditorInit(initialized);\n\n // Ensure we only add our listeners once (can be called multiple times by mustache template).\n if (initialized) {\n return;\n }\n\n // Add event handlers to generic report editor elements.\n document.addEventListener('click', event => {\n\n // Toggle between edit and preview mode.\n const toggleEditViewMode = event.target.closest(reportSelectors.actions.toggleEditPreview);\n if (toggleEditViewMode) {\n event.preventDefault();\n\n const reportElement = event.target.closest(reportSelectors.regions.report);\n const pendingPromise = new Pending('core_reportbuilder/reports:get');\n const toggledEditMode = toggleEditViewMode.dataset.editMode !== \"1\";\n\n addIconToContainer(toggleEditViewMode)\n .then(() => getReport(reportElement.dataset.reportId, toggledEditMode))\n .then(response => Promise.all([\n $.parseHTML(response.javascript, null, true).map(node => node.innerHTML).join(\"\\n\"),\n Templates.renderForPromise('core_reportbuilder/local/dynamictabs/editor', response),\n ]))\n .then(([responseJs, {html, js}]) => Templates.replaceNode(reportElement, html, js + responseJs))\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }\n\n // Edit report details modal.\n const reportEdit = event.target.closest(reportSelectors.actions.reportEdit);\n if (reportEdit) {\n event.preventDefault();\n\n const reportModal = createReportModal(event.target, getString('editreportdetails', 'core_reportbuilder'),\n reportEdit.dataset.reportId);\n reportModal.addEventListener(reportModal.events.FORM_SUBMITTED, () => {\n getString('reportupdated', 'core_reportbuilder')\n .then(addToast)\n .then(() => {\n return window.location.reload();\n })\n .catch(Notification.exception);\n });\n reportModal.show();\n }\n });\n\n initialized = true;\n};\n"],"names":["initialized","document","addEventListener","event","toggleEditViewMode","target","closest","reportSelectors","actions","toggleEditPreview","preventDefault","reportElement","regions","report","pendingPromise","Pending","toggledEditMode","dataset","editMode","then","reportId","response","Promise","all","$","parseHTML","javascript","map","node","innerHTML","join","Templates","renderForPromise","_ref","responseJs","html","js","replaceNode","resolve","catch","Notification","exception","reportEdit","reportModal","events","FORM_SUBMITTED","addToast","window","location","reload","show"],"mappings":"09DA0CIA,aAAc,gBAKE,uBACEA,kCACGA,+BACHA,+BACAA,iCACCA,aAGfA,cAKJC,SAASC,iBAAiB,SAASC,cAGzBC,mBAAqBD,MAAME,OAAOC,QAAQC,gBAAgBC,QAAQC,sBACpEL,mBAAoB,CACpBD,MAAMO,uBAEAC,cAAgBR,MAAME,OAAOC,QAAQC,gBAAgBK,QAAQC,QAC7DC,eAAiB,IAAIC,iBAAQ,kCAC7BC,gBAA0D,MAAxCZ,mBAAmBa,QAAQC,6CAEhCd,oBACde,MAAK,KAAM,sBAAUR,cAAcM,QAAQG,SAAUJ,mBACrDG,MAAKE,UAAYC,QAAQC,IAAI,CAC1BC,gBAAEC,UAAUJ,SAASK,WAAY,MAAM,GAAMC,KAAIC,MAAQA,KAAKC,YAAWC,KAAK,MAC9EC,mBAAUC,iBAAiB,8CAA+CX,cAE7EF,MAAKc,WAAEC,YAAYC,KAACA,KAADC,GAAOA,iBAASL,mBAAUM,YAAY1B,cAAewB,KAAMC,GAAKF,eACnFf,MAAK,IAAML,eAAewB,YAC1BC,MAAMC,sBAAaC,iBAItBC,WAAavC,MAAME,OAAOC,QAAQC,gBAAgBC,QAAQkC,eAC5DA,WAAY,CACZvC,MAAMO,uBAEAiC,aAAc,6BAAkBxC,MAAME,QAAQ,mBAAU,oBAAqB,sBAC/EqC,WAAWzB,QAAQG,UACvBuB,YAAYzC,iBAAiByC,YAAYC,OAAOC,gBAAgB,yBAClD,gBAAiB,sBACtB1B,KAAK2B,YACL3B,MAAK,IACK4B,OAAOC,SAASC,WAE1BV,MAAMC,sBAAaC,cAE5BE,YAAYO,WAIpBlD,aAAc"} \ No newline at end of file +{"version":3,"file":"editor.min.js","sources":["../src/editor.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 * Report builder editor\n *\n * @module core_reportbuilder/editor\n * @copyright 2021 David Matamoros \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport $ from 'jquery';\nimport 'core/inplace_editable';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {init as columnsEditorInit} from 'core_reportbuilder/local/editor/columns';\nimport {init as conditionsEditorInit} from 'core_reportbuilder/local/editor/conditions';\nimport {init as filtersEditorInit} from 'core_reportbuilder/local/editor/filters';\nimport {init as sortingEditorInit} from 'core_reportbuilder/local/editor/sorting';\nimport {init as cardviewEditorInit} from 'core_reportbuilder/local/editor/card_view';\nimport {getReport} from 'core_reportbuilder/local/repository/reports';\nimport {createReportModal} from 'core_reportbuilder/local/repository/modals';\n\nlet initialized = false;\n\n/**\n * Initialise editor and all it's modules\n */\nexport const init = () => {\n columnsEditorInit(initialized);\n conditionsEditorInit(initialized);\n filtersEditorInit(initialized);\n sortingEditorInit(initialized);\n cardviewEditorInit(initialized);\n\n // Ensure we only add our listeners once (can be called multiple times by mustache template).\n if (initialized) {\n return;\n }\n\n // Add event handlers to generic report editor elements.\n document.addEventListener('click', event => {\n\n // Toggle between edit and preview mode.\n const toggleEditViewMode = event.target.closest(reportSelectors.actions.toggleEditPreview);\n if (toggleEditViewMode) {\n event.preventDefault();\n\n const reportElement = event.target.closest(reportSelectors.regions.report);\n const pendingPromise = new Pending('core_reportbuilder/reports:get');\n const toggledEditMode = toggleEditViewMode.dataset.editMode !== \"1\";\n\n addIconToContainer(toggleEditViewMode)\n .then(() => getReport(reportElement.dataset.reportId, toggledEditMode))\n .then(response => Promise.all([\n $.parseHTML(response.javascript, null, true).map(node => node.innerHTML).join(\"\\n\"),\n Templates.renderForPromise('core_reportbuilder/local/dynamictabs/editor', response),\n ]))\n .then(([responseJs, {html, js}]) => Templates.replaceNode(reportElement, html, js + responseJs))\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }\n\n // Edit report details modal.\n const reportEdit = event.target.closest(reportSelectors.actions.reportEdit);\n if (reportEdit) {\n event.preventDefault();\n\n const reportModal = createReportModal(event.target, getString('editreportdetails', 'core_reportbuilder'),\n reportEdit.dataset.reportId);\n reportModal.addEventListener(reportModal.events.FORM_SUBMITTED, () => {\n getString('reportupdated', 'core_reportbuilder')\n .then(addToast)\n .then(() => {\n return window.location.reload();\n })\n .catch(Notification.exception);\n });\n reportModal.show();\n }\n });\n\n initialized = true;\n};\n"],"names":["initialized","document","addEventListener","event","toggleEditViewMode","target","closest","reportSelectors","actions","toggleEditPreview","preventDefault","reportElement","regions","report","pendingPromise","Pending","toggledEditMode","dataset","editMode","then","reportId","response","Promise","all","$","parseHTML","javascript","map","node","innerHTML","join","Templates","renderForPromise","_ref","responseJs","html","js","replaceNode","resolve","catch","Notification","exception","reportEdit","reportModal","events","FORM_SUBMITTED","addToast","window","location","reload","show"],"mappings":"09DA0CIA,aAAc,gBAKE,uBACEA,kCACGA,+BACHA,+BACAA,iCACCA,aAGfA,cAKJC,SAASC,iBAAiB,SAASC,cAGzBC,mBAAqBD,MAAME,OAAOC,QAAQC,gBAAgBC,QAAQC,sBACpEL,mBAAoB,CACpBD,MAAMO,uBAEAC,cAAgBR,MAAME,OAAOC,QAAQC,gBAAgBK,QAAQC,QAC7DC,eAAiB,IAAIC,iBAAQ,kCAC7BC,gBAA0D,MAAxCZ,mBAAmBa,QAAQC,6CAEhCd,oBACde,MAAK,KAAM,sBAAUR,cAAcM,QAAQG,SAAUJ,mBACrDG,MAAKE,UAAYC,QAAQC,IAAI,CAC1BC,gBAAEC,UAAUJ,SAASK,WAAY,MAAM,GAAMC,KAAIC,MAAQA,KAAKC,YAAWC,KAAK,MAC9EC,mBAAUC,iBAAiB,8CAA+CX,cAE7EF,MAAKc,WAAEC,YAAYC,KAACA,KAADC,GAAOA,iBAASL,mBAAUM,YAAY1B,cAAewB,KAAMC,GAAKF,eACnFf,MAAK,IAAML,eAAewB,YAC1BC,MAAMC,sBAAaC,iBAItBC,WAAavC,MAAME,OAAOC,QAAQC,gBAAgBC,QAAQkC,eAC5DA,WAAY,CACZvC,MAAMO,uBAEAiC,aAAc,6BAAkBxC,MAAME,QAAQ,kBAAU,oBAAqB,sBAC/EqC,WAAWzB,QAAQG,UACvBuB,YAAYzC,iBAAiByC,YAAYC,OAAOC,gBAAgB,wBAClD,gBAAiB,sBACtB1B,KAAK2B,YACL3B,MAAK,IACK4B,OAAOC,SAASC,WAE1BV,MAAMC,sBAAaC,cAE5BE,YAAYO,WAIpBlD,aAAc"} \ No newline at end of file diff --git a/reportbuilder/amd/build/filters.min.js b/reportbuilder/amd/build/filters.min.js index 05b9c0926f6..79761b920e6 100644 --- a/reportbuilder/amd/build/filters.min.js +++ b/reportbuilder/amd/build/filters.min.js @@ -5,6 +5,6 @@ define("core_reportbuilder/filters",["exports","core/event_dispatcher","core/fra * @module core_reportbuilder/filters * @copyright 2021 Paul Holden * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_templates=_interopRequireDefault(_templates),_dynamicform=_interopRequireDefault(_dynamicform),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);const setFilterButtonCount=async(reportElement,filterCount)=>{const filterButtonLabel=reportElement.querySelector(reportSelectors.regions.filterButtonLabel);filterButtonLabel.textContent=filterCount>0?await(0,_str.get_string)("filtersappliedx","core_reportbuilder",filterCount):await(0,_str.get_string)("filters","moodle")};_exports.init=(reportId,contextId)=>{const reportElement=document.querySelector(reportSelectors.forReport(reportId)),filterFormContainer=reportElement.querySelector(reportSelectors.regions.filtersForm);if(filterFormContainer.dataset.initialized)return;filterFormContainer.dataset.initialized=!0;const filterForm=new _dynamicform.default(filterFormContainer,"\\core_reportbuilder\\form\\filter");filterForm.addEventListener(filterForm.events.FORM_SUBMITTED,(event=>{event.preventDefault(),(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),setFilterButtonCount(reportElement,event.detail),(0,_str.get_string)("filtersapplied","core_reportbuilder").then(_toast.add).catch(_notification.default.exception)})),filterForm.addEventListener(filterForm.events.NOSUBMIT_BUTTON_PRESSED,(event=>{event.preventDefault();const pendingPromise=new _pending.default("core_reportbuilder/filters:reset"),reportParameters=reportElement.dataset.parameter;(0,_filters.resetFilters)(reportId,reportParameters).then((()=>(0,_str.get_string)("filtersreset","core_reportbuilder"))).then(_toast.add).then((()=>(0,_fragment.loadFragment)("core_reportbuilder","filters_form",contextId,{reportid:reportId,parameters:reportParameters}))).then(((html,js)=>(_templates.default.replaceNodeContents(filterFormContainer,html,js),(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),setFilterButtonCount(reportElement,0),pendingPromise.resolve()))).catch(_notification.default.exception)})),document.querySelector("#region-main").style.overflowX="visible"}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_templates=_interopRequireDefault(_templates),_dynamicform=_interopRequireDefault(_dynamicform),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);const setFilterButtonCount=async(reportElement,filterCount)=>{const filterButtonLabel=reportElement.querySelector(reportSelectors.regions.filterButtonLabel);filterButtonLabel.textContent=filterCount>0?await(0,_str.getString)("filtersappliedx","core_reportbuilder",filterCount):await(0,_str.getString)("filters","moodle")};_exports.init=(reportId,contextId)=>{const reportElement=document.querySelector(reportSelectors.forReport(reportId)),filterFormContainer=reportElement.querySelector(reportSelectors.regions.filtersForm);if(filterFormContainer.dataset.initialized)return;filterFormContainer.dataset.initialized=!0;const filterForm=new _dynamicform.default(filterFormContainer,"\\core_reportbuilder\\form\\filter");filterForm.addEventListener(filterForm.events.FORM_SUBMITTED,(event=>{event.preventDefault(),(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),setFilterButtonCount(reportElement,event.detail),(0,_str.getString)("filtersapplied","core_reportbuilder").then(_toast.add).catch(_notification.default.exception)})),filterForm.addEventListener(filterForm.events.NOSUBMIT_BUTTON_PRESSED,(event=>{event.preventDefault();const pendingPromise=new _pending.default("core_reportbuilder/filters:reset"),reportParameters=reportElement.dataset.parameter;(0,_filters.resetFilters)(reportId,reportParameters).then((()=>(0,_str.getString)("filtersreset","core_reportbuilder"))).then(_toast.add).then((()=>(0,_fragment.loadFragment)("core_reportbuilder","filters_form",contextId,{reportid:reportId,parameters:reportParameters}))).then(((html,js)=>(_templates.default.replaceNodeContents(filterFormContainer,html,js),(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),setFilterButtonCount(reportElement,0),pendingPromise.resolve()))).catch(_notification.default.exception)})),document.querySelector("#region-main").style.overflowX="visible"}})); //# sourceMappingURL=filters.min.js.map \ No newline at end of file diff --git a/reportbuilder/amd/build/filters.min.js.map b/reportbuilder/amd/build/filters.min.js.map index 9fce2ff61b8..06cc2f700f0 100644 --- a/reportbuilder/amd/build/filters.min.js.map +++ b/reportbuilder/amd/build/filters.min.js.map @@ -1 +1 @@ -{"version":3,"file":"filters.min.js","sources":["../src/filters.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 * Report builder filter management\n *\n * @module core_reportbuilder/filters\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport {loadFragment} from 'core/fragment';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {get_string as getString} from 'core/str';\nimport Templates from 'core/templates';\nimport {add as addToast} from 'core/toast';\nimport DynamicForm from 'core_form/dynamicform';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {resetFilters} from 'core_reportbuilder/local/repository/filters';\n\n/**\n * Update filter button text to indicate applied filter count\n *\n * @param {Element} reportElement\n * @param {Number} filterCount\n */\nconst setFilterButtonCount = async(reportElement, filterCount) => {\n const filterButtonLabel = reportElement.querySelector(reportSelectors.regions.filterButtonLabel);\n\n if (filterCount > 0) {\n filterButtonLabel.textContent = await getString('filtersappliedx', 'core_reportbuilder', filterCount);\n } else {\n filterButtonLabel.textContent = await getString('filters', 'moodle');\n }\n};\n\n/**\n * Initialise module for given report\n *\n * @method\n * @param {Number} reportId\n * @param {Number} contextId\n */\nexport const init = (reportId, contextId) => {\n const reportElement = document.querySelector(reportSelectors.forReport(reportId));\n const filterFormContainer = reportElement.querySelector(reportSelectors.regions.filtersForm);\n\n // Ensure we only add our listeners once (can be called multiple times by mustache template).\n if (filterFormContainer.dataset.initialized) {\n return;\n }\n filterFormContainer.dataset.initialized = true;\n\n const filterForm = new DynamicForm(filterFormContainer, '\\\\core_reportbuilder\\\\form\\\\filter');\n\n // Submit report filters.\n filterForm.addEventListener(filterForm.events.FORM_SUBMITTED, event => {\n event.preventDefault();\n\n // After the form has been submitted, we should trigger report table reload.\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n setFilterButtonCount(reportElement, event.detail);\n\n getString('filtersapplied', 'core_reportbuilder')\n .then(addToast)\n .catch(Notification.exception);\n });\n\n // Reset report filters.\n filterForm.addEventListener(filterForm.events.NOSUBMIT_BUTTON_PRESSED, event => {\n event.preventDefault();\n\n const pendingPromise = new Pending('core_reportbuilder/filters:reset');\n const reportParameters = reportElement.dataset.parameter;\n\n resetFilters(reportId, reportParameters)\n .then(() => getString('filtersreset', 'core_reportbuilder'))\n .then(addToast)\n .then(() => loadFragment('core_reportbuilder', 'filters_form', contextId, {\n reportid: reportId,\n parameters: reportParameters,\n }))\n .then((html, js) => {\n Templates.replaceNodeContents(filterFormContainer, html, js);\n\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n setFilterButtonCount(reportElement, 0);\n\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n });\n\n // Modify \"region-main\" overflow for big filter forms.\n document.querySelector('#region-main').style.overflowX = \"visible\";\n};\n"],"names":["setFilterButtonCount","async","reportElement","filterCount","filterButtonLabel","querySelector","reportSelectors","regions","textContent","reportId","contextId","document","forReport","filterFormContainer","filtersForm","dataset","initialized","filterForm","DynamicForm","addEventListener","events","FORM_SUBMITTED","event","preventDefault","reportEvents","tableReload","detail","then","addToast","catch","Notification","exception","NOSUBMIT_BUTTON_PRESSED","pendingPromise","Pending","reportParameters","parameter","reportid","parameters","html","js","replaceNodeContents","resolve","style","overflowX"],"mappings":";;;;;;;kYAyCMA,qBAAuBC,MAAMC,cAAeC,qBACxCC,kBAAoBF,cAAcG,cAAcC,gBAAgBC,QAAQH,mBAG1EA,kBAAkBI,YADlBL,YAAc,QACwB,mBAAU,kBAAmB,qBAAsBA,mBAEnD,mBAAU,UAAW,yBAW/C,CAACM,SAAUC,mBACrBR,cAAgBS,SAASN,cAAcC,gBAAgBM,UAAUH,WACjEI,oBAAsBX,cAAcG,cAAcC,gBAAgBC,QAAQO,gBAG5ED,oBAAoBE,QAAQC,mBAGhCH,oBAAoBE,QAAQC,aAAc,QAEpCC,WAAa,IAAIC,qBAAYL,oBAAqB,sCAGxDI,WAAWE,iBAAiBF,WAAWG,OAAOC,gBAAgBC,QAC1DA,MAAMC,qDAGQC,aAAaC,YAAa,GAAIvB,eAC5CF,qBAAqBE,cAAeoB,MAAMI,4BAEhC,iBAAkB,sBACvBC,KAAKC,YACLC,MAAMC,sBAAaC,cAI5Bd,WAAWE,iBAAiBF,WAAWG,OAAOY,yBAAyBV,QACnEA,MAAMC,uBAEAU,eAAiB,IAAIC,iBAAQ,oCAC7BC,iBAAmBjC,cAAca,QAAQqB,oCAElC3B,SAAU0B,kBAClBR,MAAK,KAAM,mBAAU,eAAgB,wBACrCA,KAAKC,YACLD,MAAK,KAAM,0BAAa,qBAAsB,eAAgBjB,UAAW,CACtE2B,SAAU5B,SACV6B,WAAYH,qBAEfR,MAAK,CAACY,KAAMC,yBACCC,oBAAoB5B,oBAAqB0B,KAAMC,wCAE3ChB,aAAaC,YAAa,GAAIvB,eAC5CF,qBAAqBE,cAAe,GAE7B+B,eAAeS,aAEzBb,MAAMC,sBAAaC,cAI5BpB,SAASN,cAAc,gBAAgBsC,MAAMC,UAAY"} \ No newline at end of file +{"version":3,"file":"filters.min.js","sources":["../src/filters.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 * Report builder filter management\n *\n * @module core_reportbuilder/filters\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport {loadFragment} from 'core/fragment';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {getString} from 'core/str';\nimport Templates from 'core/templates';\nimport {add as addToast} from 'core/toast';\nimport DynamicForm from 'core_form/dynamicform';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {resetFilters} from 'core_reportbuilder/local/repository/filters';\n\n/**\n * Update filter button text to indicate applied filter count\n *\n * @param {Element} reportElement\n * @param {Number} filterCount\n */\nconst setFilterButtonCount = async(reportElement, filterCount) => {\n const filterButtonLabel = reportElement.querySelector(reportSelectors.regions.filterButtonLabel);\n\n if (filterCount > 0) {\n filterButtonLabel.textContent = await getString('filtersappliedx', 'core_reportbuilder', filterCount);\n } else {\n filterButtonLabel.textContent = await getString('filters', 'moodle');\n }\n};\n\n/**\n * Initialise module for given report\n *\n * @method\n * @param {Number} reportId\n * @param {Number} contextId\n */\nexport const init = (reportId, contextId) => {\n const reportElement = document.querySelector(reportSelectors.forReport(reportId));\n const filterFormContainer = reportElement.querySelector(reportSelectors.regions.filtersForm);\n\n // Ensure we only add our listeners once (can be called multiple times by mustache template).\n if (filterFormContainer.dataset.initialized) {\n return;\n }\n filterFormContainer.dataset.initialized = true;\n\n const filterForm = new DynamicForm(filterFormContainer, '\\\\core_reportbuilder\\\\form\\\\filter');\n\n // Submit report filters.\n filterForm.addEventListener(filterForm.events.FORM_SUBMITTED, event => {\n event.preventDefault();\n\n // After the form has been submitted, we should trigger report table reload.\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n setFilterButtonCount(reportElement, event.detail);\n\n getString('filtersapplied', 'core_reportbuilder')\n .then(addToast)\n .catch(Notification.exception);\n });\n\n // Reset report filters.\n filterForm.addEventListener(filterForm.events.NOSUBMIT_BUTTON_PRESSED, event => {\n event.preventDefault();\n\n const pendingPromise = new Pending('core_reportbuilder/filters:reset');\n const reportParameters = reportElement.dataset.parameter;\n\n resetFilters(reportId, reportParameters)\n .then(() => getString('filtersreset', 'core_reportbuilder'))\n .then(addToast)\n .then(() => loadFragment('core_reportbuilder', 'filters_form', contextId, {\n reportid: reportId,\n parameters: reportParameters,\n }))\n .then((html, js) => {\n Templates.replaceNodeContents(filterFormContainer, html, js);\n\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n setFilterButtonCount(reportElement, 0);\n\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n });\n\n // Modify \"region-main\" overflow for big filter forms.\n document.querySelector('#region-main').style.overflowX = \"visible\";\n};\n"],"names":["setFilterButtonCount","async","reportElement","filterCount","filterButtonLabel","querySelector","reportSelectors","regions","textContent","reportId","contextId","document","forReport","filterFormContainer","filtersForm","dataset","initialized","filterForm","DynamicForm","addEventListener","events","FORM_SUBMITTED","event","preventDefault","reportEvents","tableReload","detail","then","addToast","catch","Notification","exception","NOSUBMIT_BUTTON_PRESSED","pendingPromise","Pending","reportParameters","parameter","reportid","parameters","html","js","replaceNodeContents","resolve","style","overflowX"],"mappings":";;;;;;;kYAyCMA,qBAAuBC,MAAMC,cAAeC,qBACxCC,kBAAoBF,cAAcG,cAAcC,gBAAgBC,QAAQH,mBAG1EA,kBAAkBI,YADlBL,YAAc,QACwB,kBAAU,kBAAmB,qBAAsBA,mBAEnD,kBAAU,UAAW,yBAW/C,CAACM,SAAUC,mBACrBR,cAAgBS,SAASN,cAAcC,gBAAgBM,UAAUH,WACjEI,oBAAsBX,cAAcG,cAAcC,gBAAgBC,QAAQO,gBAG5ED,oBAAoBE,QAAQC,mBAGhCH,oBAAoBE,QAAQC,aAAc,QAEpCC,WAAa,IAAIC,qBAAYL,oBAAqB,sCAGxDI,WAAWE,iBAAiBF,WAAWG,OAAOC,gBAAgBC,QAC1DA,MAAMC,qDAGQC,aAAaC,YAAa,GAAIvB,eAC5CF,qBAAqBE,cAAeoB,MAAMI,2BAEhC,iBAAkB,sBACvBC,KAAKC,YACLC,MAAMC,sBAAaC,cAI5Bd,WAAWE,iBAAiBF,WAAWG,OAAOY,yBAAyBV,QACnEA,MAAMC,uBAEAU,eAAiB,IAAIC,iBAAQ,oCAC7BC,iBAAmBjC,cAAca,QAAQqB,oCAElC3B,SAAU0B,kBAClBR,MAAK,KAAM,kBAAU,eAAgB,wBACrCA,KAAKC,YACLD,MAAK,KAAM,0BAAa,qBAAsB,eAAgBjB,UAAW,CACtE2B,SAAU5B,SACV6B,WAAYH,qBAEfR,MAAK,CAACY,KAAMC,yBACCC,oBAAoB5B,oBAAqB0B,KAAMC,wCAE3ChB,aAAaC,YAAa,GAAIvB,eAC5CF,qBAAqBE,cAAe,GAE7B+B,eAAeS,aAEzBb,MAAMC,sBAAaC,cAI5BpB,SAASN,cAAc,gBAAgBsC,MAAMC,UAAY"} \ No newline at end of file diff --git a/reportbuilder/amd/build/local/editor/card_view.min.js b/reportbuilder/amd/build/local/editor/card_view.min.js index 62669053d43..6aad148c293 100644 --- a/reportbuilder/amd/build/local/editor/card_view.min.js +++ b/reportbuilder/amd/build/local/editor/card_view.min.js @@ -1,3 +1,3 @@ -define("core_reportbuilder/local/editor/card_view",["exports","core_form/dynamicform","core/toast","core/str","core/pubsub","core/notification","core_reportbuilder/local/events","core_reportbuilder/local/selectors"],(function(_exports,_dynamicform,_toast,_str,_pubsub,_notification,reportEvents,reportSelectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_dynamicform=_interopRequireDefault(_dynamicform),_notification=_interopRequireDefault(_notification),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);let cardViewForm=null;_exports.init=initialized=>{(()=>{const cardViewFormContainer=document.querySelector(reportSelectors.regions.settingsCardView);cardViewFormContainer&&(cardViewForm=new _dynamicform.default(cardViewFormContainer,"\\core_reportbuilder\\form\\card_view"),cardViewForm.addEventListener(cardViewForm.events.FORM_SUBMITTED,(event=>{event.preventDefault(),(0,_str.get_string)("cardviewsettingssaved","core_reportbuilder").then(_toast.add).catch(_notification.default.exception)})))})(),initialized||(0,_pubsub.subscribe)(reportEvents.publish.reportColumnsUpdated,(()=>{const reportElement=document.querySelector(reportSelectors.regions.report);cardViewForm.load({reportid:reportElement.dataset.reportId})}))}})); +define("core_reportbuilder/local/editor/card_view",["exports","core_form/dynamicform","core/toast","core/str","core/pubsub","core/notification","core_reportbuilder/local/events","core_reportbuilder/local/selectors"],(function(_exports,_dynamicform,_toast,_str,_pubsub,_notification,reportEvents,reportSelectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_dynamicform=_interopRequireDefault(_dynamicform),_notification=_interopRequireDefault(_notification),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);let cardViewForm=null;_exports.init=initialized=>{(()=>{const cardViewFormContainer=document.querySelector(reportSelectors.regions.settingsCardView);cardViewFormContainer&&(cardViewForm=new _dynamicform.default(cardViewFormContainer,"\\core_reportbuilder\\form\\card_view"),cardViewForm.addEventListener(cardViewForm.events.FORM_SUBMITTED,(event=>{event.preventDefault(),(0,_str.getString)("cardviewsettingssaved","core_reportbuilder").then(_toast.add).catch(_notification.default.exception)})))})(),initialized||(0,_pubsub.subscribe)(reportEvents.publish.reportColumnsUpdated,(()=>{const reportElement=document.querySelector(reportSelectors.regions.report);cardViewForm.load({reportid:reportElement.dataset.reportId})}))}})); //# sourceMappingURL=card_view.min.js.map \ No newline at end of file diff --git a/reportbuilder/amd/build/local/editor/card_view.min.js.map b/reportbuilder/amd/build/local/editor/card_view.min.js.map index d8538cf314b..7809a241aac 100644 --- a/reportbuilder/amd/build/local/editor/card_view.min.js.map +++ b/reportbuilder/amd/build/local/editor/card_view.min.js.map @@ -1 +1 @@ -{"version":3,"file":"card_view.min.js","sources":["../../../src/local/editor/card_view.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 * Report builder card view editor\n *\n * @module core_reportbuilder/local/editor/card_view\n * @copyright 2021 Mikel Martín \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport DynamicForm from 'core_form/dynamicform';\nimport {add as addToast} from 'core/toast';\nimport {get_string as getString} from \"core/str\";\nimport {subscribe as subscribe} from 'core/pubsub';\nimport Notification from 'core/notification';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\n\nlet cardViewForm = null;\n\n/**\n * Initialise card view form, must be called on each init because the form container is re-created when switching editor modes\n */\nconst initCardViewForm = () => {\n const cardViewFormContainer = document.querySelector(reportSelectors.regions.settingsCardView);\n if (!cardViewFormContainer) {\n return;\n }\n cardViewForm = new DynamicForm(cardViewFormContainer, '\\\\core_reportbuilder\\\\form\\\\card_view');\n\n cardViewForm.addEventListener(cardViewForm.events.FORM_SUBMITTED, (event) => {\n event.preventDefault();\n\n getString('cardviewsettingssaved', 'core_reportbuilder')\n .then(addToast)\n .catch(Notification.exception);\n });\n};\n\n/**\n * Initialise module\n *\n * @param {Boolean} initialized Ensure we only add our listeners once\n */\nexport const init = (initialized) => {\n initCardViewForm();\n if (initialized) {\n return;\n }\n\n // Update form each time a column is added or removed to the custom report.\n subscribe(reportEvents.publish.reportColumnsUpdated, () => {\n const reportElement = document.querySelector(reportSelectors.regions.report);\n cardViewForm.load({reportid: reportElement.dataset.reportId});\n });\n};\n"],"names":["cardViewForm","initialized","cardViewFormContainer","document","querySelector","reportSelectors","regions","settingsCardView","DynamicForm","addEventListener","events","FORM_SUBMITTED","event","preventDefault","then","addToast","catch","Notification","exception","initCardViewForm","reportEvents","publish","reportColumnsUpdated","reportElement","report","load","reportid","dataset","reportId"],"mappings":"omDAiCIA,aAAe,mBA0BEC,cArBI,YACfC,sBAAwBC,SAASC,cAAcC,gBAAgBC,QAAQC,kBACxEL,wBAGLF,aAAe,IAAIQ,qBAAYN,sBAAuB,yCAEtDF,aAAaS,iBAAiBT,aAAaU,OAAOC,gBAAiBC,QAC/DA,MAAMC,qCAEI,wBAAyB,sBAC9BC,KAAKC,YACLC,MAAMC,sBAAaC,gBAU5BC,GACIlB,mCAKMmB,aAAaC,QAAQC,sBAAsB,WAC3CC,cAAgBpB,SAASC,cAAcC,gBAAgBC,QAAQkB,QACrExB,aAAayB,KAAK,CAACC,SAAUH,cAAcI,QAAQC"} \ No newline at end of file +{"version":3,"file":"card_view.min.js","sources":["../../../src/local/editor/card_view.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 * Report builder card view editor\n *\n * @module core_reportbuilder/local/editor/card_view\n * @copyright 2021 Mikel Martín \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport DynamicForm from 'core_form/dynamicform';\nimport {add as addToast} from 'core/toast';\nimport {getString} from \"core/str\";\nimport {subscribe as subscribe} from 'core/pubsub';\nimport Notification from 'core/notification';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\n\nlet cardViewForm = null;\n\n/**\n * Initialise card view form, must be called on each init because the form container is re-created when switching editor modes\n */\nconst initCardViewForm = () => {\n const cardViewFormContainer = document.querySelector(reportSelectors.regions.settingsCardView);\n if (!cardViewFormContainer) {\n return;\n }\n cardViewForm = new DynamicForm(cardViewFormContainer, '\\\\core_reportbuilder\\\\form\\\\card_view');\n\n cardViewForm.addEventListener(cardViewForm.events.FORM_SUBMITTED, (event) => {\n event.preventDefault();\n\n getString('cardviewsettingssaved', 'core_reportbuilder')\n .then(addToast)\n .catch(Notification.exception);\n });\n};\n\n/**\n * Initialise module\n *\n * @param {Boolean} initialized Ensure we only add our listeners once\n */\nexport const init = (initialized) => {\n initCardViewForm();\n if (initialized) {\n return;\n }\n\n // Update form each time a column is added or removed to the custom report.\n subscribe(reportEvents.publish.reportColumnsUpdated, () => {\n const reportElement = document.querySelector(reportSelectors.regions.report);\n cardViewForm.load({reportid: reportElement.dataset.reportId});\n });\n};\n"],"names":["cardViewForm","initialized","cardViewFormContainer","document","querySelector","reportSelectors","regions","settingsCardView","DynamicForm","addEventListener","events","FORM_SUBMITTED","event","preventDefault","then","addToast","catch","Notification","exception","initCardViewForm","reportEvents","publish","reportColumnsUpdated","reportElement","report","load","reportid","dataset","reportId"],"mappings":"omDAiCIA,aAAe,mBA0BEC,cArBI,YACfC,sBAAwBC,SAASC,cAAcC,gBAAgBC,QAAQC,kBACxEL,wBAGLF,aAAe,IAAIQ,qBAAYN,sBAAuB,yCAEtDF,aAAaS,iBAAiBT,aAAaU,OAAOC,gBAAiBC,QAC/DA,MAAMC,oCAEI,wBAAyB,sBAC9BC,KAAKC,YACLC,MAAMC,sBAAaC,gBAU5BC,GACIlB,mCAKMmB,aAAaC,QAAQC,sBAAsB,WAC3CC,cAAgBpB,SAASC,cAAcC,gBAAgBC,QAAQkB,QACrExB,aAAayB,KAAK,CAACC,SAAUH,cAAcI,QAAQC"} \ No newline at end of file diff --git a/reportbuilder/amd/build/local/editor/columns.min.js b/reportbuilder/amd/build/local/editor/columns.min.js index bdd10f719ed..fbc87fcf8fe 100644 --- a/reportbuilder/amd/build/local/editor/columns.min.js +++ b/reportbuilder/amd/build/local/editor/columns.min.js @@ -1,3 +1,3 @@ -define("core_reportbuilder/local/editor/columns",["exports","jquery","core/event_dispatcher","core/inplace_editable","core/local/inplace_editable/events","core/notification","core/pending","core/prefetch","core/pubsub","core/sortable_list","core/str","core/toast","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/columns","core_reportbuilder/local/repository/sorting"],(function(_exports,_jquery,_event_dispatcher,_inplace_editable,_events,_notification,_pending,_prefetch,_pubsub,_sortable_list,_str,_toast,reportEvents,reportSelectors,_columns,_sorting){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_sortable_list=_interopRequireDefault(_sortable_list),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);_exports.init=initialized=>{((0,_prefetch.prefetchStrings)("core_reportbuilder",["columnadded","columnaggregated","columndeleted","columnmoved","deletecolumn","deletecolumnconfirm"]),(0,_prefetch.prefetchStrings)("core",["delete"]),initialized)||(document.addEventListener("click",(event=>{const reportAddColumn=event.target.closest(reportSelectors.actions.reportAddColumn);if(reportAddColumn){event.preventDefault();const pendingPromise=new _pending.default("core_reportbuilder/columns:add"),reportElement=reportAddColumn.closest(reportSelectors.regions.report);(0,_columns.addColumn)(reportElement.dataset.reportId,reportAddColumn.dataset.uniqueIdentifier).then((data=>(0,_pubsub.publish)(reportEvents.publish.reportColumnsUpdated,data))).then((()=>(0,_str.get_string)("columnadded","core_reportbuilder",reportAddColumn.dataset.name))).then(_toast.add).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)}const reportRemoveColumn=event.target.closest(reportSelectors.actions.reportRemoveColumn);if(reportRemoveColumn){event.preventDefault();const reportElement=reportRemoveColumn.closest(reportSelectors.regions.report),columnHeader=reportRemoveColumn.closest(reportSelectors.regions.columnHeader),columnName=columnHeader.dataset.columnName;_notification.default.saveCancelPromise((0,_str.get_string)("deletecolumn","core_reportbuilder",columnName),(0,_str.get_string)("deletecolumnconfirm","core_reportbuilder",columnName),(0,_str.get_string)("delete","core"),{triggerElement:reportRemoveColumn}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/columns:remove");return(0,_columns.deleteColumn)(reportElement.dataset.reportId,columnHeader.dataset.columnId).then((data=>(0,_pubsub.publish)(reportEvents.publish.reportColumnsUpdated,data))).then((()=>(0,_toast.add)((0,_str.get_string)("columndeleted","core_reportbuilder",columnName)))).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))}})),new _sortable_list.default("".concat(reportSelectors.regions.reportTable," thead tr"),{isHorizontal:!0}).getElementName=element=>Promise.resolve(element.data("columnName")),(0,_jquery.default)(document).on(_sortable_list.default.EVENTS.DRAG,"".concat(reportSelectors.regions.report," th[data-column-id]"),((event,info)=>{const reportElement=event.target.closest(reportSelectors.regions.report),columnPosition=info.element.data("columnPosition"),targetColumnPosition=info.targetNextElement.data("columnPosition");(0,_jquery.default)(reportElement).find("tbody tr").each((function(){const cell=(0,_jquery.default)(this).children("td.c".concat(columnPosition-1))[0];if(targetColumnPosition){var beforeCell=(0,_jquery.default)(this).children("td.c".concat(targetColumnPosition-1))[0];this.insertBefore(cell,beforeCell)}else this.appendChild(cell)}))})),(0,_jquery.default)(document).on(_sortable_list.default.EVENTS.DROP,"".concat(reportSelectors.regions.report," th[data-column-id]"),((event,info)=>{if(info.positionChanged){const pendingPromise=new _pending.default("core_reportbuilder/columns:reorder"),reportElement=event.target.closest(reportSelectors.regions.report),columnId=info.element.data("columnId"),columnName=info.element.data("columnName"),columnPosition=info.element.data("columnPosition");let targetColumnPosition=info.targetNextElement.data("columnPosition")||info.element.siblings().length+2;targetColumnPosition>columnPosition&&targetColumnPosition--,(0,_columns.reorderColumn)(reportElement.dataset.reportId,columnId,targetColumnPosition).then((()=>(0,_str.get_string)("columnmoved","core_reportbuilder",columnName))).then(_toast.add).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)}})),document.addEventListener(_events.eventTypes.elementUpdated,(event=>{const columnAggregation=event.target.closest('[data-itemtype="columnaggregation"]');if(columnAggregation){const pendingPromise=new _pending.default("core_reportbuilder/columns:aggregate"),reportElement=columnAggregation.closest(reportSelectors.regions.report),columnHeader=columnAggregation.closest(reportSelectors.regions.columnHeader);(0,_str.get_string)("columnaggregated","core_reportbuilder",columnHeader.dataset.columnName).then(_toast.add).then((()=>{const columnAggregationLink='[data-itemtype="columnaggregation"][data-itemid="'+"".concat(columnAggregation.dataset.itemid,'"] > a');return(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preserveTriggerElement:columnAggregationLink},reportElement),(0,_sorting.getColumnSorting)(reportElement.dataset.reportId)})).then((data=>(0,_pubsub.publish)(reportEvents.publish.reportColumnsUpdated,data))).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)}})))}})); +define("core_reportbuilder/local/editor/columns",["exports","jquery","core/event_dispatcher","core/inplace_editable","core/local/inplace_editable/events","core/notification","core/pending","core/prefetch","core/pubsub","core/sortable_list","core/str","core/toast","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/columns","core_reportbuilder/local/repository/sorting"],(function(_exports,_jquery,_event_dispatcher,_inplace_editable,_events,_notification,_pending,_prefetch,_pubsub,_sortable_list,_str,_toast,reportEvents,reportSelectors,_columns,_sorting){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_sortable_list=_interopRequireDefault(_sortable_list),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);_exports.init=initialized=>{((0,_prefetch.prefetchStrings)("core_reportbuilder",["columnadded","columnaggregated","columndeleted","columnmoved","deletecolumn","deletecolumnconfirm"]),(0,_prefetch.prefetchStrings)("core",["delete"]),initialized)||(document.addEventListener("click",(event=>{const reportAddColumn=event.target.closest(reportSelectors.actions.reportAddColumn);if(reportAddColumn){event.preventDefault();const pendingPromise=new _pending.default("core_reportbuilder/columns:add"),reportElement=reportAddColumn.closest(reportSelectors.regions.report);(0,_columns.addColumn)(reportElement.dataset.reportId,reportAddColumn.dataset.uniqueIdentifier).then((data=>(0,_pubsub.publish)(reportEvents.publish.reportColumnsUpdated,data))).then((()=>(0,_str.getString)("columnadded","core_reportbuilder",reportAddColumn.dataset.name))).then(_toast.add).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)}const reportRemoveColumn=event.target.closest(reportSelectors.actions.reportRemoveColumn);if(reportRemoveColumn){event.preventDefault();const reportElement=reportRemoveColumn.closest(reportSelectors.regions.report),columnHeader=reportRemoveColumn.closest(reportSelectors.regions.columnHeader),columnName=columnHeader.dataset.columnName;_notification.default.saveCancelPromise((0,_str.getString)("deletecolumn","core_reportbuilder",columnName),(0,_str.getString)("deletecolumnconfirm","core_reportbuilder",columnName),(0,_str.getString)("delete","core"),{triggerElement:reportRemoveColumn}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/columns:remove");return(0,_columns.deleteColumn)(reportElement.dataset.reportId,columnHeader.dataset.columnId).then((data=>(0,_pubsub.publish)(reportEvents.publish.reportColumnsUpdated,data))).then((()=>(0,_toast.add)((0,_str.getString)("columndeleted","core_reportbuilder",columnName)))).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))}})),new _sortable_list.default("".concat(reportSelectors.regions.reportTable," thead tr"),{isHorizontal:!0}).getElementName=element=>Promise.resolve(element.data("columnName")),(0,_jquery.default)(document).on(_sortable_list.default.EVENTS.DRAG,"".concat(reportSelectors.regions.report," th[data-column-id]"),((event,info)=>{const reportElement=event.target.closest(reportSelectors.regions.report),columnPosition=info.element.data("columnPosition"),targetColumnPosition=info.targetNextElement.data("columnPosition");(0,_jquery.default)(reportElement).find("tbody tr").each((function(){const cell=(0,_jquery.default)(this).children("td.c".concat(columnPosition-1))[0];if(targetColumnPosition){var beforeCell=(0,_jquery.default)(this).children("td.c".concat(targetColumnPosition-1))[0];this.insertBefore(cell,beforeCell)}else this.appendChild(cell)}))})),(0,_jquery.default)(document).on(_sortable_list.default.EVENTS.DROP,"".concat(reportSelectors.regions.report," th[data-column-id]"),((event,info)=>{if(info.positionChanged){const pendingPromise=new _pending.default("core_reportbuilder/columns:reorder"),reportElement=event.target.closest(reportSelectors.regions.report),columnId=info.element.data("columnId"),columnName=info.element.data("columnName"),columnPosition=info.element.data("columnPosition");let targetColumnPosition=info.targetNextElement.data("columnPosition")||info.element.siblings().length+2;targetColumnPosition>columnPosition&&targetColumnPosition--,(0,_columns.reorderColumn)(reportElement.dataset.reportId,columnId,targetColumnPosition).then((()=>(0,_str.getString)("columnmoved","core_reportbuilder",columnName))).then(_toast.add).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)}})),document.addEventListener(_events.eventTypes.elementUpdated,(event=>{const columnAggregation=event.target.closest('[data-itemtype="columnaggregation"]');if(columnAggregation){const pendingPromise=new _pending.default("core_reportbuilder/columns:aggregate"),reportElement=columnAggregation.closest(reportSelectors.regions.report),columnHeader=columnAggregation.closest(reportSelectors.regions.columnHeader);(0,_str.getString)("columnaggregated","core_reportbuilder",columnHeader.dataset.columnName).then(_toast.add).then((()=>{const columnAggregationLink='[data-itemtype="columnaggregation"][data-itemid="'+"".concat(columnAggregation.dataset.itemid,'"] > a');return(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preserveTriggerElement:columnAggregationLink},reportElement),(0,_sorting.getColumnSorting)(reportElement.dataset.reportId)})).then((data=>(0,_pubsub.publish)(reportEvents.publish.reportColumnsUpdated,data))).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)}})))}})); //# sourceMappingURL=columns.min.js.map \ No newline at end of file diff --git a/reportbuilder/amd/build/local/editor/columns.min.js.map b/reportbuilder/amd/build/local/editor/columns.min.js.map index 9a5e4419e79..5e86b86e63a 100644 --- a/reportbuilder/amd/build/local/editor/columns.min.js.map +++ b/reportbuilder/amd/build/local/editor/columns.min.js.map @@ -1 +1 @@ -{"version":3,"file":"columns.min.js","sources":["../../../src/local/editor/columns.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 * Report builder columns editor\n *\n * @module core_reportbuilder/local/editor/columns\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport $ from 'jquery';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport 'core/inplace_editable';\nimport {eventTypes as inplaceEditableEvents} from 'core/local/inplace_editable/events';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport {publish} from 'core/pubsub';\nimport SortableList from 'core/sortable_list';\nimport {get_string as getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {addColumn, deleteColumn, reorderColumn} from 'core_reportbuilder/local/repository/columns';\nimport {getColumnSorting} from 'core_reportbuilder/local/repository/sorting';\n\n/**\n * Initialise module, prefetch all required strings\n *\n * @param {Boolean} initialized Ensure we only add our listeners once\n */\nexport const init = initialized => {\n prefetchStrings('core_reportbuilder', [\n 'columnadded',\n 'columnaggregated',\n 'columndeleted',\n 'columnmoved',\n 'deletecolumn',\n 'deletecolumnconfirm',\n ]);\n\n prefetchStrings('core', [\n 'delete',\n ]);\n\n if (initialized) {\n return;\n }\n\n document.addEventListener('click', event => {\n\n // Add column to report.\n const reportAddColumn = event.target.closest(reportSelectors.actions.reportAddColumn);\n if (reportAddColumn) {\n event.preventDefault();\n\n const pendingPromise = new Pending('core_reportbuilder/columns:add');\n const reportElement = reportAddColumn.closest(reportSelectors.regions.report);\n\n addColumn(reportElement.dataset.reportId, reportAddColumn.dataset.uniqueIdentifier)\n .then(data => publish(reportEvents.publish.reportColumnsUpdated, data))\n .then(() => getString('columnadded', 'core_reportbuilder', reportAddColumn.dataset.name))\n .then(addToast)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n\n // Remove column from report.\n const reportRemoveColumn = event.target.closest(reportSelectors.actions.reportRemoveColumn);\n if (reportRemoveColumn) {\n event.preventDefault();\n\n const reportElement = reportRemoveColumn.closest(reportSelectors.regions.report);\n const columnHeader = reportRemoveColumn.closest(reportSelectors.regions.columnHeader);\n const columnName = columnHeader.dataset.columnName;\n\n Notification.saveCancelPromise(\n getString('deletecolumn', 'core_reportbuilder', columnName),\n getString('deletecolumnconfirm', 'core_reportbuilder', columnName),\n getString('delete', 'core'),\n {triggerElement: reportRemoveColumn}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/columns:remove');\n\n return deleteColumn(reportElement.dataset.reportId, columnHeader.dataset.columnId)\n .then(data => publish(reportEvents.publish.reportColumnsUpdated, data))\n .then(() => addToast(getString('columndeleted', 'core_reportbuilder', columnName)))\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n\n // Initialize sortable list to handle column moving (note JQuery dependency, see MDL-72293 for resolution).\n var columnSortableList = new SortableList(`${reportSelectors.regions.reportTable} thead tr`, {isHorizontal: true});\n columnSortableList.getElementName = element => Promise.resolve(element.data('columnName'));\n\n $(document).on(SortableList.EVENTS.DRAG, `${reportSelectors.regions.report} th[data-column-id]`, (event, info) => {\n const reportElement = event.target.closest(reportSelectors.regions.report);\n const columnPosition = info.element.data('columnPosition');\n const targetColumnPosition = info.targetNextElement.data('columnPosition');\n\n $(reportElement).find('tbody tr').each(function() {\n const cell = $(this).children(`td.c${columnPosition - 1}`)[0];\n if (targetColumnPosition) {\n var beforeCell = $(this).children(`td.c${targetColumnPosition - 1}`)[0];\n this.insertBefore(cell, beforeCell);\n } else {\n this.appendChild(cell);\n }\n });\n });\n\n $(document).on(SortableList.EVENTS.DROP, `${reportSelectors.regions.report} th[data-column-id]`, (event, info) => {\n if (info.positionChanged) {\n const pendingPromise = new Pending('core_reportbuilder/columns:reorder');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n const columnId = info.element.data('columnId');\n const columnName = info.element.data('columnName');\n const columnPosition = info.element.data('columnPosition');\n\n // Select target position, if moving to the end then count number of element siblings.\n let targetColumnPosition = info.targetNextElement.data('columnPosition') || info.element.siblings().length + 2;\n if (targetColumnPosition > columnPosition) {\n targetColumnPosition--;\n }\n\n reorderColumn(reportElement.dataset.reportId, columnId, targetColumnPosition)\n .then(() => getString('columnmoved', 'core_reportbuilder', columnName))\n .then(addToast)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n });\n\n // Initialize inplace editable listeners for column aggregation.\n document.addEventListener(inplaceEditableEvents.elementUpdated, event => {\n\n const columnAggregation = event.target.closest('[data-itemtype=\"columnaggregation\"]');\n if (columnAggregation) {\n const pendingPromise = new Pending('core_reportbuilder/columns:aggregate');\n const reportElement = columnAggregation.closest(reportSelectors.regions.report);\n const columnHeader = columnAggregation.closest(reportSelectors.regions.columnHeader);\n\n getString('columnaggregated', 'core_reportbuilder', columnHeader.dataset.columnName)\n .then(addToast)\n .then(() => {\n // Pass preserveTriggerElement parameter so columnAggregationLink will be focused after the report reload.\n const columnAggregationLink = `[data-itemtype=\"columnaggregation\"][data-itemid=\"`\n + `${columnAggregation.dataset.itemid}\"] > a`;\n\n // Now reload the table, and notify listeners that columns have been updated.\n dispatchEvent(reportEvents.tableReload, {preserveTriggerElement: columnAggregationLink}, reportElement);\n return getColumnSorting(reportElement.dataset.reportId);\n })\n .then(data => publish(reportEvents.publish.reportColumnsUpdated, data))\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }\n });\n};\n"],"names":["initialized","document","addEventListener","event","reportAddColumn","target","closest","reportSelectors","actions","preventDefault","pendingPromise","Pending","reportElement","regions","report","dataset","reportId","uniqueIdentifier","then","data","reportEvents","publish","reportColumnsUpdated","name","addToast","tableReload","preservePagination","resolve","catch","Notification","exception","reportRemoveColumn","columnHeader","columnName","saveCancelPromise","triggerElement","columnId","SortableList","reportTable","isHorizontal","getElementName","element","Promise","on","EVENTS","DRAG","info","columnPosition","targetColumnPosition","targetNextElement","find","each","cell","this","children","beforeCell","insertBefore","appendChild","DROP","positionChanged","siblings","length","inplaceEditableEvents","elementUpdated","columnAggregation","columnAggregationLink","itemid","preserveTriggerElement"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"columns.min.js","sources":["../../../src/local/editor/columns.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 * Report builder columns editor\n *\n * @module core_reportbuilder/local/editor/columns\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport $ from 'jquery';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport 'core/inplace_editable';\nimport {eventTypes as inplaceEditableEvents} from 'core/local/inplace_editable/events';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport {publish} from 'core/pubsub';\nimport SortableList from 'core/sortable_list';\nimport {getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {addColumn, deleteColumn, reorderColumn} from 'core_reportbuilder/local/repository/columns';\nimport {getColumnSorting} from 'core_reportbuilder/local/repository/sorting';\n\n/**\n * Initialise module, prefetch all required strings\n *\n * @param {Boolean} initialized Ensure we only add our listeners once\n */\nexport const init = initialized => {\n prefetchStrings('core_reportbuilder', [\n 'columnadded',\n 'columnaggregated',\n 'columndeleted',\n 'columnmoved',\n 'deletecolumn',\n 'deletecolumnconfirm',\n ]);\n\n prefetchStrings('core', [\n 'delete',\n ]);\n\n if (initialized) {\n return;\n }\n\n document.addEventListener('click', event => {\n\n // Add column to report.\n const reportAddColumn = event.target.closest(reportSelectors.actions.reportAddColumn);\n if (reportAddColumn) {\n event.preventDefault();\n\n const pendingPromise = new Pending('core_reportbuilder/columns:add');\n const reportElement = reportAddColumn.closest(reportSelectors.regions.report);\n\n addColumn(reportElement.dataset.reportId, reportAddColumn.dataset.uniqueIdentifier)\n .then(data => publish(reportEvents.publish.reportColumnsUpdated, data))\n .then(() => getString('columnadded', 'core_reportbuilder', reportAddColumn.dataset.name))\n .then(addToast)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n\n // Remove column from report.\n const reportRemoveColumn = event.target.closest(reportSelectors.actions.reportRemoveColumn);\n if (reportRemoveColumn) {\n event.preventDefault();\n\n const reportElement = reportRemoveColumn.closest(reportSelectors.regions.report);\n const columnHeader = reportRemoveColumn.closest(reportSelectors.regions.columnHeader);\n const columnName = columnHeader.dataset.columnName;\n\n Notification.saveCancelPromise(\n getString('deletecolumn', 'core_reportbuilder', columnName),\n getString('deletecolumnconfirm', 'core_reportbuilder', columnName),\n getString('delete', 'core'),\n {triggerElement: reportRemoveColumn}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/columns:remove');\n\n return deleteColumn(reportElement.dataset.reportId, columnHeader.dataset.columnId)\n .then(data => publish(reportEvents.publish.reportColumnsUpdated, data))\n .then(() => addToast(getString('columndeleted', 'core_reportbuilder', columnName)))\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n\n // Initialize sortable list to handle column moving (note JQuery dependency, see MDL-72293 for resolution).\n var columnSortableList = new SortableList(`${reportSelectors.regions.reportTable} thead tr`, {isHorizontal: true});\n columnSortableList.getElementName = element => Promise.resolve(element.data('columnName'));\n\n $(document).on(SortableList.EVENTS.DRAG, `${reportSelectors.regions.report} th[data-column-id]`, (event, info) => {\n const reportElement = event.target.closest(reportSelectors.regions.report);\n const columnPosition = info.element.data('columnPosition');\n const targetColumnPosition = info.targetNextElement.data('columnPosition');\n\n $(reportElement).find('tbody tr').each(function() {\n const cell = $(this).children(`td.c${columnPosition - 1}`)[0];\n if (targetColumnPosition) {\n var beforeCell = $(this).children(`td.c${targetColumnPosition - 1}`)[0];\n this.insertBefore(cell, beforeCell);\n } else {\n this.appendChild(cell);\n }\n });\n });\n\n $(document).on(SortableList.EVENTS.DROP, `${reportSelectors.regions.report} th[data-column-id]`, (event, info) => {\n if (info.positionChanged) {\n const pendingPromise = new Pending('core_reportbuilder/columns:reorder');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n const columnId = info.element.data('columnId');\n const columnName = info.element.data('columnName');\n const columnPosition = info.element.data('columnPosition');\n\n // Select target position, if moving to the end then count number of element siblings.\n let targetColumnPosition = info.targetNextElement.data('columnPosition') || info.element.siblings().length + 2;\n if (targetColumnPosition > columnPosition) {\n targetColumnPosition--;\n }\n\n reorderColumn(reportElement.dataset.reportId, columnId, targetColumnPosition)\n .then(() => getString('columnmoved', 'core_reportbuilder', columnName))\n .then(addToast)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n });\n\n // Initialize inplace editable listeners for column aggregation.\n document.addEventListener(inplaceEditableEvents.elementUpdated, event => {\n\n const columnAggregation = event.target.closest('[data-itemtype=\"columnaggregation\"]');\n if (columnAggregation) {\n const pendingPromise = new Pending('core_reportbuilder/columns:aggregate');\n const reportElement = columnAggregation.closest(reportSelectors.regions.report);\n const columnHeader = columnAggregation.closest(reportSelectors.regions.columnHeader);\n\n getString('columnaggregated', 'core_reportbuilder', columnHeader.dataset.columnName)\n .then(addToast)\n .then(() => {\n // Pass preserveTriggerElement parameter so columnAggregationLink will be focused after the report reload.\n const columnAggregationLink = `[data-itemtype=\"columnaggregation\"][data-itemid=\"`\n + `${columnAggregation.dataset.itemid}\"] > a`;\n\n // Now reload the table, and notify listeners that columns have been updated.\n dispatchEvent(reportEvents.tableReload, {preserveTriggerElement: columnAggregationLink}, reportElement);\n return getColumnSorting(reportElement.dataset.reportId);\n })\n .then(data => publish(reportEvents.publish.reportColumnsUpdated, data))\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }\n });\n};\n"],"names":["initialized","document","addEventListener","event","reportAddColumn","target","closest","reportSelectors","actions","preventDefault","pendingPromise","Pending","reportElement","regions","report","dataset","reportId","uniqueIdentifier","then","data","reportEvents","publish","reportColumnsUpdated","name","addToast","tableReload","preservePagination","resolve","catch","Notification","exception","reportRemoveColumn","columnHeader","columnName","saveCancelPromise","triggerElement","columnId","SortableList","reportTable","isHorizontal","getElementName","element","Promise","on","EVENTS","DRAG","info","columnPosition","targetColumnPosition","targetNextElement","find","each","cell","this","children","beforeCell","insertBefore","appendChild","DROP","positionChanged","siblings","length","inplaceEditableEvents","elementUpdated","columnAggregation","columnAggregationLink","itemid","preserveTriggerElement"],"mappings":"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"} \ No newline at end of file diff --git a/reportbuilder/amd/build/local/editor/conditions.min.js b/reportbuilder/amd/build/local/editor/conditions.min.js index b0be685a8ea..cc6f40c79e4 100644 --- a/reportbuilder/amd/build/local/editor/conditions.min.js +++ b/reportbuilder/amd/build/local/editor/conditions.min.js @@ -1,3 +1,3 @@ -define("core_reportbuilder/local/editor/conditions",["exports","jquery","core/custom_interaction_events","core/event_dispatcher","core/inplace_editable","core/notification","core/pending","core/prefetch","core/sortable_list","core/str","core/templates","core/toast","core_form/dynamicform","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/conditions"],(function(_exports,_jquery,_custom_interaction_events,_event_dispatcher,_inplace_editable,_notification,_pending,_prefetch,_sortable_list,_str,_templates,_toast,_dynamicform,reportEvents,reportSelectors,_conditions){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_sortable_list=_interopRequireDefault(_sortable_list),_templates=_interopRequireDefault(_templates),_dynamicform=_interopRequireDefault(_dynamicform),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);const reloadSettingsConditionsRegion=(reportElement,templateContext)=>{const pendingPromise=new _pending.default("core_reportbuilder/conditions:reload"),settingsConditionsRegion=reportElement.querySelector(reportSelectors.regions.settingsConditions);return _templates.default.renderForPromise("core_reportbuilder/local/settings/conditions",{conditions:templateContext}).then((_ref=>{let{html:html,js:js}=_ref;const conditionsjs=_jquery.default.parseHTML(templateContext.javascript,null,!0).map((node=>node.innerHTML)).join("\n");_templates.default.replaceNode(settingsConditionsRegion,html,js+conditionsjs),initConditionsForm();const reportAddCondition=reportElement.querySelector(reportSelectors.actions.reportAddCondition);return null==reportAddCondition||reportAddCondition.focus(),pendingPromise.resolve()}))},initConditionsForm=()=>{_custom_interaction_events.default.define(reportSelectors.actions.reportAddCondition,[_custom_interaction_events.default.events.accessibleChange]);const reportElement=document.querySelector(reportSelectors.regions.report),conditionFormContainer=reportElement.querySelector(reportSelectors.regions.settingsConditions);if(!conditionFormContainer)return;const conditionForm=new _dynamicform.default(conditionFormContainer,"\\core_reportbuilder\\form\\condition");conditionForm.addEventListener(conditionForm.events.FORM_SUBMITTED,(event=>{event.preventDefault(),(0,_str.get_string)("conditionsapplied","core_reportbuilder").then(_toast.add).catch(_notification.default.exception),(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement)})),conditionForm.addEventListener(conditionForm.events.NOSUBMIT_BUTTON_PRESSED,(event=>{event.preventDefault(),_notification.default.saveCancelPromise((0,_str.get_string)("resetconditions","core_reportbuilder"),(0,_str.get_string)("resetconditionsconfirm","core_reportbuilder"),(0,_str.get_string)("resetall","core_reportbuilder"),{triggerElement:event.detail}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/conditions:reset");return(0,_conditions.resetConditions)(reportElement.dataset.reportId).then((data=>reloadSettingsConditionsRegion(reportElement,data))).then((()=>(0,_toast.add)((0,_str.get_string)("conditionsreset","core_reportbuilder")))).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))}))};_exports.init=initialized=>{((0,_prefetch.prefetchStrings)("core_reportbuilder",["conditionadded","conditiondeleted","conditionmoved","conditionsapplied","conditionsreset","deletecondition","deleteconditionconfirm","resetall","resetconditions","resetconditionsconfirm"]),(0,_prefetch.prefetchStrings)("core",["delete"]),initConditionsForm(),initialized)||((0,_jquery.default)(document).on(_custom_interaction_events.default.events.accessibleChange,reportSelectors.actions.reportAddCondition,(event=>{const reportAddCondition=event.target.closest(reportSelectors.actions.reportAddCondition);if(reportAddCondition){if(event.preventDefault(),0===reportAddCondition.selectedIndex)return;const reportElement=reportAddCondition.closest(reportSelectors.regions.report),pendingPromise=new _pending.default("core_reportbuilder/conditions:add");(0,_conditions.addCondition)(reportElement.dataset.reportId,reportAddCondition.value).then((data=>reloadSettingsConditionsRegion(reportElement,data))).then((()=>(0,_str.get_string)("conditionadded","core_reportbuilder",reportAddCondition.options[reportAddCondition.selectedIndex].text))).then(_toast.add).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)}})),document.addEventListener("click",(event=>{const reportRemoveCondition=event.target.closest(reportSelectors.actions.reportRemoveCondition);if(reportRemoveCondition){event.preventDefault();const reportElement=reportRemoveCondition.closest(reportSelectors.regions.report),conditionContainer=reportRemoveCondition.closest(reportSelectors.regions.activeCondition),conditionName=conditionContainer.dataset.conditionName;_notification.default.saveCancelPromise((0,_str.get_string)("deletecondition","core_reportbuilder",conditionName),(0,_str.get_string)("deleteconditionconfirm","core_reportbuilder",conditionName),(0,_str.get_string)("delete","core"),{triggerElement:reportRemoveCondition}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/conditions:remove");return(0,_conditions.deleteCondition)(reportElement.dataset.reportId,conditionContainer.dataset.conditionId).then((data=>reloadSettingsConditionsRegion(reportElement,data))).then((()=>(0,_toast.add)((0,_str.get_string)("conditiondeleted","core_reportbuilder",conditionName)))).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))}})),new _sortable_list.default("".concat(reportSelectors.regions.activeConditions),{isHorizontal:!1}).getElementName=element=>Promise.resolve(element.data("conditionName")),(0,_jquery.default)(document).on(_sortable_list.default.EVENTS.DROP,reportSelectors.regions.activeCondition,((event,info)=>{if(info.positionChanged){const pendingPromise=new _pending.default("core_reportbuilder/conditions:reorder"),reportElement=event.target.closest(reportSelectors.regions.report),conditionId=info.element.data("conditionId"),conditionPosition=info.element.data("conditionPosition");let targetConditionPosition=info.targetNextElement.data("conditionPosition")||info.element.siblings().length+2;targetConditionPosition>conditionPosition&&targetConditionPosition--,(0,_conditions.reorderCondition)(reportElement.dataset.reportId,conditionId,targetConditionPosition).then((data=>reloadSettingsConditionsRegion(reportElement,data))).then((()=>(0,_str.get_string)("conditionmoved","core_reportbuilder",info.element.data("conditionName")))).then(_toast.add).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)}})))}})); +define("core_reportbuilder/local/editor/conditions",["exports","jquery","core/custom_interaction_events","core/event_dispatcher","core/inplace_editable","core/notification","core/pending","core/prefetch","core/sortable_list","core/str","core/templates","core/toast","core_form/dynamicform","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/conditions"],(function(_exports,_jquery,_custom_interaction_events,_event_dispatcher,_inplace_editable,_notification,_pending,_prefetch,_sortable_list,_str,_templates,_toast,_dynamicform,reportEvents,reportSelectors,_conditions){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_sortable_list=_interopRequireDefault(_sortable_list),_templates=_interopRequireDefault(_templates),_dynamicform=_interopRequireDefault(_dynamicform),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);const reloadSettingsConditionsRegion=(reportElement,templateContext)=>{const pendingPromise=new _pending.default("core_reportbuilder/conditions:reload"),settingsConditionsRegion=reportElement.querySelector(reportSelectors.regions.settingsConditions);return _templates.default.renderForPromise("core_reportbuilder/local/settings/conditions",{conditions:templateContext}).then((_ref=>{let{html:html,js:js}=_ref;const conditionsjs=_jquery.default.parseHTML(templateContext.javascript,null,!0).map((node=>node.innerHTML)).join("\n");_templates.default.replaceNode(settingsConditionsRegion,html,js+conditionsjs),initConditionsForm();const reportAddCondition=reportElement.querySelector(reportSelectors.actions.reportAddCondition);return null==reportAddCondition||reportAddCondition.focus(),pendingPromise.resolve()}))},initConditionsForm=()=>{_custom_interaction_events.default.define(reportSelectors.actions.reportAddCondition,[_custom_interaction_events.default.events.accessibleChange]);const reportElement=document.querySelector(reportSelectors.regions.report),conditionFormContainer=reportElement.querySelector(reportSelectors.regions.settingsConditions);if(!conditionFormContainer)return;const conditionForm=new _dynamicform.default(conditionFormContainer,"\\core_reportbuilder\\form\\condition");conditionForm.addEventListener(conditionForm.events.FORM_SUBMITTED,(event=>{event.preventDefault(),(0,_str.getString)("conditionsapplied","core_reportbuilder").then(_toast.add).catch(_notification.default.exception),(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement)})),conditionForm.addEventListener(conditionForm.events.NOSUBMIT_BUTTON_PRESSED,(event=>{event.preventDefault(),_notification.default.saveCancelPromise((0,_str.getString)("resetconditions","core_reportbuilder"),(0,_str.getString)("resetconditionsconfirm","core_reportbuilder"),(0,_str.getString)("resetall","core_reportbuilder"),{triggerElement:event.detail}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/conditions:reset");return(0,_conditions.resetConditions)(reportElement.dataset.reportId).then((data=>reloadSettingsConditionsRegion(reportElement,data))).then((()=>(0,_toast.add)((0,_str.getString)("conditionsreset","core_reportbuilder")))).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))}))};_exports.init=initialized=>{((0,_prefetch.prefetchStrings)("core_reportbuilder",["conditionadded","conditiondeleted","conditionmoved","conditionsapplied","conditionsreset","deletecondition","deleteconditionconfirm","resetall","resetconditions","resetconditionsconfirm"]),(0,_prefetch.prefetchStrings)("core",["delete"]),initConditionsForm(),initialized)||((0,_jquery.default)(document).on(_custom_interaction_events.default.events.accessibleChange,reportSelectors.actions.reportAddCondition,(event=>{const reportAddCondition=event.target.closest(reportSelectors.actions.reportAddCondition);if(reportAddCondition){if(event.preventDefault(),0===reportAddCondition.selectedIndex)return;const reportElement=reportAddCondition.closest(reportSelectors.regions.report),pendingPromise=new _pending.default("core_reportbuilder/conditions:add");(0,_conditions.addCondition)(reportElement.dataset.reportId,reportAddCondition.value).then((data=>reloadSettingsConditionsRegion(reportElement,data))).then((()=>(0,_str.getString)("conditionadded","core_reportbuilder",reportAddCondition.options[reportAddCondition.selectedIndex].text))).then(_toast.add).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)}})),document.addEventListener("click",(event=>{const reportRemoveCondition=event.target.closest(reportSelectors.actions.reportRemoveCondition);if(reportRemoveCondition){event.preventDefault();const reportElement=reportRemoveCondition.closest(reportSelectors.regions.report),conditionContainer=reportRemoveCondition.closest(reportSelectors.regions.activeCondition),conditionName=conditionContainer.dataset.conditionName;_notification.default.saveCancelPromise((0,_str.getString)("deletecondition","core_reportbuilder",conditionName),(0,_str.getString)("deleteconditionconfirm","core_reportbuilder",conditionName),(0,_str.getString)("delete","core"),{triggerElement:reportRemoveCondition}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/conditions:remove");return(0,_conditions.deleteCondition)(reportElement.dataset.reportId,conditionContainer.dataset.conditionId).then((data=>reloadSettingsConditionsRegion(reportElement,data))).then((()=>(0,_toast.add)((0,_str.getString)("conditiondeleted","core_reportbuilder",conditionName)))).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))}})),new _sortable_list.default("".concat(reportSelectors.regions.activeConditions),{isHorizontal:!1}).getElementName=element=>Promise.resolve(element.data("conditionName")),(0,_jquery.default)(document).on(_sortable_list.default.EVENTS.DROP,reportSelectors.regions.activeCondition,((event,info)=>{if(info.positionChanged){const pendingPromise=new _pending.default("core_reportbuilder/conditions:reorder"),reportElement=event.target.closest(reportSelectors.regions.report),conditionId=info.element.data("conditionId"),conditionPosition=info.element.data("conditionPosition");let targetConditionPosition=info.targetNextElement.data("conditionPosition")||info.element.siblings().length+2;targetConditionPosition>conditionPosition&&targetConditionPosition--,(0,_conditions.reorderCondition)(reportElement.dataset.reportId,conditionId,targetConditionPosition).then((data=>reloadSettingsConditionsRegion(reportElement,data))).then((()=>(0,_str.getString)("conditionmoved","core_reportbuilder",info.element.data("conditionName")))).then(_toast.add).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)}})))}})); //# sourceMappingURL=conditions.min.js.map \ No newline at end of file diff --git a/reportbuilder/amd/build/local/editor/conditions.min.js.map b/reportbuilder/amd/build/local/editor/conditions.min.js.map index b8670246d7b..64abbcfb0b8 100644 --- a/reportbuilder/amd/build/local/editor/conditions.min.js.map +++ b/reportbuilder/amd/build/local/editor/conditions.min.js.map @@ -1 +1 @@ -{"version":3,"file":"conditions.min.js","sources":["../../../src/local/editor/conditions.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 * Report builder conditions editor\n *\n * @module core_reportbuilder/local/editor/conditions\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport $ from 'jquery';\nimport CustomEvents from 'core/custom_interaction_events';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport 'core/inplace_editable';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport SortableList from 'core/sortable_list';\nimport {get_string as getString} from 'core/str';\nimport Templates from 'core/templates';\nimport {add as addToast} from 'core/toast';\nimport DynamicForm from 'core_form/dynamicform';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {addCondition, deleteCondition, reorderCondition, resetConditions} from 'core_reportbuilder/local/repository/conditions';\n\n/**\n * Reload conditions settings region\n *\n * @param {Element} reportElement\n * @param {Object} templateContext\n * @return {Promise}\n */\nconst reloadSettingsConditionsRegion = (reportElement, templateContext) => {\n const pendingPromise = new Pending('core_reportbuilder/conditions:reload');\n const settingsConditionsRegion = reportElement.querySelector(reportSelectors.regions.settingsConditions);\n\n return Templates.renderForPromise('core_reportbuilder/local/settings/conditions', {conditions: templateContext})\n .then(({html, js}) => {\n const conditionsjs = $.parseHTML(templateContext.javascript, null, true).map(node => node.innerHTML).join(\"\\n\");\n Templates.replaceNode(settingsConditionsRegion, html, js + conditionsjs);\n\n initConditionsForm();\n\n // Re-focus the add condition element after reloading the region.\n const reportAddCondition = reportElement.querySelector(reportSelectors.actions.reportAddCondition);\n reportAddCondition?.focus();\n\n return pendingPromise.resolve();\n });\n};\n\n/**\n * Initialise conditions form, must be called on each init because the form container is re-created when switching editor modes\n */\nconst initConditionsForm = () => {\n CustomEvents.define(reportSelectors.actions.reportAddCondition, [CustomEvents.events.accessibleChange]);\n\n // Handle dynamic conditions form.\n const reportElement = document.querySelector(reportSelectors.regions.report);\n const conditionFormContainer = reportElement.querySelector(reportSelectors.regions.settingsConditions);\n if (!conditionFormContainer) {\n return;\n }\n const conditionForm = new DynamicForm(conditionFormContainer, '\\\\core_reportbuilder\\\\form\\\\condition');\n\n // Submit report conditions.\n conditionForm.addEventListener(conditionForm.events.FORM_SUBMITTED, event => {\n event.preventDefault();\n\n getString('conditionsapplied', 'core_reportbuilder')\n .then(addToast)\n .catch(Notification.exception);\n\n // After the form has been submitted, we should trigger report table reload.\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n });\n\n // Reset report conditions.\n conditionForm.addEventListener(conditionForm.events.NOSUBMIT_BUTTON_PRESSED, event => {\n event.preventDefault();\n\n Notification.saveCancelPromise(\n getString('resetconditions', 'core_reportbuilder'),\n getString('resetconditionsconfirm', 'core_reportbuilder'),\n getString('resetall', 'core_reportbuilder'),\n {triggerElement: event.detail}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/conditions:reset');\n\n return resetConditions(reportElement.dataset.reportId)\n .then(data => reloadSettingsConditionsRegion(reportElement, data))\n .then(() => addToast(getString('conditionsreset', 'core_reportbuilder')))\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n });\n};\n\n/**\n * Initialise module, prefetch all required strings\n *\n * @param {Boolean} initialized Ensure we only add our listeners once\n */\nexport const init = initialized => {\n prefetchStrings('core_reportbuilder', [\n 'conditionadded',\n 'conditiondeleted',\n 'conditionmoved',\n 'conditionsapplied',\n 'conditionsreset',\n 'deletecondition',\n 'deleteconditionconfirm',\n 'resetall',\n 'resetconditions',\n 'resetconditionsconfirm',\n ]);\n\n prefetchStrings('core', [\n 'delete',\n ]);\n\n initConditionsForm();\n if (initialized) {\n return;\n }\n\n // Add condition to report. Use custom events helper to ensure consistency across platforms.\n $(document).on(CustomEvents.events.accessibleChange, reportSelectors.actions.reportAddCondition, event => {\n const reportAddCondition = event.target.closest(reportSelectors.actions.reportAddCondition);\n if (reportAddCondition) {\n event.preventDefault();\n\n // Check if dropdown is closed with no condition selected.\n if (reportAddCondition.selectedIndex === 0) {\n return;\n }\n\n const reportElement = reportAddCondition.closest(reportSelectors.regions.report);\n const pendingPromise = new Pending('core_reportbuilder/conditions:add');\n\n addCondition(reportElement.dataset.reportId, reportAddCondition.value)\n .then(data => reloadSettingsConditionsRegion(reportElement, data))\n .then(() => getString('conditionadded', 'core_reportbuilder',\n reportAddCondition.options[reportAddCondition.selectedIndex].text))\n .then(addToast)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n });\n\n document.addEventListener('click', event => {\n\n // Remove condition from report.\n const reportRemoveCondition = event.target.closest(reportSelectors.actions.reportRemoveCondition);\n if (reportRemoveCondition) {\n event.preventDefault();\n\n const reportElement = reportRemoveCondition.closest(reportSelectors.regions.report);\n const conditionContainer = reportRemoveCondition.closest(reportSelectors.regions.activeCondition);\n const conditionName = conditionContainer.dataset.conditionName;\n\n Notification.saveCancelPromise(\n getString('deletecondition', 'core_reportbuilder', conditionName),\n getString('deleteconditionconfirm', 'core_reportbuilder', conditionName),\n getString('delete', 'core'),\n {triggerElement: reportRemoveCondition}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/conditions:remove');\n\n return deleteCondition(reportElement.dataset.reportId, conditionContainer.dataset.conditionId)\n .then(data => reloadSettingsConditionsRegion(reportElement, data))\n .then(() => addToast(getString('conditiondeleted', 'core_reportbuilder', conditionName)))\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n\n // Initialize sortable list to handle active conditions moving (note JQuery dependency, see MDL-72293 for resolution).\n var activeConditionsSortableList = new SortableList(`${reportSelectors.regions.activeConditions}`,\n {isHorizontal: false});\n activeConditionsSortableList.getElementName = element => Promise.resolve(element.data('conditionName'));\n\n $(document).on(SortableList.EVENTS.DROP, reportSelectors.regions.activeCondition, (event, info) => {\n if (info.positionChanged) {\n const pendingPromise = new Pending('core_reportbuilder/conditions:reorder');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n const conditionId = info.element.data('conditionId');\n const conditionPosition = info.element.data('conditionPosition');\n\n // Select target position, if moving to the end then count number of element siblings.\n let targetConditionPosition = info.targetNextElement.data('conditionPosition') || info.element.siblings().length + 2;\n if (targetConditionPosition > conditionPosition) {\n targetConditionPosition--;\n }\n\n reorderCondition(reportElement.dataset.reportId, conditionId, targetConditionPosition)\n .then(data => reloadSettingsConditionsRegion(reportElement, data))\n .then(() => getString('conditionmoved', 'core_reportbuilder', info.element.data('conditionName')))\n .then(addToast)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n });\n};\n"],"names":["reloadSettingsConditionsRegion","reportElement","templateContext","pendingPromise","Pending","settingsConditionsRegion","querySelector","reportSelectors","regions","settingsConditions","Templates","renderForPromise","conditions","then","_ref","html","js","conditionsjs","$","parseHTML","javascript","map","node","innerHTML","join","replaceNode","initConditionsForm","reportAddCondition","actions","focus","resolve","define","CustomEvents","events","accessibleChange","document","report","conditionFormContainer","conditionForm","DynamicForm","addEventListener","FORM_SUBMITTED","event","preventDefault","addToast","catch","Notification","exception","reportEvents","tableReload","NOSUBMIT_BUTTON_PRESSED","saveCancelPromise","triggerElement","detail","dataset","reportId","data","initialized","on","target","closest","selectedIndex","value","options","text","reportRemoveCondition","conditionContainer","activeCondition","conditionName","conditionId","SortableList","activeConditions","isHorizontal","getElementName","element","Promise","EVENTS","DROP","info","positionChanged","conditionPosition","targetConditionPosition","targetNextElement","siblings","length"],"mappings":"qqEAgDMA,+BAAiC,CAACC,cAAeC,yBAC7CC,eAAiB,IAAIC,iBAAQ,wCAC7BC,yBAA2BJ,cAAcK,cAAcC,gBAAgBC,QAAQC,2BAE9EC,mBAAUC,iBAAiB,+CAAgD,CAACC,WAAYV,kBAC1FW,MAAKC,WAACC,KAACA,KAADC,GAAOA,eACJC,aAAeC,gBAAEC,UAAUjB,gBAAgBkB,WAAY,MAAM,GAAMC,KAAIC,MAAQA,KAAKC,YAAWC,KAAK,yBAChGC,YAAYpB,yBAA0BU,KAAMC,GAAKC,cAE3DS,2BAGMC,mBAAqB1B,cAAcK,cAAcC,gBAAgBqB,QAAQD,2BAC/EA,MAAAA,oBAAAA,mBAAoBE,QAEb1B,eAAe2B,cAO5BJ,mBAAqB,wCACVK,OAAOxB,gBAAgBqB,QAAQD,mBAAoB,CAACK,mCAAaC,OAAOC,yBAG/EjC,cAAgBkC,SAAS7B,cAAcC,gBAAgBC,QAAQ4B,QAC/DC,uBAAyBpC,cAAcK,cAAcC,gBAAgBC,QAAQC,wBAC9E4B,oCAGCC,cAAgB,IAAIC,qBAAYF,uBAAwB,yCAG9DC,cAAcE,iBAAiBF,cAAcL,OAAOQ,gBAAgBC,QAChEA,MAAMC,qCAEI,oBAAqB,sBAC1B9B,KAAK+B,YACLC,MAAMC,sBAAaC,+CAGVC,aAAaC,YAAa,GAAIhD,kBAIhDqC,cAAcE,iBAAiBF,cAAcL,OAAOiB,yBAAyBR,QACzEA,MAAMC,uCAEOQ,mBACT,mBAAU,kBAAmB,uBAC7B,mBAAU,yBAA0B,uBACpC,mBAAU,WAAY,sBACtB,CAACC,eAAgBV,MAAMW,SACzBxC,MAAK,WACGV,eAAiB,IAAIC,iBAAQ,8CAE5B,+BAAgBH,cAAcqD,QAAQC,UACxC1C,MAAK2C,MAAQxD,+BAA+BC,cAAeuD,QAC3D3C,MAAK,KAAM,eAAS,mBAAU,kBAAmB,yBACjDA,MAAK,yCACYmC,aAAaC,YAAa,GAAIhD,eACrCE,eAAe2B,aAEzBe,MAAMC,sBAAaC,cACzBF,OAAM,2BAWGY,6CACA,qBAAsB,CAClC,iBACA,mBACA,iBACA,oBACA,kBACA,kBACA,yBACA,WACA,kBACA,yDAGY,OAAQ,CACpB,WAGJ/B,qBACI+B,mCAKFtB,UAAUuB,GAAG1B,mCAAaC,OAAOC,iBAAkB3B,gBAAgBqB,QAAQD,oBAAoBe,cACvFf,mBAAqBe,MAAMiB,OAAOC,QAAQrD,gBAAgBqB,QAAQD,uBACpEA,mBAAoB,IACpBe,MAAMC,iBAGmC,IAArChB,mBAAmBkC,2BAIjB5D,cAAgB0B,mBAAmBiC,QAAQrD,gBAAgBC,QAAQ4B,QACnEjC,eAAiB,IAAIC,iBAAQ,kEAEtBH,cAAcqD,QAAQC,SAAU5B,mBAAmBmC,OAC3DjD,MAAK2C,MAAQxD,+BAA+BC,cAAeuD,QAC3D3C,MAAK,KAAM,mBAAU,iBAAkB,qBACpCc,mBAAmBoC,QAAQpC,mBAAmBkC,eAAeG,QAChEnD,KAAK+B,YACL/B,MAAK,yCACYmC,aAAaC,YAAa,GAAIhD,eACrCE,eAAe2B,aAEzBe,MAAMC,sBAAaC,eAIhCZ,SAASK,iBAAiB,SAASE,cAGzBuB,sBAAwBvB,MAAMiB,OAAOC,QAAQrD,gBAAgBqB,QAAQqC,0BACvEA,sBAAuB,CACvBvB,MAAMC,uBAEA1C,cAAgBgE,sBAAsBL,QAAQrD,gBAAgBC,QAAQ4B,QACtE8B,mBAAqBD,sBAAsBL,QAAQrD,gBAAgBC,QAAQ2D,iBAC3EC,cAAgBF,mBAAmBZ,QAAQc,oCAEpCjB,mBACT,mBAAU,kBAAmB,qBAAsBiB,gBACnD,mBAAU,yBAA0B,qBAAsBA,gBAC1D,mBAAU,SAAU,QACpB,CAAChB,eAAgBa,wBACnBpD,MAAK,WACGV,eAAiB,IAAIC,iBAAQ,+CAE5B,+BAAgBH,cAAcqD,QAAQC,SAAUW,mBAAmBZ,QAAQe,aAC7ExD,MAAK2C,MAAQxD,+BAA+BC,cAAeuD,QAC3D3C,MAAK,KAAM,eAAS,mBAAU,mBAAoB,qBAAsBuD,kBACxEvD,MAAK,yCACYmC,aAAaC,YAAa,GAAIhD,eACrCE,eAAe2B,aAEzBe,MAAMC,sBAAaC,cACzBF,OAAM,aAOkB,IAAIyB,iCAAgB/D,gBAAgBC,QAAQ+D,kBAC3E,CAACC,cAAc,IACUC,eAAiBC,SAAWC,QAAQ7C,QAAQ4C,QAAQlB,KAAK,sCAEpFrB,UAAUuB,GAAGY,uBAAaM,OAAOC,KAAMtE,gBAAgBC,QAAQ2D,iBAAiB,CAACzB,MAAOoC,WAClFA,KAAKC,gBAAiB,OAChB5E,eAAiB,IAAIC,iBAAQ,yCAC7BH,cAAgByC,MAAMiB,OAAOC,QAAQrD,gBAAgBC,QAAQ4B,QAC7DiC,YAAcS,KAAKJ,QAAQlB,KAAK,eAChCwB,kBAAoBF,KAAKJ,QAAQlB,KAAK,yBAGxCyB,wBAA0BH,KAAKI,kBAAkB1B,KAAK,sBAAwBsB,KAAKJ,QAAQS,WAAWC,OAAS,EAC/GH,wBAA0BD,mBAC1BC,2DAGahF,cAAcqD,QAAQC,SAAUc,YAAaY,yBACzDpE,MAAK2C,MAAQxD,+BAA+BC,cAAeuD,QAC3D3C,MAAK,KAAM,mBAAU,iBAAkB,qBAAsBiE,KAAKJ,QAAQlB,KAAK,oBAC/E3C,KAAK+B,YACL/B,MAAK,yCACYmC,aAAaC,YAAa,GAAIhD,eACrCE,eAAe2B,aAEzBe,MAAMC,sBAAaC"} \ No newline at end of file +{"version":3,"file":"conditions.min.js","sources":["../../../src/local/editor/conditions.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 * Report builder conditions editor\n *\n * @module core_reportbuilder/local/editor/conditions\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport $ from 'jquery';\nimport CustomEvents from 'core/custom_interaction_events';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport 'core/inplace_editable';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport SortableList from 'core/sortable_list';\nimport {getString} from 'core/str';\nimport Templates from 'core/templates';\nimport {add as addToast} from 'core/toast';\nimport DynamicForm from 'core_form/dynamicform';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {addCondition, deleteCondition, reorderCondition, resetConditions} from 'core_reportbuilder/local/repository/conditions';\n\n/**\n * Reload conditions settings region\n *\n * @param {Element} reportElement\n * @param {Object} templateContext\n * @return {Promise}\n */\nconst reloadSettingsConditionsRegion = (reportElement, templateContext) => {\n const pendingPromise = new Pending('core_reportbuilder/conditions:reload');\n const settingsConditionsRegion = reportElement.querySelector(reportSelectors.regions.settingsConditions);\n\n return Templates.renderForPromise('core_reportbuilder/local/settings/conditions', {conditions: templateContext})\n .then(({html, js}) => {\n const conditionsjs = $.parseHTML(templateContext.javascript, null, true).map(node => node.innerHTML).join(\"\\n\");\n Templates.replaceNode(settingsConditionsRegion, html, js + conditionsjs);\n\n initConditionsForm();\n\n // Re-focus the add condition element after reloading the region.\n const reportAddCondition = reportElement.querySelector(reportSelectors.actions.reportAddCondition);\n reportAddCondition?.focus();\n\n return pendingPromise.resolve();\n });\n};\n\n/**\n * Initialise conditions form, must be called on each init because the form container is re-created when switching editor modes\n */\nconst initConditionsForm = () => {\n CustomEvents.define(reportSelectors.actions.reportAddCondition, [CustomEvents.events.accessibleChange]);\n\n // Handle dynamic conditions form.\n const reportElement = document.querySelector(reportSelectors.regions.report);\n const conditionFormContainer = reportElement.querySelector(reportSelectors.regions.settingsConditions);\n if (!conditionFormContainer) {\n return;\n }\n const conditionForm = new DynamicForm(conditionFormContainer, '\\\\core_reportbuilder\\\\form\\\\condition');\n\n // Submit report conditions.\n conditionForm.addEventListener(conditionForm.events.FORM_SUBMITTED, event => {\n event.preventDefault();\n\n getString('conditionsapplied', 'core_reportbuilder')\n .then(addToast)\n .catch(Notification.exception);\n\n // After the form has been submitted, we should trigger report table reload.\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n });\n\n // Reset report conditions.\n conditionForm.addEventListener(conditionForm.events.NOSUBMIT_BUTTON_PRESSED, event => {\n event.preventDefault();\n\n Notification.saveCancelPromise(\n getString('resetconditions', 'core_reportbuilder'),\n getString('resetconditionsconfirm', 'core_reportbuilder'),\n getString('resetall', 'core_reportbuilder'),\n {triggerElement: event.detail}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/conditions:reset');\n\n return resetConditions(reportElement.dataset.reportId)\n .then(data => reloadSettingsConditionsRegion(reportElement, data))\n .then(() => addToast(getString('conditionsreset', 'core_reportbuilder')))\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n });\n};\n\n/**\n * Initialise module, prefetch all required strings\n *\n * @param {Boolean} initialized Ensure we only add our listeners once\n */\nexport const init = initialized => {\n prefetchStrings('core_reportbuilder', [\n 'conditionadded',\n 'conditiondeleted',\n 'conditionmoved',\n 'conditionsapplied',\n 'conditionsreset',\n 'deletecondition',\n 'deleteconditionconfirm',\n 'resetall',\n 'resetconditions',\n 'resetconditionsconfirm',\n ]);\n\n prefetchStrings('core', [\n 'delete',\n ]);\n\n initConditionsForm();\n if (initialized) {\n return;\n }\n\n // Add condition to report. Use custom events helper to ensure consistency across platforms.\n $(document).on(CustomEvents.events.accessibleChange, reportSelectors.actions.reportAddCondition, event => {\n const reportAddCondition = event.target.closest(reportSelectors.actions.reportAddCondition);\n if (reportAddCondition) {\n event.preventDefault();\n\n // Check if dropdown is closed with no condition selected.\n if (reportAddCondition.selectedIndex === 0) {\n return;\n }\n\n const reportElement = reportAddCondition.closest(reportSelectors.regions.report);\n const pendingPromise = new Pending('core_reportbuilder/conditions:add');\n\n addCondition(reportElement.dataset.reportId, reportAddCondition.value)\n .then(data => reloadSettingsConditionsRegion(reportElement, data))\n .then(() => getString('conditionadded', 'core_reportbuilder',\n reportAddCondition.options[reportAddCondition.selectedIndex].text))\n .then(addToast)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n });\n\n document.addEventListener('click', event => {\n\n // Remove condition from report.\n const reportRemoveCondition = event.target.closest(reportSelectors.actions.reportRemoveCondition);\n if (reportRemoveCondition) {\n event.preventDefault();\n\n const reportElement = reportRemoveCondition.closest(reportSelectors.regions.report);\n const conditionContainer = reportRemoveCondition.closest(reportSelectors.regions.activeCondition);\n const conditionName = conditionContainer.dataset.conditionName;\n\n Notification.saveCancelPromise(\n getString('deletecondition', 'core_reportbuilder', conditionName),\n getString('deleteconditionconfirm', 'core_reportbuilder', conditionName),\n getString('delete', 'core'),\n {triggerElement: reportRemoveCondition}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/conditions:remove');\n\n return deleteCondition(reportElement.dataset.reportId, conditionContainer.dataset.conditionId)\n .then(data => reloadSettingsConditionsRegion(reportElement, data))\n .then(() => addToast(getString('conditiondeleted', 'core_reportbuilder', conditionName)))\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n\n // Initialize sortable list to handle active conditions moving (note JQuery dependency, see MDL-72293 for resolution).\n var activeConditionsSortableList = new SortableList(`${reportSelectors.regions.activeConditions}`,\n {isHorizontal: false});\n activeConditionsSortableList.getElementName = element => Promise.resolve(element.data('conditionName'));\n\n $(document).on(SortableList.EVENTS.DROP, reportSelectors.regions.activeCondition, (event, info) => {\n if (info.positionChanged) {\n const pendingPromise = new Pending('core_reportbuilder/conditions:reorder');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n const conditionId = info.element.data('conditionId');\n const conditionPosition = info.element.data('conditionPosition');\n\n // Select target position, if moving to the end then count number of element siblings.\n let targetConditionPosition = info.targetNextElement.data('conditionPosition') || info.element.siblings().length + 2;\n if (targetConditionPosition > conditionPosition) {\n targetConditionPosition--;\n }\n\n reorderCondition(reportElement.dataset.reportId, conditionId, targetConditionPosition)\n .then(data => reloadSettingsConditionsRegion(reportElement, data))\n .then(() => getString('conditionmoved', 'core_reportbuilder', info.element.data('conditionName')))\n .then(addToast)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n });\n};\n"],"names":["reloadSettingsConditionsRegion","reportElement","templateContext","pendingPromise","Pending","settingsConditionsRegion","querySelector","reportSelectors","regions","settingsConditions","Templates","renderForPromise","conditions","then","_ref","html","js","conditionsjs","$","parseHTML","javascript","map","node","innerHTML","join","replaceNode","initConditionsForm","reportAddCondition","actions","focus","resolve","define","CustomEvents","events","accessibleChange","document","report","conditionFormContainer","conditionForm","DynamicForm","addEventListener","FORM_SUBMITTED","event","preventDefault","addToast","catch","Notification","exception","reportEvents","tableReload","NOSUBMIT_BUTTON_PRESSED","saveCancelPromise","triggerElement","detail","dataset","reportId","data","initialized","on","target","closest","selectedIndex","value","options","text","reportRemoveCondition","conditionContainer","activeCondition","conditionName","conditionId","SortableList","activeConditions","isHorizontal","getElementName","element","Promise","EVENTS","DROP","info","positionChanged","conditionPosition","targetConditionPosition","targetNextElement","siblings","length"],"mappings":"qqEAgDMA,+BAAiC,CAACC,cAAeC,yBAC7CC,eAAiB,IAAIC,iBAAQ,wCAC7BC,yBAA2BJ,cAAcK,cAAcC,gBAAgBC,QAAQC,2BAE9EC,mBAAUC,iBAAiB,+CAAgD,CAACC,WAAYV,kBAC1FW,MAAKC,WAACC,KAACA,KAADC,GAAOA,eACJC,aAAeC,gBAAEC,UAAUjB,gBAAgBkB,WAAY,MAAM,GAAMC,KAAIC,MAAQA,KAAKC,YAAWC,KAAK,yBAChGC,YAAYpB,yBAA0BU,KAAMC,GAAKC,cAE3DS,2BAGMC,mBAAqB1B,cAAcK,cAAcC,gBAAgBqB,QAAQD,2BAC/EA,MAAAA,oBAAAA,mBAAoBE,QAEb1B,eAAe2B,cAO5BJ,mBAAqB,wCACVK,OAAOxB,gBAAgBqB,QAAQD,mBAAoB,CAACK,mCAAaC,OAAOC,yBAG/EjC,cAAgBkC,SAAS7B,cAAcC,gBAAgBC,QAAQ4B,QAC/DC,uBAAyBpC,cAAcK,cAAcC,gBAAgBC,QAAQC,wBAC9E4B,oCAGCC,cAAgB,IAAIC,qBAAYF,uBAAwB,yCAG9DC,cAAcE,iBAAiBF,cAAcL,OAAOQ,gBAAgBC,QAChEA,MAAMC,oCAEI,oBAAqB,sBAC1B9B,KAAK+B,YACLC,MAAMC,sBAAaC,+CAGVC,aAAaC,YAAa,GAAIhD,kBAIhDqC,cAAcE,iBAAiBF,cAAcL,OAAOiB,yBAAyBR,QACzEA,MAAMC,uCAEOQ,mBACT,kBAAU,kBAAmB,uBAC7B,kBAAU,yBAA0B,uBACpC,kBAAU,WAAY,sBACtB,CAACC,eAAgBV,MAAMW,SACzBxC,MAAK,WACGV,eAAiB,IAAIC,iBAAQ,8CAE5B,+BAAgBH,cAAcqD,QAAQC,UACxC1C,MAAK2C,MAAQxD,+BAA+BC,cAAeuD,QAC3D3C,MAAK,KAAM,eAAS,kBAAU,kBAAmB,yBACjDA,MAAK,yCACYmC,aAAaC,YAAa,GAAIhD,eACrCE,eAAe2B,aAEzBe,MAAMC,sBAAaC,cACzBF,OAAM,2BAWGY,6CACA,qBAAsB,CAClC,iBACA,mBACA,iBACA,oBACA,kBACA,kBACA,yBACA,WACA,kBACA,yDAGY,OAAQ,CACpB,WAGJ/B,qBACI+B,mCAKFtB,UAAUuB,GAAG1B,mCAAaC,OAAOC,iBAAkB3B,gBAAgBqB,QAAQD,oBAAoBe,cACvFf,mBAAqBe,MAAMiB,OAAOC,QAAQrD,gBAAgBqB,QAAQD,uBACpEA,mBAAoB,IACpBe,MAAMC,iBAGmC,IAArChB,mBAAmBkC,2BAIjB5D,cAAgB0B,mBAAmBiC,QAAQrD,gBAAgBC,QAAQ4B,QACnEjC,eAAiB,IAAIC,iBAAQ,kEAEtBH,cAAcqD,QAAQC,SAAU5B,mBAAmBmC,OAC3DjD,MAAK2C,MAAQxD,+BAA+BC,cAAeuD,QAC3D3C,MAAK,KAAM,kBAAU,iBAAkB,qBACpCc,mBAAmBoC,QAAQpC,mBAAmBkC,eAAeG,QAChEnD,KAAK+B,YACL/B,MAAK,yCACYmC,aAAaC,YAAa,GAAIhD,eACrCE,eAAe2B,aAEzBe,MAAMC,sBAAaC,eAIhCZ,SAASK,iBAAiB,SAASE,cAGzBuB,sBAAwBvB,MAAMiB,OAAOC,QAAQrD,gBAAgBqB,QAAQqC,0BACvEA,sBAAuB,CACvBvB,MAAMC,uBAEA1C,cAAgBgE,sBAAsBL,QAAQrD,gBAAgBC,QAAQ4B,QACtE8B,mBAAqBD,sBAAsBL,QAAQrD,gBAAgBC,QAAQ2D,iBAC3EC,cAAgBF,mBAAmBZ,QAAQc,oCAEpCjB,mBACT,kBAAU,kBAAmB,qBAAsBiB,gBACnD,kBAAU,yBAA0B,qBAAsBA,gBAC1D,kBAAU,SAAU,QACpB,CAAChB,eAAgBa,wBACnBpD,MAAK,WACGV,eAAiB,IAAIC,iBAAQ,+CAE5B,+BAAgBH,cAAcqD,QAAQC,SAAUW,mBAAmBZ,QAAQe,aAC7ExD,MAAK2C,MAAQxD,+BAA+BC,cAAeuD,QAC3D3C,MAAK,KAAM,eAAS,kBAAU,mBAAoB,qBAAsBuD,kBACxEvD,MAAK,yCACYmC,aAAaC,YAAa,GAAIhD,eACrCE,eAAe2B,aAEzBe,MAAMC,sBAAaC,cACzBF,OAAM,aAOkB,IAAIyB,iCAAgB/D,gBAAgBC,QAAQ+D,kBAC3E,CAACC,cAAc,IACUC,eAAiBC,SAAWC,QAAQ7C,QAAQ4C,QAAQlB,KAAK,sCAEpFrB,UAAUuB,GAAGY,uBAAaM,OAAOC,KAAMtE,gBAAgBC,QAAQ2D,iBAAiB,CAACzB,MAAOoC,WAClFA,KAAKC,gBAAiB,OAChB5E,eAAiB,IAAIC,iBAAQ,yCAC7BH,cAAgByC,MAAMiB,OAAOC,QAAQrD,gBAAgBC,QAAQ4B,QAC7DiC,YAAcS,KAAKJ,QAAQlB,KAAK,eAChCwB,kBAAoBF,KAAKJ,QAAQlB,KAAK,yBAGxCyB,wBAA0BH,KAAKI,kBAAkB1B,KAAK,sBAAwBsB,KAAKJ,QAAQS,WAAWC,OAAS,EAC/GH,wBAA0BD,mBAC1BC,2DAGahF,cAAcqD,QAAQC,SAAUc,YAAaY,yBACzDpE,MAAK2C,MAAQxD,+BAA+BC,cAAeuD,QAC3D3C,MAAK,KAAM,kBAAU,iBAAkB,qBAAsBiE,KAAKJ,QAAQlB,KAAK,oBAC/E3C,KAAK+B,YACL/B,MAAK,yCACYmC,aAAaC,YAAa,GAAIhD,eACrCE,eAAe2B,aAEzBe,MAAMC,sBAAaC"} \ No newline at end of file diff --git a/reportbuilder/amd/build/local/editor/filters.min.js b/reportbuilder/amd/build/local/editor/filters.min.js index 8a2864dabc1..9427167a701 100644 --- a/reportbuilder/amd/build/local/editor/filters.min.js +++ b/reportbuilder/amd/build/local/editor/filters.min.js @@ -1,3 +1,3 @@ -define("core_reportbuilder/local/editor/filters",["exports","jquery","core/custom_interaction_events","core/inplace_editable","core/notification","core/pending","core/prefetch","core/sortable_list","core/str","core/templates","core/toast","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/filters"],(function(_exports,_jquery,_custom_interaction_events,_inplace_editable,_notification,_pending,_prefetch,_sortable_list,_str,_templates,_toast,reportSelectors,_filters){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_sortable_list=_interopRequireDefault(_sortable_list),_templates=_interopRequireDefault(_templates),reportSelectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(reportSelectors);const reloadSettingsFiltersRegion=(reportElement,templateContext)=>{const pendingPromise=new _pending.default("core_reportbuilder/filters:reload"),settingsFiltersRegion=reportElement.querySelector(reportSelectors.regions.settingsFilters);return _templates.default.renderForPromise("core_reportbuilder/local/settings/filters",{filters:templateContext}).then((_ref=>{let{html:html,js:js}=_ref;_templates.default.replaceNode(settingsFiltersRegion,html,js),initFiltersForm();const reportAddFilter=reportElement.querySelector(reportSelectors.actions.reportAddFilter);return null==reportAddFilter||reportAddFilter.focus(),pendingPromise.resolve()}))},initFiltersForm=()=>{_custom_interaction_events.default.define(reportSelectors.actions.reportAddFilter,[_custom_interaction_events.default.events.accessibleChange])};_exports.init=initialized=>{((0,_prefetch.prefetchStrings)("core_reportbuilder",["deletefilter","deletefilterconfirm","filteradded","filterdeleted","filtermoved"]),(0,_prefetch.prefetchStrings)("core",["delete"]),initFiltersForm(),initialized)||((0,_jquery.default)(document).on(_custom_interaction_events.default.events.accessibleChange,reportSelectors.actions.reportAddFilter,(event=>{const reportAddFilter=event.target.closest(reportSelectors.actions.reportAddFilter);if(reportAddFilter){if(event.preventDefault(),0===reportAddFilter.selectedIndex)return;const reportElement=reportAddFilter.closest(reportSelectors.regions.report),pendingPromise=new _pending.default("core_reportbuilder/filters:add");(0,_filters.addFilter)(reportElement.dataset.reportId,reportAddFilter.value).then((data=>reloadSettingsFiltersRegion(reportElement,data))).then((()=>(0,_str.get_string)("filteradded","core_reportbuilder",reportAddFilter.options[reportAddFilter.selectedIndex].text))).then(_toast.add).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)}})),document.addEventListener("click",(event=>{const reportRemoveFilter=event.target.closest(reportSelectors.actions.reportRemoveFilter);if(reportRemoveFilter){event.preventDefault();const reportElement=reportRemoveFilter.closest(reportSelectors.regions.report),filterContainer=reportRemoveFilter.closest(reportSelectors.regions.activeFilter),filterName=filterContainer.dataset.filterName;_notification.default.saveCancelPromise((0,_str.get_string)("deletefilter","core_reportbuilder",filterName),(0,_str.get_string)("deletefilterconfirm","core_reportbuilder",filterName),(0,_str.get_string)("delete","core"),{triggerElement:reportRemoveFilter}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/filters:remove");return(0,_filters.deleteFilter)(reportElement.dataset.reportId,filterContainer.dataset.filterId).then((data=>reloadSettingsFiltersRegion(reportElement,data))).then((()=>(0,_toast.add)((0,_str.get_string)("filterdeleted","core_reportbuilder",filterName)))).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)})).catch((()=>{}))}})),new _sortable_list.default("".concat(reportSelectors.regions.activeFilters," ul"),{isHorizontal:!1}).getElementName=element=>Promise.resolve(element.data("filterName")),(0,_jquery.default)(document).on(_sortable_list.default.EVENTS.DROP,"".concat(reportSelectors.regions.report," li[data-filter-id]"),((event,info)=>{if(info.positionChanged){const pendingPromise=new _pending.default("core_reportbuilder/filters:reorder"),reportElement=event.target.closest(reportSelectors.regions.report),filterId=info.element.data("filterId"),filterPosition=info.element.data("filterPosition");let targetFilterPosition=info.targetNextElement.data("filterPosition")||info.element.siblings().length+2;targetFilterPosition>filterPosition&&targetFilterPosition--,(0,_filters.reorderFilter)(reportElement.dataset.reportId,filterId,targetFilterPosition).then((data=>reloadSettingsFiltersRegion(reportElement,data))).then((()=>(0,_str.get_string)("filtermoved","core_reportbuilder",info.element.data("filterName")))).then(_toast.add).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)}})))}})); +define("core_reportbuilder/local/editor/filters",["exports","jquery","core/custom_interaction_events","core/inplace_editable","core/notification","core/pending","core/prefetch","core/sortable_list","core/str","core/templates","core/toast","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/filters"],(function(_exports,_jquery,_custom_interaction_events,_inplace_editable,_notification,_pending,_prefetch,_sortable_list,_str,_templates,_toast,reportSelectors,_filters){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_sortable_list=_interopRequireDefault(_sortable_list),_templates=_interopRequireDefault(_templates),reportSelectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(reportSelectors);const reloadSettingsFiltersRegion=(reportElement,templateContext)=>{const pendingPromise=new _pending.default("core_reportbuilder/filters:reload"),settingsFiltersRegion=reportElement.querySelector(reportSelectors.regions.settingsFilters);return _templates.default.renderForPromise("core_reportbuilder/local/settings/filters",{filters:templateContext}).then((_ref=>{let{html:html,js:js}=_ref;_templates.default.replaceNode(settingsFiltersRegion,html,js),initFiltersForm();const reportAddFilter=reportElement.querySelector(reportSelectors.actions.reportAddFilter);return null==reportAddFilter||reportAddFilter.focus(),pendingPromise.resolve()}))},initFiltersForm=()=>{_custom_interaction_events.default.define(reportSelectors.actions.reportAddFilter,[_custom_interaction_events.default.events.accessibleChange])};_exports.init=initialized=>{((0,_prefetch.prefetchStrings)("core_reportbuilder",["deletefilter","deletefilterconfirm","filteradded","filterdeleted","filtermoved"]),(0,_prefetch.prefetchStrings)("core",["delete"]),initFiltersForm(),initialized)||((0,_jquery.default)(document).on(_custom_interaction_events.default.events.accessibleChange,reportSelectors.actions.reportAddFilter,(event=>{const reportAddFilter=event.target.closest(reportSelectors.actions.reportAddFilter);if(reportAddFilter){if(event.preventDefault(),0===reportAddFilter.selectedIndex)return;const reportElement=reportAddFilter.closest(reportSelectors.regions.report),pendingPromise=new _pending.default("core_reportbuilder/filters:add");(0,_filters.addFilter)(reportElement.dataset.reportId,reportAddFilter.value).then((data=>reloadSettingsFiltersRegion(reportElement,data))).then((()=>(0,_str.getString)("filteradded","core_reportbuilder",reportAddFilter.options[reportAddFilter.selectedIndex].text))).then(_toast.add).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)}})),document.addEventListener("click",(event=>{const reportRemoveFilter=event.target.closest(reportSelectors.actions.reportRemoveFilter);if(reportRemoveFilter){event.preventDefault();const reportElement=reportRemoveFilter.closest(reportSelectors.regions.report),filterContainer=reportRemoveFilter.closest(reportSelectors.regions.activeFilter),filterName=filterContainer.dataset.filterName;_notification.default.saveCancelPromise((0,_str.getString)("deletefilter","core_reportbuilder",filterName),(0,_str.getString)("deletefilterconfirm","core_reportbuilder",filterName),(0,_str.getString)("delete","core"),{triggerElement:reportRemoveFilter}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/filters:remove");return(0,_filters.deleteFilter)(reportElement.dataset.reportId,filterContainer.dataset.filterId).then((data=>reloadSettingsFiltersRegion(reportElement,data))).then((()=>(0,_toast.add)((0,_str.getString)("filterdeleted","core_reportbuilder",filterName)))).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)})).catch((()=>{}))}})),new _sortable_list.default("".concat(reportSelectors.regions.activeFilters," ul"),{isHorizontal:!1}).getElementName=element=>Promise.resolve(element.data("filterName")),(0,_jquery.default)(document).on(_sortable_list.default.EVENTS.DROP,"".concat(reportSelectors.regions.report," li[data-filter-id]"),((event,info)=>{if(info.positionChanged){const pendingPromise=new _pending.default("core_reportbuilder/filters:reorder"),reportElement=event.target.closest(reportSelectors.regions.report),filterId=info.element.data("filterId"),filterPosition=info.element.data("filterPosition");let targetFilterPosition=info.targetNextElement.data("filterPosition")||info.element.siblings().length+2;targetFilterPosition>filterPosition&&targetFilterPosition--,(0,_filters.reorderFilter)(reportElement.dataset.reportId,filterId,targetFilterPosition).then((data=>reloadSettingsFiltersRegion(reportElement,data))).then((()=>(0,_str.getString)("filtermoved","core_reportbuilder",info.element.data("filterName")))).then(_toast.add).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)}})))}})); //# sourceMappingURL=filters.min.js.map \ No newline at end of file diff --git a/reportbuilder/amd/build/local/editor/filters.min.js.map b/reportbuilder/amd/build/local/editor/filters.min.js.map index 678e56fb5d5..1f9e664c706 100644 --- a/reportbuilder/amd/build/local/editor/filters.min.js.map +++ b/reportbuilder/amd/build/local/editor/filters.min.js.map @@ -1 +1 @@ -{"version":3,"file":"filters.min.js","sources":["../../../src/local/editor/filters.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 * Report builder filters editor\n *\n * @module core_reportbuilder/local/editor/filters\n * @copyright 2021 David Matamoros \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport $ from 'jquery';\nimport CustomEvents from 'core/custom_interaction_events';\nimport 'core/inplace_editable';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport SortableList from 'core/sortable_list';\nimport {get_string as getString} from 'core/str';\nimport Templates from 'core/templates';\nimport {add as addToast} from 'core/toast';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {addFilter, deleteFilter, reorderFilter} from 'core_reportbuilder/local/repository/filters';\n\n/**\n * Reload filters settings region\n *\n * @param {Element} reportElement\n * @param {Object} templateContext\n * @return {Promise}\n */\nconst reloadSettingsFiltersRegion = (reportElement, templateContext) => {\n const pendingPromise = new Pending('core_reportbuilder/filters:reload');\n const settingsFiltersRegion = reportElement.querySelector(reportSelectors.regions.settingsFilters);\n\n return Templates.renderForPromise('core_reportbuilder/local/settings/filters', {filters: templateContext})\n .then(({html, js}) => {\n Templates.replaceNode(settingsFiltersRegion, html, js);\n\n initFiltersForm();\n\n // Re-focus the add filter element after reloading the region.\n const reportAddFilter = reportElement.querySelector(reportSelectors.actions.reportAddFilter);\n reportAddFilter?.focus();\n\n return pendingPromise.resolve();\n });\n};\n\n/**\n * Initialise filters form, must be called on each init because the form container is re-created when switching editor modes\n */\nconst initFiltersForm = () => {\n CustomEvents.define(reportSelectors.actions.reportAddFilter, [CustomEvents.events.accessibleChange]);\n};\n\n/**\n * Initialise module, prefetch all required strings\n *\n * @param {Boolean} initialized Ensure we only add our listeners once\n */\nexport const init = initialized => {\n prefetchStrings('core_reportbuilder', [\n 'deletefilter',\n 'deletefilterconfirm',\n 'filteradded',\n 'filterdeleted',\n 'filtermoved',\n ]);\n\n prefetchStrings('core', [\n 'delete',\n ]);\n\n initFiltersForm();\n if (initialized) {\n return;\n }\n\n // Add filter to report. Use custom events helper to ensure consistency across platforms.\n $(document).on(CustomEvents.events.accessibleChange, reportSelectors.actions.reportAddFilter, event => {\n const reportAddFilter = event.target.closest(reportSelectors.actions.reportAddFilter);\n if (reportAddFilter) {\n event.preventDefault();\n\n // Check if dropdown is closed with no filter selected.\n if (reportAddFilter.selectedIndex === 0) {\n return;\n }\n\n const reportElement = reportAddFilter.closest(reportSelectors.regions.report);\n const pendingPromise = new Pending('core_reportbuilder/filters:add');\n\n addFilter(reportElement.dataset.reportId, reportAddFilter.value)\n .then(data => reloadSettingsFiltersRegion(reportElement, data))\n .then(() => getString('filteradded', 'core_reportbuilder',\n reportAddFilter.options[reportAddFilter.selectedIndex].text))\n .then(addToast)\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }\n });\n\n document.addEventListener('click', event => {\n\n // Remove filter from report.\n const reportRemoveFilter = event.target.closest(reportSelectors.actions.reportRemoveFilter);\n if (reportRemoveFilter) {\n event.preventDefault();\n\n const reportElement = reportRemoveFilter.closest(reportSelectors.regions.report);\n const filterContainer = reportRemoveFilter.closest(reportSelectors.regions.activeFilter);\n const filterName = filterContainer.dataset.filterName;\n\n Notification.saveCancelPromise(\n getString('deletefilter', 'core_reportbuilder', filterName),\n getString('deletefilterconfirm', 'core_reportbuilder', filterName),\n getString('delete', 'core'),\n {triggerElement: reportRemoveFilter}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/filters:remove');\n\n return deleteFilter(reportElement.dataset.reportId, filterContainer.dataset.filterId)\n .then(data => reloadSettingsFiltersRegion(reportElement, data))\n .then(() => addToast(getString('filterdeleted', 'core_reportbuilder', filterName)))\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n\n // Initialize sortable list to handle active filters moving (note JQuery dependency, see MDL-72293 for resolution).\n var activeFiltersSortableList = new SortableList(`${reportSelectors.regions.activeFilters} ul`, {isHorizontal: false});\n activeFiltersSortableList.getElementName = element => Promise.resolve(element.data('filterName'));\n\n $(document).on(SortableList.EVENTS.DROP, `${reportSelectors.regions.report} li[data-filter-id]`, (event, info) => {\n if (info.positionChanged) {\n const pendingPromise = new Pending('core_reportbuilder/filters:reorder');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n const filterId = info.element.data('filterId');\n const filterPosition = info.element.data('filterPosition');\n\n // Select target position, if moving to the end then count number of element siblings.\n let targetFilterPosition = info.targetNextElement.data('filterPosition') || info.element.siblings().length + 2;\n if (targetFilterPosition > filterPosition) {\n targetFilterPosition--;\n }\n\n reorderFilter(reportElement.dataset.reportId, filterId, targetFilterPosition)\n .then(data => reloadSettingsFiltersRegion(reportElement, data))\n .then(() => getString('filtermoved', 'core_reportbuilder', info.element.data('filterName')))\n .then(addToast)\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }\n });\n};\n"],"names":["reloadSettingsFiltersRegion","reportElement","templateContext","pendingPromise","Pending","settingsFiltersRegion","querySelector","reportSelectors","regions","settingsFilters","Templates","renderForPromise","filters","then","_ref","html","js","replaceNode","initFiltersForm","reportAddFilter","actions","focus","resolve","define","CustomEvents","events","accessibleChange","initialized","document","on","event","target","closest","preventDefault","selectedIndex","report","dataset","reportId","value","data","options","text","addToast","catch","Notification","exception","addEventListener","reportRemoveFilter","filterContainer","activeFilter","filterName","saveCancelPromise","triggerElement","filterId","SortableList","activeFilters","isHorizontal","getElementName","element","Promise","EVENTS","DROP","info","positionChanged","filterPosition","targetFilterPosition","targetNextElement","siblings","length"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"filters.min.js","sources":["../../../src/local/editor/filters.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 * Report builder filters editor\n *\n * @module core_reportbuilder/local/editor/filters\n * @copyright 2021 David Matamoros \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport $ from 'jquery';\nimport CustomEvents from 'core/custom_interaction_events';\nimport 'core/inplace_editable';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport SortableList from 'core/sortable_list';\nimport {getString} from 'core/str';\nimport Templates from 'core/templates';\nimport {add as addToast} from 'core/toast';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {addFilter, deleteFilter, reorderFilter} from 'core_reportbuilder/local/repository/filters';\n\n/**\n * Reload filters settings region\n *\n * @param {Element} reportElement\n * @param {Object} templateContext\n * @return {Promise}\n */\nconst reloadSettingsFiltersRegion = (reportElement, templateContext) => {\n const pendingPromise = new Pending('core_reportbuilder/filters:reload');\n const settingsFiltersRegion = reportElement.querySelector(reportSelectors.regions.settingsFilters);\n\n return Templates.renderForPromise('core_reportbuilder/local/settings/filters', {filters: templateContext})\n .then(({html, js}) => {\n Templates.replaceNode(settingsFiltersRegion, html, js);\n\n initFiltersForm();\n\n // Re-focus the add filter element after reloading the region.\n const reportAddFilter = reportElement.querySelector(reportSelectors.actions.reportAddFilter);\n reportAddFilter?.focus();\n\n return pendingPromise.resolve();\n });\n};\n\n/**\n * Initialise filters form, must be called on each init because the form container is re-created when switching editor modes\n */\nconst initFiltersForm = () => {\n CustomEvents.define(reportSelectors.actions.reportAddFilter, [CustomEvents.events.accessibleChange]);\n};\n\n/**\n * Initialise module, prefetch all required strings\n *\n * @param {Boolean} initialized Ensure we only add our listeners once\n */\nexport const init = initialized => {\n prefetchStrings('core_reportbuilder', [\n 'deletefilter',\n 'deletefilterconfirm',\n 'filteradded',\n 'filterdeleted',\n 'filtermoved',\n ]);\n\n prefetchStrings('core', [\n 'delete',\n ]);\n\n initFiltersForm();\n if (initialized) {\n return;\n }\n\n // Add filter to report. Use custom events helper to ensure consistency across platforms.\n $(document).on(CustomEvents.events.accessibleChange, reportSelectors.actions.reportAddFilter, event => {\n const reportAddFilter = event.target.closest(reportSelectors.actions.reportAddFilter);\n if (reportAddFilter) {\n event.preventDefault();\n\n // Check if dropdown is closed with no filter selected.\n if (reportAddFilter.selectedIndex === 0) {\n return;\n }\n\n const reportElement = reportAddFilter.closest(reportSelectors.regions.report);\n const pendingPromise = new Pending('core_reportbuilder/filters:add');\n\n addFilter(reportElement.dataset.reportId, reportAddFilter.value)\n .then(data => reloadSettingsFiltersRegion(reportElement, data))\n .then(() => getString('filteradded', 'core_reportbuilder',\n reportAddFilter.options[reportAddFilter.selectedIndex].text))\n .then(addToast)\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }\n });\n\n document.addEventListener('click', event => {\n\n // Remove filter from report.\n const reportRemoveFilter = event.target.closest(reportSelectors.actions.reportRemoveFilter);\n if (reportRemoveFilter) {\n event.preventDefault();\n\n const reportElement = reportRemoveFilter.closest(reportSelectors.regions.report);\n const filterContainer = reportRemoveFilter.closest(reportSelectors.regions.activeFilter);\n const filterName = filterContainer.dataset.filterName;\n\n Notification.saveCancelPromise(\n getString('deletefilter', 'core_reportbuilder', filterName),\n getString('deletefilterconfirm', 'core_reportbuilder', filterName),\n getString('delete', 'core'),\n {triggerElement: reportRemoveFilter}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/filters:remove');\n\n return deleteFilter(reportElement.dataset.reportId, filterContainer.dataset.filterId)\n .then(data => reloadSettingsFiltersRegion(reportElement, data))\n .then(() => addToast(getString('filterdeleted', 'core_reportbuilder', filterName)))\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n\n // Initialize sortable list to handle active filters moving (note JQuery dependency, see MDL-72293 for resolution).\n var activeFiltersSortableList = new SortableList(`${reportSelectors.regions.activeFilters} ul`, {isHorizontal: false});\n activeFiltersSortableList.getElementName = element => Promise.resolve(element.data('filterName'));\n\n $(document).on(SortableList.EVENTS.DROP, `${reportSelectors.regions.report} li[data-filter-id]`, (event, info) => {\n if (info.positionChanged) {\n const pendingPromise = new Pending('core_reportbuilder/filters:reorder');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n const filterId = info.element.data('filterId');\n const filterPosition = info.element.data('filterPosition');\n\n // Select target position, if moving to the end then count number of element siblings.\n let targetFilterPosition = info.targetNextElement.data('filterPosition') || info.element.siblings().length + 2;\n if (targetFilterPosition > filterPosition) {\n targetFilterPosition--;\n }\n\n reorderFilter(reportElement.dataset.reportId, filterId, targetFilterPosition)\n .then(data => reloadSettingsFiltersRegion(reportElement, data))\n .then(() => getString('filtermoved', 'core_reportbuilder', info.element.data('filterName')))\n .then(addToast)\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }\n });\n};\n"],"names":["reloadSettingsFiltersRegion","reportElement","templateContext","pendingPromise","Pending","settingsFiltersRegion","querySelector","reportSelectors","regions","settingsFilters","Templates","renderForPromise","filters","then","_ref","html","js","replaceNode","initFiltersForm","reportAddFilter","actions","focus","resolve","define","CustomEvents","events","accessibleChange","initialized","document","on","event","target","closest","preventDefault","selectedIndex","report","dataset","reportId","value","data","options","text","addToast","catch","Notification","exception","addEventListener","reportRemoveFilter","filterContainer","activeFilter","filterName","saveCancelPromise","triggerElement","filterId","SortableList","activeFilters","isHorizontal","getElementName","element","Promise","EVENTS","DROP","info","positionChanged","filterPosition","targetFilterPosition","targetNextElement","siblings","length"],"mappings":"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"} \ No newline at end of file diff --git a/reportbuilder/amd/build/local/editor/sorting.min.js b/reportbuilder/amd/build/local/editor/sorting.min.js index 07d70313fe5..e2f2830ab9c 100644 --- a/reportbuilder/amd/build/local/editor/sorting.min.js +++ b/reportbuilder/amd/build/local/editor/sorting.min.js @@ -1,3 +1,3 @@ -define("core_reportbuilder/local/editor/sorting",["exports","jquery","core/inplace_editable","core/notification","core/pending","core/pubsub","core/sortable_list","core/str","core/toast","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/sorting","core/templates","core/event_dispatcher","core_reportbuilder/local/events"],(function(_exports,_jquery,_inplace_editable,_notification,_pending,_pubsub,_sortable_list,_str,_toast,reportSelectors,_sorting,_templates,_event_dispatcher,reportEvents){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_sortable_list=_interopRequireDefault(_sortable_list),reportSelectors=_interopRequireWildcard(reportSelectors),_templates=_interopRequireDefault(_templates),reportEvents=_interopRequireWildcard(reportEvents);const SORTORDER_ASCENDING=4,SORTORDER_DESCENDING=3,reloadSettingsSortingRegion=context=>{const pendingPromise=new _pending.default("core_reportbuilder/sorting:reload"),settingsSortingRegion=document.querySelector(reportSelectors.regions.settingsSorting);return _templates.default.renderForPromise("core_reportbuilder/local/settings/sorting",{sorting:context}).then((_ref=>{let{html:html,js:js}=_ref;return _templates.default.replaceNode(settingsSortingRegion,html,js),pendingPromise.resolve()}))},updateSorting=(reportElement,element,sortenabled,sortdirection)=>{const reportId=reportElement.dataset.reportId,listElement=element.closest("li"),columnId=listElement.dataset.columnSortId,columnName=listElement.dataset.columnSortName;return(0,_sorting.toggleColumnSorting)(reportId,columnId,sortenabled,sortdirection).then(reloadSettingsSortingRegion).then((()=>(0,_str.get_string)("columnsortupdated","core_reportbuilder",columnName))).then(_toast.add).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),null)))};_exports.init=initialized=>{initialized||((0,_pubsub.subscribe)(reportEvents.publish.reportColumnsUpdated,(data=>reloadSettingsSortingRegion(data).catch(_notification.default.exception))),document.addEventListener("click",(event=>{const toggleSorting=event.target.closest(reportSelectors.actions.reportToggleColumnSort);if(toggleSorting){event.preventDefault();const pendingPromise=new _pending.default("core_reportbuilder/sorting:toggle"),reportElement=toggleSorting.closest(reportSelectors.regions.report),sortdirection=parseInt(toggleSorting.closest("li").dataset.columnSortDirection);updateSorting(reportElement,toggleSorting,toggleSorting.checked,sortdirection).then((()=>{const toggleSortingElement=document.getElementById(toggleSorting.id);return null==toggleSortingElement||toggleSortingElement.focus(),pendingPromise.resolve()})).catch(_notification.default.exception)}const toggleSortDirection=event.target.closest(reportSelectors.actions.reportToggleColumnSortDirection);if(toggleSortDirection){event.preventDefault();const pendingPromise=new _pending.default("core_reportbuilder/sorting:direction"),reportElement=toggleSortDirection.closest(reportSelectors.regions.report),listElement=toggleSortDirection.closest("li"),toggleSorting=listElement.querySelector(reportSelectors.actions.reportToggleColumnSort);let sortdirection=parseInt(listElement.dataset.columnSortDirection);sortdirection===SORTORDER_ASCENDING?sortdirection=SORTORDER_DESCENDING:sortdirection===SORTORDER_DESCENDING&&(sortdirection=SORTORDER_ASCENDING),updateSorting(reportElement,toggleSortDirection,toggleSorting.checked,sortdirection).then((()=>{const toggleSortDirectionElement=document.getElementById(toggleSortDirection.id);return null==toggleSortDirectionElement||toggleSortDirectionElement.focus(),pendingPromise.resolve()})).catch(_notification.default.exception)}})),new _sortable_list.default("".concat(reportSelectors.regions.settingsSorting," ul"),{isHorizontal:!1}).getElementName=element=>Promise.resolve(element.data("columnSortName")),(0,_jquery.default)(document).on(_sortable_list.default.EVENTS.DROP,"".concat(reportSelectors.regions.report," li[data-column-sort-id]"),((event,info)=>{if(info.positionChanged){const pendingPromise=new _pending.default("core_reportbuilder/sorting:reorder"),reportElement=event.target.closest(reportSelectors.regions.report),columnId=info.element.data("columnSortId"),columnPosition=info.element.data("columnSortPosition");let targetColumnSortPosition=info.targetNextElement.data("columnSortPosition")||info.element.siblings().length+2;targetColumnSortPosition>columnPosition&&targetColumnSortPosition--,(0,_sorting.reorderColumnSorting)(reportElement.dataset.reportId,columnId,targetColumnSortPosition).then(reloadSettingsSortingRegion).then((()=>(0,_str.get_string)("columnsortupdated","core_reportbuilder",info.element.data("columnSortName")))).then(_toast.add).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),null))).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)}})))}})); +define("core_reportbuilder/local/editor/sorting",["exports","jquery","core/inplace_editable","core/notification","core/pending","core/pubsub","core/sortable_list","core/str","core/toast","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/sorting","core/templates","core/event_dispatcher","core_reportbuilder/local/events"],(function(_exports,_jquery,_inplace_editable,_notification,_pending,_pubsub,_sortable_list,_str,_toast,reportSelectors,_sorting,_templates,_event_dispatcher,reportEvents){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_sortable_list=_interopRequireDefault(_sortable_list),reportSelectors=_interopRequireWildcard(reportSelectors),_templates=_interopRequireDefault(_templates),reportEvents=_interopRequireWildcard(reportEvents);const SORTORDER_ASCENDING=4,SORTORDER_DESCENDING=3,reloadSettingsSortingRegion=context=>{const pendingPromise=new _pending.default("core_reportbuilder/sorting:reload"),settingsSortingRegion=document.querySelector(reportSelectors.regions.settingsSorting);return _templates.default.renderForPromise("core_reportbuilder/local/settings/sorting",{sorting:context}).then((_ref=>{let{html:html,js:js}=_ref;return _templates.default.replaceNode(settingsSortingRegion,html,js),pendingPromise.resolve()}))},updateSorting=(reportElement,element,sortenabled,sortdirection)=>{const reportId=reportElement.dataset.reportId,listElement=element.closest("li"),columnId=listElement.dataset.columnSortId,columnName=listElement.dataset.columnSortName;return(0,_sorting.toggleColumnSorting)(reportId,columnId,sortenabled,sortdirection).then(reloadSettingsSortingRegion).then((()=>(0,_str.getString)("columnsortupdated","core_reportbuilder",columnName))).then(_toast.add).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),null)))};_exports.init=initialized=>{initialized||((0,_pubsub.subscribe)(reportEvents.publish.reportColumnsUpdated,(data=>reloadSettingsSortingRegion(data).catch(_notification.default.exception))),document.addEventListener("click",(event=>{const toggleSorting=event.target.closest(reportSelectors.actions.reportToggleColumnSort);if(toggleSorting){event.preventDefault();const pendingPromise=new _pending.default("core_reportbuilder/sorting:toggle"),reportElement=toggleSorting.closest(reportSelectors.regions.report),sortdirection=parseInt(toggleSorting.closest("li").dataset.columnSortDirection);updateSorting(reportElement,toggleSorting,toggleSorting.checked,sortdirection).then((()=>{const toggleSortingElement=document.getElementById(toggleSorting.id);return null==toggleSortingElement||toggleSortingElement.focus(),pendingPromise.resolve()})).catch(_notification.default.exception)}const toggleSortDirection=event.target.closest(reportSelectors.actions.reportToggleColumnSortDirection);if(toggleSortDirection){event.preventDefault();const pendingPromise=new _pending.default("core_reportbuilder/sorting:direction"),reportElement=toggleSortDirection.closest(reportSelectors.regions.report),listElement=toggleSortDirection.closest("li"),toggleSorting=listElement.querySelector(reportSelectors.actions.reportToggleColumnSort);let sortdirection=parseInt(listElement.dataset.columnSortDirection);sortdirection===SORTORDER_ASCENDING?sortdirection=SORTORDER_DESCENDING:sortdirection===SORTORDER_DESCENDING&&(sortdirection=SORTORDER_ASCENDING),updateSorting(reportElement,toggleSortDirection,toggleSorting.checked,sortdirection).then((()=>{const toggleSortDirectionElement=document.getElementById(toggleSortDirection.id);return null==toggleSortDirectionElement||toggleSortDirectionElement.focus(),pendingPromise.resolve()})).catch(_notification.default.exception)}})),new _sortable_list.default("".concat(reportSelectors.regions.settingsSorting," ul"),{isHorizontal:!1}).getElementName=element=>Promise.resolve(element.data("columnSortName")),(0,_jquery.default)(document).on(_sortable_list.default.EVENTS.DROP,"".concat(reportSelectors.regions.report," li[data-column-sort-id]"),((event,info)=>{if(info.positionChanged){const pendingPromise=new _pending.default("core_reportbuilder/sorting:reorder"),reportElement=event.target.closest(reportSelectors.regions.report),columnId=info.element.data("columnSortId"),columnPosition=info.element.data("columnSortPosition");let targetColumnSortPosition=info.targetNextElement.data("columnSortPosition")||info.element.siblings().length+2;targetColumnSortPosition>columnPosition&&targetColumnSortPosition--,(0,_sorting.reorderColumnSorting)(reportElement.dataset.reportId,columnId,targetColumnSortPosition).then(reloadSettingsSortingRegion).then((()=>(0,_str.getString)("columnsortupdated","core_reportbuilder",info.element.data("columnSortName")))).then(_toast.add).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement),null))).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)}})))}})); //# sourceMappingURL=sorting.min.js.map \ No newline at end of file diff --git a/reportbuilder/amd/build/local/editor/sorting.min.js.map b/reportbuilder/amd/build/local/editor/sorting.min.js.map index 7fba81ed85f..5ab53545e32 100644 --- a/reportbuilder/amd/build/local/editor/sorting.min.js.map +++ b/reportbuilder/amd/build/local/editor/sorting.min.js.map @@ -1 +1 @@ -{"version":3,"file":"sorting.min.js","sources":["../../../src/local/editor/sorting.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 * Report builder columns sorting editor\n *\n * @module core_reportbuilder/local/editor/sorting\n * @copyright 2021 David Matamoros \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport $ from 'jquery';\nimport 'core/inplace_editable';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {subscribe} from 'core/pubsub';\nimport SortableList from 'core/sortable_list';\nimport {get_string as getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {reorderColumnSorting, toggleColumnSorting} from 'core_reportbuilder/local/repository/sorting';\nimport Templates from 'core/templates';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport * as reportEvents from 'core_reportbuilder/local/events';\n\n// These constants match PHP consts SORT_ASC, SORT_DESC.\nconst SORTORDER = {\n ASCENDING: 4,\n DESCENDING: 3,\n};\n\n/**\n * Reload sorting settings region\n *\n * @param {Object} context\n * @return {Promise}\n */\nconst reloadSettingsSortingRegion = context => {\n const pendingPromise = new Pending('core_reportbuilder/sorting:reload');\n const settingsSortingRegion = document.querySelector(reportSelectors.regions.settingsSorting);\n\n return Templates.renderForPromise('core_reportbuilder/local/settings/sorting', {sorting: context})\n .then(({html, js}) => {\n Templates.replaceNode(settingsSortingRegion, html, js);\n return pendingPromise.resolve();\n });\n};\n\n/**\n * Updates column sorting\n *\n * @param {Element} reportElement\n * @param {Element} element\n * @param {Number} sortenabled\n * @param {Number} sortdirection\n * @return {Promise}\n */\nconst updateSorting = (reportElement, element, sortenabled, sortdirection) => {\n const reportId = reportElement.dataset.reportId;\n const listElement = element.closest('li');\n const columnId = listElement.dataset.columnSortId;\n const columnName = listElement.dataset.columnSortName;\n\n return toggleColumnSorting(reportId, columnId, sortenabled, sortdirection)\n .then(reloadSettingsSortingRegion)\n .then(() => getString('columnsortupdated', 'core_reportbuilder', columnName))\n .then(addToast)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return null;\n });\n};\n\n/**\n * Initialise module\n *\n * @param {Boolean} initialized Ensure we only add our listeners once\n */\nexport const init = (initialized) => {\n if (initialized) {\n return;\n }\n\n // Update sorting region each time report columns are updated (added or removed).\n subscribe(reportEvents.publish.reportColumnsUpdated, data => reloadSettingsSortingRegion(data)\n .catch(Notification.exception)\n );\n\n document.addEventListener('click', event => {\n\n // Enable/disable sorting on columns.\n const toggleSorting = event.target.closest(reportSelectors.actions.reportToggleColumnSort);\n if (toggleSorting) {\n event.preventDefault();\n\n const pendingPromise = new Pending('core_reportbuilder/sorting:toggle');\n const reportElement = toggleSorting.closest(reportSelectors.regions.report);\n const sortdirection = parseInt(toggleSorting.closest('li').dataset.columnSortDirection);\n\n updateSorting(reportElement, toggleSorting, toggleSorting.checked, sortdirection)\n .then(() => {\n // Re-focus the toggle sorting element after reloading the region.\n const toggleSortingElement = document.getElementById(toggleSorting.id);\n toggleSortingElement?.focus();\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n\n // Change column sort direction.\n const toggleSortDirection = event.target.closest(reportSelectors.actions.reportToggleColumnSortDirection);\n if (toggleSortDirection) {\n event.preventDefault();\n\n const pendingPromise = new Pending('core_reportbuilder/sorting:direction');\n const reportElement = toggleSortDirection.closest(reportSelectors.regions.report);\n const listElement = toggleSortDirection.closest('li');\n const toggleSorting = listElement.querySelector(reportSelectors.actions.reportToggleColumnSort);\n\n let sortdirection = parseInt(listElement.dataset.columnSortDirection);\n if (sortdirection === SORTORDER.ASCENDING) {\n sortdirection = SORTORDER.DESCENDING;\n } else if (sortdirection === SORTORDER.DESCENDING) {\n sortdirection = SORTORDER.ASCENDING;\n }\n\n updateSorting(reportElement, toggleSortDirection, toggleSorting.checked, sortdirection)\n .then(() => {\n // Re-focus the toggle sort direction element after reloading the region.\n const toggleSortDirectionElement = document.getElementById(toggleSortDirection.id);\n toggleSortDirectionElement?.focus();\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n });\n\n // Initialize sortable list to handle column sorting moving (note JQuery dependency, see MDL-72293 for resolution).\n var columnsSortingSortableList = new SortableList(`${reportSelectors.regions.settingsSorting} ul`, {isHorizontal: false});\n columnsSortingSortableList.getElementName = element => Promise.resolve(element.data('columnSortName'));\n\n $(document).on(SortableList.EVENTS.DROP, `${reportSelectors.regions.report} li[data-column-sort-id]`, (event, info) => {\n if (info.positionChanged) {\n const pendingPromise = new Pending('core_reportbuilder/sorting:reorder');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n const columnId = info.element.data('columnSortId');\n const columnPosition = info.element.data('columnSortPosition');\n\n // Select target position, if moving to the end then count number of element siblings.\n let targetColumnSortPosition = info.targetNextElement.data('columnSortPosition') || info.element.siblings().length + 2;\n if (targetColumnSortPosition > columnPosition) {\n targetColumnSortPosition--;\n }\n\n reorderColumnSorting(reportElement.dataset.reportId, columnId, targetColumnSortPosition)\n .then(reloadSettingsSortingRegion)\n .then(() => getString('columnsortupdated', 'core_reportbuilder', info.element.data('columnSortName')))\n .then(addToast)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return null;\n })\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }\n });\n};\n"],"names":["SORTORDER","reloadSettingsSortingRegion","context","pendingPromise","Pending","settingsSortingRegion","document","querySelector","reportSelectors","regions","settingsSorting","Templates","renderForPromise","sorting","then","_ref","html","js","replaceNode","resolve","updateSorting","reportElement","element","sortenabled","sortdirection","reportId","dataset","listElement","closest","columnId","columnSortId","columnName","columnSortName","addToast","reportEvents","tableReload","initialized","publish","reportColumnsUpdated","data","catch","Notification","exception","addEventListener","event","toggleSorting","target","actions","reportToggleColumnSort","preventDefault","report","parseInt","columnSortDirection","checked","toggleSortingElement","getElementById","id","focus","toggleSortDirection","reportToggleColumnSortDirection","toggleSortDirectionElement","SortableList","isHorizontal","getElementName","Promise","on","EVENTS","DROP","info","positionChanged","columnPosition","targetColumnSortPosition","targetNextElement","siblings","length"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"sorting.min.js","sources":["../../../src/local/editor/sorting.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 * Report builder columns sorting editor\n *\n * @module core_reportbuilder/local/editor/sorting\n * @copyright 2021 David Matamoros \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport $ from 'jquery';\nimport 'core/inplace_editable';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {subscribe} from 'core/pubsub';\nimport SortableList from 'core/sortable_list';\nimport {getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {reorderColumnSorting, toggleColumnSorting} from 'core_reportbuilder/local/repository/sorting';\nimport Templates from 'core/templates';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport * as reportEvents from 'core_reportbuilder/local/events';\n\n// These constants match PHP consts SORT_ASC, SORT_DESC.\nconst SORTORDER = {\n ASCENDING: 4,\n DESCENDING: 3,\n};\n\n/**\n * Reload sorting settings region\n *\n * @param {Object} context\n * @return {Promise}\n */\nconst reloadSettingsSortingRegion = context => {\n const pendingPromise = new Pending('core_reportbuilder/sorting:reload');\n const settingsSortingRegion = document.querySelector(reportSelectors.regions.settingsSorting);\n\n return Templates.renderForPromise('core_reportbuilder/local/settings/sorting', {sorting: context})\n .then(({html, js}) => {\n Templates.replaceNode(settingsSortingRegion, html, js);\n return pendingPromise.resolve();\n });\n};\n\n/**\n * Updates column sorting\n *\n * @param {Element} reportElement\n * @param {Element} element\n * @param {Number} sortenabled\n * @param {Number} sortdirection\n * @return {Promise}\n */\nconst updateSorting = (reportElement, element, sortenabled, sortdirection) => {\n const reportId = reportElement.dataset.reportId;\n const listElement = element.closest('li');\n const columnId = listElement.dataset.columnSortId;\n const columnName = listElement.dataset.columnSortName;\n\n return toggleColumnSorting(reportId, columnId, sortenabled, sortdirection)\n .then(reloadSettingsSortingRegion)\n .then(() => getString('columnsortupdated', 'core_reportbuilder', columnName))\n .then(addToast)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return null;\n });\n};\n\n/**\n * Initialise module\n *\n * @param {Boolean} initialized Ensure we only add our listeners once\n */\nexport const init = (initialized) => {\n if (initialized) {\n return;\n }\n\n // Update sorting region each time report columns are updated (added or removed).\n subscribe(reportEvents.publish.reportColumnsUpdated, data => reloadSettingsSortingRegion(data)\n .catch(Notification.exception)\n );\n\n document.addEventListener('click', event => {\n\n // Enable/disable sorting on columns.\n const toggleSorting = event.target.closest(reportSelectors.actions.reportToggleColumnSort);\n if (toggleSorting) {\n event.preventDefault();\n\n const pendingPromise = new Pending('core_reportbuilder/sorting:toggle');\n const reportElement = toggleSorting.closest(reportSelectors.regions.report);\n const sortdirection = parseInt(toggleSorting.closest('li').dataset.columnSortDirection);\n\n updateSorting(reportElement, toggleSorting, toggleSorting.checked, sortdirection)\n .then(() => {\n // Re-focus the toggle sorting element after reloading the region.\n const toggleSortingElement = document.getElementById(toggleSorting.id);\n toggleSortingElement?.focus();\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n\n // Change column sort direction.\n const toggleSortDirection = event.target.closest(reportSelectors.actions.reportToggleColumnSortDirection);\n if (toggleSortDirection) {\n event.preventDefault();\n\n const pendingPromise = new Pending('core_reportbuilder/sorting:direction');\n const reportElement = toggleSortDirection.closest(reportSelectors.regions.report);\n const listElement = toggleSortDirection.closest('li');\n const toggleSorting = listElement.querySelector(reportSelectors.actions.reportToggleColumnSort);\n\n let sortdirection = parseInt(listElement.dataset.columnSortDirection);\n if (sortdirection === SORTORDER.ASCENDING) {\n sortdirection = SORTORDER.DESCENDING;\n } else if (sortdirection === SORTORDER.DESCENDING) {\n sortdirection = SORTORDER.ASCENDING;\n }\n\n updateSorting(reportElement, toggleSortDirection, toggleSorting.checked, sortdirection)\n .then(() => {\n // Re-focus the toggle sort direction element after reloading the region.\n const toggleSortDirectionElement = document.getElementById(toggleSortDirection.id);\n toggleSortDirectionElement?.focus();\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n });\n\n // Initialize sortable list to handle column sorting moving (note JQuery dependency, see MDL-72293 for resolution).\n var columnsSortingSortableList = new SortableList(`${reportSelectors.regions.settingsSorting} ul`, {isHorizontal: false});\n columnsSortingSortableList.getElementName = element => Promise.resolve(element.data('columnSortName'));\n\n $(document).on(SortableList.EVENTS.DROP, `${reportSelectors.regions.report} li[data-column-sort-id]`, (event, info) => {\n if (info.positionChanged) {\n const pendingPromise = new Pending('core_reportbuilder/sorting:reorder');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n const columnId = info.element.data('columnSortId');\n const columnPosition = info.element.data('columnSortPosition');\n\n // Select target position, if moving to the end then count number of element siblings.\n let targetColumnSortPosition = info.targetNextElement.data('columnSortPosition') || info.element.siblings().length + 2;\n if (targetColumnSortPosition > columnPosition) {\n targetColumnSortPosition--;\n }\n\n reorderColumnSorting(reportElement.dataset.reportId, columnId, targetColumnSortPosition)\n .then(reloadSettingsSortingRegion)\n .then(() => getString('columnsortupdated', 'core_reportbuilder', info.element.data('columnSortName')))\n .then(addToast)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return null;\n })\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }\n });\n};\n"],"names":["SORTORDER","reloadSettingsSortingRegion","context","pendingPromise","Pending","settingsSortingRegion","document","querySelector","reportSelectors","regions","settingsSorting","Templates","renderForPromise","sorting","then","_ref","html","js","replaceNode","resolve","updateSorting","reportElement","element","sortenabled","sortdirection","reportId","dataset","listElement","closest","columnId","columnSortId","columnName","columnSortName","addToast","reportEvents","tableReload","initialized","publish","reportColumnsUpdated","data","catch","Notification","exception","addEventListener","event","toggleSorting","target","actions","reportToggleColumnSort","preventDefault","report","parseInt","columnSortDirection","checked","toggleSortingElement","getElementById","id","focus","toggleSortDirection","reportToggleColumnSortDirection","toggleSortDirectionElement","SortableList","isHorizontal","getElementName","Promise","on","EVENTS","DROP","info","positionChanged","columnPosition","targetColumnSortPosition","targetNextElement","siblings","length"],"mappings":"u7DAwCMA,oBACS,EADTA,qBAEU,EASVC,4BAA8BC,gBAC1BC,eAAiB,IAAIC,iBAAQ,qCAC7BC,sBAAwBC,SAASC,cAAcC,gBAAgBC,QAAQC,wBAEtEC,mBAAUC,iBAAiB,4CAA6C,CAACC,QAASX,UACpFY,MAAKC,WAACC,KAACA,KAADC,GAAOA,mCACAC,YAAYb,sBAAuBW,KAAMC,IAC5Cd,eAAegB,cAa5BC,cAAgB,CAACC,cAAeC,QAASC,YAAaC,uBAClDC,SAAWJ,cAAcK,QAAQD,SACjCE,YAAcL,QAAQM,QAAQ,MAC9BC,SAAWF,YAAYD,QAAQI,aAC/BC,WAAaJ,YAAYD,QAAQM,sBAEhC,gCAAoBP,SAAUI,SAAUN,YAAaC,eACvDV,KAAKb,6BACLa,MAAK,KAAM,kBAAU,oBAAqB,qBAAsBiB,cAChEjB,KAAKmB,YACLnB,MAAK,yCACYoB,aAAaC,YAAa,GAAId,eACrC,uBASEe,cACbA,oCAKMF,aAAaG,QAAQC,sBAAsBC,MAAQtC,4BAA4BsC,MACpFC,MAAMC,sBAAaC,aAGxBpC,SAASqC,iBAAiB,SAASC,cAGzBC,cAAgBD,MAAME,OAAOlB,QAAQpB,gBAAgBuC,QAAQC,2BAC/DH,cAAe,CACfD,MAAMK,uBAEA9C,eAAiB,IAAIC,iBAAQ,qCAC7BiB,cAAgBwB,cAAcjB,QAAQpB,gBAAgBC,QAAQyC,QAC9D1B,cAAgB2B,SAASN,cAAcjB,QAAQ,MAAMF,QAAQ0B,qBAEnEhC,cAAcC,cAAewB,cAAeA,cAAcQ,QAAS7B,eAC9DV,MAAK,WAEIwC,qBAAuBhD,SAASiD,eAAeV,cAAcW,WACnEF,MAAAA,sBAAAA,qBAAsBG,QACftD,eAAegB,aAEzBqB,MAAMC,sBAAaC,iBAItBgB,oBAAsBd,MAAME,OAAOlB,QAAQpB,gBAAgBuC,QAAQY,oCACrED,oBAAqB,CACrBd,MAAMK,uBAEA9C,eAAiB,IAAIC,iBAAQ,wCAC7BiB,cAAgBqC,oBAAoB9B,QAAQpB,gBAAgBC,QAAQyC,QACpEvB,YAAc+B,oBAAoB9B,QAAQ,MAC1CiB,cAAgBlB,YAAYpB,cAAcC,gBAAgBuC,QAAQC,4BAEpExB,cAAgB2B,SAASxB,YAAYD,QAAQ0B,qBAC7C5B,gBAAkBxB,oBAClBwB,cAAgBxB,qBACTwB,gBAAkBxB,uBACzBwB,cAAgBxB,qBAGpBoB,cAAcC,cAAeqC,oBAAqBb,cAAcQ,QAAS7B,eACpEV,MAAK,WAEI8C,2BAA6BtD,SAASiD,eAAeG,oBAAoBF,WAC/EI,MAAAA,4BAAAA,2BAA4BH,QACrBtD,eAAegB,aAEzBqB,MAAMC,sBAAaC,eAKC,IAAImB,iCAAgBrD,gBAAgBC,QAAQC,uBAAsB,CAACoD,cAAc,IACvFC,eAAiBzC,SAAW0C,QAAQ7C,QAAQG,QAAQiB,KAAK,uCAElFjC,UAAU2D,GAAGJ,uBAAaK,OAAOC,eAAS3D,gBAAgBC,QAAQyC,oCAAkC,CAACN,MAAOwB,WACtGA,KAAKC,gBAAiB,OAChBlE,eAAiB,IAAIC,iBAAQ,sCAC7BiB,cAAgBuB,MAAME,OAAOlB,QAAQpB,gBAAgBC,QAAQyC,QAC7DrB,SAAWuC,KAAK9C,QAAQiB,KAAK,gBAC7B+B,eAAiBF,KAAK9C,QAAQiB,KAAK,0BAGrCgC,yBAA2BH,KAAKI,kBAAkBjC,KAAK,uBAAyB6B,KAAK9C,QAAQmD,WAAWC,OAAS,EACjHH,yBAA2BD,gBAC3BC,6DAGiBlD,cAAcK,QAAQD,SAAUI,SAAU0C,0BAC1DzD,KAAKb,6BACLa,MAAK,KAAM,kBAAU,oBAAqB,qBAAsBsD,KAAK9C,QAAQiB,KAAK,qBAClFzB,KAAKmB,YACLnB,MAAK,yCACYoB,aAAaC,YAAa,GAAId,eACrC,QAEVP,MAAK,IAAMX,eAAegB,YAC1BqB,MAAMC,sBAAaC"} \ No newline at end of file diff --git a/reportbuilder/amd/build/local/repository/modals.min.js b/reportbuilder/amd/build/local/repository/modals.min.js index 3cf9fb633ac..9f3b4414429 100644 --- a/reportbuilder/amd/build/local/repository/modals.min.js +++ b/reportbuilder/amd/build/local/repository/modals.min.js @@ -5,6 +5,6 @@ define("core_reportbuilder/local/repository/modals",["exports","core_form/modalf * @module core_reportbuilder/local/repository/modals * @copyright 2021 David Matamoros * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.createScheduleModal=_exports.createReportModal=void 0,_modalform=(obj=_modalform)&&obj.__esModule?obj:{default:obj};const createModalForm=(triggerElement,modalTitle,formClass,formArgs)=>new _modalform.default({modalConfig:{title:modalTitle},formClass:formClass,args:formArgs,saveButtonText:(0,_str.get_string)("save","moodle"),returnFocus:triggerElement});_exports.createReportModal=function(triggerElement,modalTitle){let reportId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return createModalForm(triggerElement,modalTitle,"core_reportbuilder\\form\\report",{id:reportId})};_exports.createScheduleModal=function(triggerElement,modalTitle,reportId){let scheduleId=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0;return createModalForm(triggerElement,modalTitle,"core_reportbuilder\\form\\schedule",{reportid:reportId,id:scheduleId})}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.createScheduleModal=_exports.createReportModal=void 0,_modalform=(obj=_modalform)&&obj.__esModule?obj:{default:obj};const createModalForm=(triggerElement,modalTitle,formClass,formArgs)=>new _modalform.default({modalConfig:{title:modalTitle},formClass:formClass,args:formArgs,saveButtonText:(0,_str.getString)("save","moodle"),returnFocus:triggerElement});_exports.createReportModal=function(triggerElement,modalTitle){let reportId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return createModalForm(triggerElement,modalTitle,"core_reportbuilder\\form\\report",{id:reportId})};_exports.createScheduleModal=function(triggerElement,modalTitle,reportId){let scheduleId=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0;return createModalForm(triggerElement,modalTitle,"core_reportbuilder\\form\\schedule",{reportid:reportId,id:scheduleId})}})); //# sourceMappingURL=modals.min.js.map \ No newline at end of file diff --git a/reportbuilder/amd/build/local/repository/modals.min.js.map b/reportbuilder/amd/build/local/repository/modals.min.js.map index 583b7e24ea8..7a6cc5c54fb 100644 --- a/reportbuilder/amd/build/local/repository/modals.min.js.map +++ b/reportbuilder/amd/build/local/repository/modals.min.js.map @@ -1 +1 @@ -{"version":3,"file":"modals.min.js","sources":["../../../src/local/repository/modals.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 * Module to handle modal form requests\n *\n * @module core_reportbuilder/local/repository/modals\n * @copyright 2021 David Matamoros \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Return modal instance\n *\n * @param {EventTarget} triggerElement\n * @param {Promise} modalTitle\n * @param {String} formClass\n * @param {Object} formArgs\n * @return {ModalForm}\n */\nconst createModalForm = (triggerElement, modalTitle, formClass, formArgs) => {\n return new ModalForm({\n modalConfig: {\n title: modalTitle,\n },\n formClass: formClass,\n args: formArgs,\n saveButtonText: getString('save', 'moodle'),\n returnFocus: triggerElement,\n });\n};\n\n/**\n * Return report modal instance\n *\n * @param {EventTarget} triggerElement\n * @param {Promise} modalTitle\n * @param {Number} reportId\n * @return {ModalForm}\n */\nexport const createReportModal = (triggerElement, modalTitle, reportId = 0) => {\n return createModalForm(triggerElement, modalTitle, 'core_reportbuilder\\\\form\\\\report', {\n id: reportId,\n });\n};\n\n/**\n * Return schedule modal instance\n *\n * @param {EventTarget} triggerElement\n * @param {Promise} modalTitle\n * @param {Number} reportId\n * @param {Number} scheduleId\n * @return {ModalForm}\n */\nexport const createScheduleModal = (triggerElement, modalTitle, reportId, scheduleId = 0) => {\n return createModalForm(triggerElement, modalTitle, 'core_reportbuilder\\\\form\\\\schedule', {\n reportid: reportId,\n id: scheduleId,\n });\n};\n"],"names":["createModalForm","triggerElement","modalTitle","formClass","formArgs","ModalForm","modalConfig","title","args","saveButtonText","returnFocus","reportId","id","scheduleId","reportid"],"mappings":";;;;;;;gMAmCMA,gBAAkB,CAACC,eAAgBC,WAAYC,UAAWC,WACrD,IAAIC,mBAAU,CACjBC,YAAa,CACTC,MAAOL,YAEXC,UAAWA,UACXK,KAAMJ,SACNK,gBAAgB,mBAAU,OAAQ,UAClCC,YAAaT,4CAYY,SAACA,eAAgBC,gBAAYS,gEAAW,SAC9DX,gBAAgBC,eAAgBC,WAAY,mCAAoC,CACnFU,GAAID,yCAauB,SAACV,eAAgBC,WAAYS,cAAUE,kEAAa,SAC5Eb,gBAAgBC,eAAgBC,WAAY,qCAAsC,CACrFY,SAAUH,SACVC,GAAIC"} \ No newline at end of file +{"version":3,"file":"modals.min.js","sources":["../../../src/local/repository/modals.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 * Module to handle modal form requests\n *\n * @module core_reportbuilder/local/repository/modals\n * @copyright 2021 David Matamoros \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport {getString} from 'core/str';\n\n/**\n * Return modal instance\n *\n * @param {EventTarget} triggerElement\n * @param {Promise} modalTitle\n * @param {String} formClass\n * @param {Object} formArgs\n * @return {ModalForm}\n */\nconst createModalForm = (triggerElement, modalTitle, formClass, formArgs) => {\n return new ModalForm({\n modalConfig: {\n title: modalTitle,\n },\n formClass: formClass,\n args: formArgs,\n saveButtonText: getString('save', 'moodle'),\n returnFocus: triggerElement,\n });\n};\n\n/**\n * Return report modal instance\n *\n * @param {EventTarget} triggerElement\n * @param {Promise} modalTitle\n * @param {Number} reportId\n * @return {ModalForm}\n */\nexport const createReportModal = (triggerElement, modalTitle, reportId = 0) => {\n return createModalForm(triggerElement, modalTitle, 'core_reportbuilder\\\\form\\\\report', {\n id: reportId,\n });\n};\n\n/**\n * Return schedule modal instance\n *\n * @param {EventTarget} triggerElement\n * @param {Promise} modalTitle\n * @param {Number} reportId\n * @param {Number} scheduleId\n * @return {ModalForm}\n */\nexport const createScheduleModal = (triggerElement, modalTitle, reportId, scheduleId = 0) => {\n return createModalForm(triggerElement, modalTitle, 'core_reportbuilder\\\\form\\\\schedule', {\n reportid: reportId,\n id: scheduleId,\n });\n};\n"],"names":["createModalForm","triggerElement","modalTitle","formClass","formArgs","ModalForm","modalConfig","title","args","saveButtonText","returnFocus","reportId","id","scheduleId","reportid"],"mappings":";;;;;;;gMAmCMA,gBAAkB,CAACC,eAAgBC,WAAYC,UAAWC,WACrD,IAAIC,mBAAU,CACjBC,YAAa,CACTC,MAAOL,YAEXC,UAAWA,UACXK,KAAMJ,SACNK,gBAAgB,kBAAU,OAAQ,UAClCC,YAAaT,4CAYY,SAACA,eAAgBC,gBAAYS,gEAAW,SAC9DX,gBAAgBC,eAAgBC,WAAY,mCAAoC,CACnFU,GAAID,yCAauB,SAACV,eAAgBC,WAAYS,cAAUE,kEAAa,SAC5Eb,gBAAgBC,eAAgBC,WAAY,qCAAsC,CACrFY,SAAUH,SACVC,GAAIC"} \ No newline at end of file diff --git a/reportbuilder/amd/build/reports_list.min.js b/reportbuilder/amd/build/reports_list.min.js index fd8fb6e5e6c..3bb721e38d9 100644 --- a/reportbuilder/amd/build/reports_list.min.js +++ b/reportbuilder/amd/build/reports_list.min.js @@ -1,3 +1,3 @@ -define("core_reportbuilder/reports_list",["exports","core/event_dispatcher","core/notification","core/pending","core/prefetch","core/str","core/toast","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/reports","core_reportbuilder/local/repository/modals"],(function(_exports,_event_dispatcher,_notification,_pending,_prefetch,_str,_toast,reportEvents,reportSelectors,_reports,_modals){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);_exports.init=()=>{(0,_prefetch.prefetchStrings)("core_reportbuilder",["deletereport","deletereportconfirm","editreportdetails","newreport","reportdeleted","reportupdated"]),(0,_prefetch.prefetchStrings)("core",["delete"]),document.addEventListener("click",(event=>{if(event.target.closest(reportSelectors.actions.reportCreate)){event.preventDefault();const reportModal=(0,_modals.createReportModal)(event.target,(0,_str.get_string)("newreport","core_reportbuilder"));reportModal.addEventListener(reportModal.events.FORM_SUBMITTED,(event=>{window.location.href=event.detail})),reportModal.show()}const reportEdit=event.target.closest(reportSelectors.actions.reportEdit);if(reportEdit){event.preventDefault();const triggerElement=reportEdit.closest(".dropdown").querySelector(".dropdown-toggle"),reportModal=(0,_modals.createReportModal)(triggerElement,(0,_str.get_string)("editreportdetails","core_reportbuilder"),reportEdit.dataset.reportId);reportModal.addEventListener(reportModal.events.FORM_SUBMITTED,(()=>{const reportElement=event.target.closest(reportSelectors.regions.report);(0,_str.get_string)("reportupdated","core_reportbuilder").then(_toast.add).then((()=>{(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement)})).catch(_notification.default.exception)})),reportModal.show()}const reportDelete=event.target.closest(reportSelectors.actions.reportDelete);if(reportDelete){event.preventDefault();const triggerElement=reportDelete.closest(".dropdown").querySelector(".dropdown-toggle");_notification.default.saveCancelPromise((0,_str.get_string)("deletereport","core_reportbuilder"),(0,_str.get_string)("deletereportconfirm","core_reportbuilder",reportDelete.dataset.reportName),(0,_str.get_string)("delete","core"),{triggerElement:triggerElement}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/reports:delete"),reportElement=event.target.closest(reportSelectors.regions.report);return(0,_reports.deleteReport)(reportDelete.dataset.reportId).then((()=>(0,_toast.add)((0,_str.get_string)("reportdeleted","core_reportbuilder")))).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))}}))}})); +define("core_reportbuilder/reports_list",["exports","core/event_dispatcher","core/notification","core/pending","core/prefetch","core/str","core/toast","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/reports","core_reportbuilder/local/repository/modals"],(function(_exports,_event_dispatcher,_notification,_pending,_prefetch,_str,_toast,reportEvents,reportSelectors,_reports,_modals){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);_exports.init=()=>{(0,_prefetch.prefetchStrings)("core_reportbuilder",["deletereport","deletereportconfirm","editreportdetails","newreport","reportdeleted","reportupdated"]),(0,_prefetch.prefetchStrings)("core",["delete"]),document.addEventListener("click",(event=>{if(event.target.closest(reportSelectors.actions.reportCreate)){event.preventDefault();const reportModal=(0,_modals.createReportModal)(event.target,(0,_str.getString)("newreport","core_reportbuilder"));reportModal.addEventListener(reportModal.events.FORM_SUBMITTED,(event=>{window.location.href=event.detail})),reportModal.show()}const reportEdit=event.target.closest(reportSelectors.actions.reportEdit);if(reportEdit){event.preventDefault();const triggerElement=reportEdit.closest(".dropdown").querySelector(".dropdown-toggle"),reportModal=(0,_modals.createReportModal)(triggerElement,(0,_str.getString)("editreportdetails","core_reportbuilder"),reportEdit.dataset.reportId);reportModal.addEventListener(reportModal.events.FORM_SUBMITTED,(()=>{const reportElement=event.target.closest(reportSelectors.regions.report);(0,_str.getString)("reportupdated","core_reportbuilder").then(_toast.add).then((()=>{(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement)})).catch(_notification.default.exception)})),reportModal.show()}const reportDelete=event.target.closest(reportSelectors.actions.reportDelete);if(reportDelete){event.preventDefault();const triggerElement=reportDelete.closest(".dropdown").querySelector(".dropdown-toggle");_notification.default.saveCancelPromise((0,_str.getString)("deletereport","core_reportbuilder"),(0,_str.getString)("deletereportconfirm","core_reportbuilder",reportDelete.dataset.reportName),(0,_str.getString)("delete","core"),{triggerElement:triggerElement}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/reports:delete"),reportElement=event.target.closest(reportSelectors.regions.report);return(0,_reports.deleteReport)(reportDelete.dataset.reportId).then((()=>(0,_toast.add)((0,_str.getString)("reportdeleted","core_reportbuilder")))).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))}}))}})); //# sourceMappingURL=reports_list.min.js.map \ No newline at end of file diff --git a/reportbuilder/amd/build/reports_list.min.js.map b/reportbuilder/amd/build/reports_list.min.js.map index fc4f4db22aa..c4d05d1d734 100644 --- a/reportbuilder/amd/build/reports_list.min.js.map +++ b/reportbuilder/amd/build/reports_list.min.js.map @@ -1 +1 @@ -{"version":3,"file":"reports_list.min.js","sources":["../src/reports_list.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 * Report builder reports list management\n *\n * @module core_reportbuilder/reports_list\n * @copyright 2021 David Matamoros \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {deleteReport} from 'core_reportbuilder/local/repository/reports';\nimport {createReportModal} from 'core_reportbuilder/local/repository/modals';\n\n/**\n * Initialise module\n */\nexport const init = () => {\n prefetchStrings('core_reportbuilder', [\n 'deletereport',\n 'deletereportconfirm',\n 'editreportdetails',\n 'newreport',\n 'reportdeleted',\n 'reportupdated',\n ]);\n\n prefetchStrings('core', [\n 'delete',\n ]);\n\n document.addEventListener('click', event => {\n const reportCreate = event.target.closest(reportSelectors.actions.reportCreate);\n if (reportCreate) {\n event.preventDefault();\n\n // Redirect user to editing interface for the report after submission.\n const reportModal = createReportModal(event.target, getString('newreport', 'core_reportbuilder'));\n reportModal.addEventListener(reportModal.events.FORM_SUBMITTED, event => {\n window.location.href = event.detail;\n });\n\n reportModal.show();\n }\n\n const reportEdit = event.target.closest(reportSelectors.actions.reportEdit);\n if (reportEdit) {\n event.preventDefault();\n\n // Reload current report page after submission.\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = reportEdit.closest('.dropdown').querySelector('.dropdown-toggle');\n const reportModal = createReportModal(triggerElement, getString('editreportdetails', 'core_reportbuilder'),\n reportEdit.dataset.reportId);\n reportModal.addEventListener(reportModal.events.FORM_SUBMITTED, () => {\n const reportElement = event.target.closest(reportSelectors.regions.report);\n\n getString('reportupdated', 'core_reportbuilder')\n .then(addToast)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return;\n })\n .catch(Notification.exception);\n });\n\n reportModal.show();\n }\n\n const reportDelete = event.target.closest(reportSelectors.actions.reportDelete);\n if (reportDelete) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = reportDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n Notification.saveCancelPromise(\n getString('deletereport', 'core_reportbuilder'),\n getString('deletereportconfirm', 'core_reportbuilder', reportDelete.dataset.reportName),\n getString('delete', 'core'),\n {triggerElement}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/reports:delete');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n\n return deleteReport(reportDelete.dataset.reportId)\n .then(() => addToast(getString('reportdeleted', 'core_reportbuilder')))\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n};\n"],"names":["document","addEventListener","event","target","closest","reportSelectors","actions","reportCreate","preventDefault","reportModal","events","FORM_SUBMITTED","window","location","href","detail","show","reportEdit","triggerElement","querySelector","dataset","reportId","reportElement","regions","report","then","addToast","reportEvents","tableReload","preservePagination","catch","Notification","exception","reportDelete","saveCancelPromise","reportName","pendingPromise","Pending","resolve"],"mappings":"yuDAuCoB,mCACA,qBAAsB,CAClC,eACA,sBACA,oBACA,YACA,gBACA,gDAGY,OAAQ,CACpB,WAGJA,SAASC,iBAAiB,SAASC,WACVA,MAAMC,OAAOC,QAAQC,gBAAgBC,QAAQC,cAChD,CACdL,MAAMM,uBAGAC,aAAc,6BAAkBP,MAAMC,QAAQ,mBAAU,YAAa,uBAC3EM,YAAYR,iBAAiBQ,YAAYC,OAAOC,gBAAgBT,QAC5DU,OAAOC,SAASC,KAAOZ,MAAMa,UAGjCN,YAAYO,aAGVC,WAAaf,MAAMC,OAAOC,QAAQC,gBAAgBC,QAAQW,eAC5DA,WAAY,CACZf,MAAMM,uBAIAU,eAAiBD,WAAWb,QAAQ,aAAae,cAAc,oBAC/DV,aAAc,6BAAkBS,gBAAgB,mBAAU,oBAAqB,sBACjFD,WAAWG,QAAQC,UACvBZ,YAAYR,iBAAiBQ,YAAYC,OAAOC,gBAAgB,WACtDW,cAAgBpB,MAAMC,OAAOC,QAAQC,gBAAgBkB,QAAQC,4BAEzD,gBAAiB,sBACtBC,KAAKC,YACLD,MAAK,yCACYE,aAAaC,YAAa,CAACC,oBAAoB,GAAOP,kBAGvEQ,MAAMC,sBAAaC,cAG5BvB,YAAYO,aAGViB,aAAe/B,MAAMC,OAAOC,QAAQC,gBAAgBC,QAAQ2B,iBAC9DA,aAAc,CACd/B,MAAMM,uBAGAU,eAAiBe,aAAa7B,QAAQ,aAAae,cAAc,0CAC1De,mBACT,mBAAU,eAAgB,uBAC1B,mBAAU,sBAAuB,qBAAsBD,aAAab,QAAQe,aAC5E,mBAAU,SAAU,QACpB,CAACjB,eAAAA,iBACHO,MAAK,WACGW,eAAiB,IAAIC,iBAAQ,qCAC7Bf,cAAgBpB,MAAMC,OAAOC,QAAQC,gBAAgBkB,QAAQC,eAE5D,yBAAaS,aAAab,QAAQC,UACpCI,MAAK,KAAM,eAAS,mBAAU,gBAAiB,yBAC/CA,MAAK,yCACYE,aAAaC,YAAa,CAACC,oBAAoB,GAAOP,eAC7Dc,eAAeE,aAEzBR,MAAMC,sBAAaC,cACzBF,OAAM"} \ No newline at end of file +{"version":3,"file":"reports_list.min.js","sources":["../src/reports_list.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 * Report builder reports list management\n *\n * @module core_reportbuilder/reports_list\n * @copyright 2021 David Matamoros \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {deleteReport} from 'core_reportbuilder/local/repository/reports';\nimport {createReportModal} from 'core_reportbuilder/local/repository/modals';\n\n/**\n * Initialise module\n */\nexport const init = () => {\n prefetchStrings('core_reportbuilder', [\n 'deletereport',\n 'deletereportconfirm',\n 'editreportdetails',\n 'newreport',\n 'reportdeleted',\n 'reportupdated',\n ]);\n\n prefetchStrings('core', [\n 'delete',\n ]);\n\n document.addEventListener('click', event => {\n const reportCreate = event.target.closest(reportSelectors.actions.reportCreate);\n if (reportCreate) {\n event.preventDefault();\n\n // Redirect user to editing interface for the report after submission.\n const reportModal = createReportModal(event.target, getString('newreport', 'core_reportbuilder'));\n reportModal.addEventListener(reportModal.events.FORM_SUBMITTED, event => {\n window.location.href = event.detail;\n });\n\n reportModal.show();\n }\n\n const reportEdit = event.target.closest(reportSelectors.actions.reportEdit);\n if (reportEdit) {\n event.preventDefault();\n\n // Reload current report page after submission.\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = reportEdit.closest('.dropdown').querySelector('.dropdown-toggle');\n const reportModal = createReportModal(triggerElement, getString('editreportdetails', 'core_reportbuilder'),\n reportEdit.dataset.reportId);\n reportModal.addEventListener(reportModal.events.FORM_SUBMITTED, () => {\n const reportElement = event.target.closest(reportSelectors.regions.report);\n\n getString('reportupdated', 'core_reportbuilder')\n .then(addToast)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return;\n })\n .catch(Notification.exception);\n });\n\n reportModal.show();\n }\n\n const reportDelete = event.target.closest(reportSelectors.actions.reportDelete);\n if (reportDelete) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = reportDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n Notification.saveCancelPromise(\n getString('deletereport', 'core_reportbuilder'),\n getString('deletereportconfirm', 'core_reportbuilder', reportDelete.dataset.reportName),\n getString('delete', 'core'),\n {triggerElement}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/reports:delete');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n\n return deleteReport(reportDelete.dataset.reportId)\n .then(() => addToast(getString('reportdeleted', 'core_reportbuilder')))\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n};\n"],"names":["document","addEventListener","event","target","closest","reportSelectors","actions","reportCreate","preventDefault","reportModal","events","FORM_SUBMITTED","window","location","href","detail","show","reportEdit","triggerElement","querySelector","dataset","reportId","reportElement","regions","report","then","addToast","reportEvents","tableReload","preservePagination","catch","Notification","exception","reportDelete","saveCancelPromise","reportName","pendingPromise","Pending","resolve"],"mappings":"yuDAuCoB,mCACA,qBAAsB,CAClC,eACA,sBACA,oBACA,YACA,gBACA,gDAGY,OAAQ,CACpB,WAGJA,SAASC,iBAAiB,SAASC,WACVA,MAAMC,OAAOC,QAAQC,gBAAgBC,QAAQC,cAChD,CACdL,MAAMM,uBAGAC,aAAc,6BAAkBP,MAAMC,QAAQ,kBAAU,YAAa,uBAC3EM,YAAYR,iBAAiBQ,YAAYC,OAAOC,gBAAgBT,QAC5DU,OAAOC,SAASC,KAAOZ,MAAMa,UAGjCN,YAAYO,aAGVC,WAAaf,MAAMC,OAAOC,QAAQC,gBAAgBC,QAAQW,eAC5DA,WAAY,CACZf,MAAMM,uBAIAU,eAAiBD,WAAWb,QAAQ,aAAae,cAAc,oBAC/DV,aAAc,6BAAkBS,gBAAgB,kBAAU,oBAAqB,sBACjFD,WAAWG,QAAQC,UACvBZ,YAAYR,iBAAiBQ,YAAYC,OAAOC,gBAAgB,WACtDW,cAAgBpB,MAAMC,OAAOC,QAAQC,gBAAgBkB,QAAQC,2BAEzD,gBAAiB,sBACtBC,KAAKC,YACLD,MAAK,yCACYE,aAAaC,YAAa,CAACC,oBAAoB,GAAOP,kBAGvEQ,MAAMC,sBAAaC,cAG5BvB,YAAYO,aAGViB,aAAe/B,MAAMC,OAAOC,QAAQC,gBAAgBC,QAAQ2B,iBAC9DA,aAAc,CACd/B,MAAMM,uBAGAU,eAAiBe,aAAa7B,QAAQ,aAAae,cAAc,0CAC1De,mBACT,kBAAU,eAAgB,uBAC1B,kBAAU,sBAAuB,qBAAsBD,aAAab,QAAQe,aAC5E,kBAAU,SAAU,QACpB,CAACjB,eAAAA,iBACHO,MAAK,WACGW,eAAiB,IAAIC,iBAAQ,qCAC7Bf,cAAgBpB,MAAMC,OAAOC,QAAQC,gBAAgBkB,QAAQC,eAE5D,yBAAaS,aAAab,QAAQC,UACpCI,MAAK,KAAM,eAAS,kBAAU,gBAAiB,yBAC/CA,MAAK,yCACYE,aAAaC,YAAa,CAACC,oBAAoB,GAAOP,eAC7Dc,eAAeE,aAEzBR,MAAMC,sBAAaC,cACzBF,OAAM"} \ No newline at end of file diff --git a/reportbuilder/amd/build/schedules.min.js b/reportbuilder/amd/build/schedules.min.js index 94cd3741ad2..73bbf3a06ea 100644 --- a/reportbuilder/amd/build/schedules.min.js +++ b/reportbuilder/amd/build/schedules.min.js @@ -1,3 +1,3 @@ -define("core_reportbuilder/schedules",["exports","core/event_dispatcher","core/inplace_editable","core/notification","core/pending","core/prefetch","core/str","core/toast","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/modals","core_reportbuilder/local/repository/schedules"],(function(_exports,_event_dispatcher,_inplace_editable,_notification,_pending,_prefetch,_str,_toast,reportEvents,reportSelectors,_modals,_schedules){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);let initialized=!1;_exports.init=reportId=>{(0,_prefetch.prefetchStrings)("core_reportbuilder",["deleteschedule","deletescheduleconfirm","disableschedule","editscheduledetails","enableschedule","newschedule","schedulecreated","scheduledeleted","schedulesent","scheduleupdated","sendschedule","sendscheduleconfirm"]),(0,_prefetch.prefetchStrings)("core",["confirm","delete"]),initialized||(document.addEventListener("click",(event=>{if(event.target.closest(reportSelectors.actions.scheduleCreate)){event.preventDefault();const scheduleModal=(0,_modals.createScheduleModal)(event.target,(0,_str.get_string)("newschedule","core_reportbuilder"),reportId);scheduleModal.addEventListener(scheduleModal.events.FORM_SUBMITTED,(()=>{(0,_str.get_string)("schedulecreated","core_reportbuilder").then(_toast.add).then((()=>{const reportElement=document.querySelector(reportSelectors.regions.report);(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement)})).catch(_notification.default.exception)})),scheduleModal.show()}const scheduleToggle=event.target.closest(reportSelectors.actions.scheduleToggle);if(scheduleToggle){const pendingPromise=new _pending.default("core_reportbuilder/schedules:toggle"),scheduleStateToggle=+!Number(scheduleToggle.dataset.state);(0,_schedules.toggleSchedule)(reportId,scheduleToggle.dataset.id,scheduleStateToggle).then((()=>{scheduleToggle.closest("tr").classList.toggle("text-muted"),scheduleToggle.dataset.state=scheduleStateToggle;const stringKey=scheduleStateToggle?"disableschedule":"enableschedule";return(0,_str.get_string)(stringKey,"core_reportbuilder")})).then((toggleLabel=>(scheduleToggle.parentElement.querySelector('label[for="'.concat(scheduleToggle.id,'"] > span')).innerHTML=toggleLabel,pendingPromise.resolve()))).catch(_notification.default.exception)}const scheduleEdit=event.target.closest(reportSelectors.actions.scheduleEdit);if(scheduleEdit){event.preventDefault();const triggerElement=scheduleEdit.closest(".dropdown").querySelector(".dropdown-toggle"),scheduleModal=(0,_modals.createScheduleModal)(triggerElement,(0,_str.get_string)("editscheduledetails","core_reportbuilder"),reportId,scheduleEdit.dataset.scheduleId);scheduleModal.addEventListener(scheduleModal.events.FORM_SUBMITTED,(()=>{(0,_str.get_string)("scheduleupdated","core_reportbuilder").then(_toast.add).then((()=>{const reportElement=scheduleEdit.closest(reportSelectors.regions.report);(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement)})).catch(_notification.default.exception)})),scheduleModal.show()}const scheduleSend=event.target.closest(reportSelectors.actions.scheduleSend);if(scheduleSend){event.preventDefault();const triggerElement=scheduleSend.closest(".dropdown").querySelector(".dropdown-toggle");_notification.default.saveCancelPromise((0,_str.get_string)("sendschedule","core_reportbuilder"),(0,_str.get_string)("sendscheduleconfirm","core_reportbuilder",scheduleSend.dataset.scheduleName),(0,_str.get_string)("confirm","core"),{triggerElement:triggerElement}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/schedules:send");return(0,_schedules.sendSchedule)(reportId,scheduleSend.dataset.scheduleId).then((0,_toast.add)((0,_str.get_string)("schedulesent","core_reportbuilder"))).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)})).catch((()=>{}))}const scheduleDelete=event.target.closest(reportSelectors.actions.scheduleDelete);if(scheduleDelete){event.preventDefault();const triggerElement=scheduleDelete.closest(".dropdown").querySelector(".dropdown-toggle");_notification.default.saveCancelPromise((0,_str.get_string)("deleteschedule","core_reportbuilder"),(0,_str.get_string)("deletescheduleconfirm","core_reportbuilder",scheduleDelete.dataset.scheduleName),(0,_str.get_string)("delete","core"),{triggerElement:triggerElement}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/schedules:delete");return(0,_schedules.deleteSchedule)(reportId,scheduleDelete.dataset.scheduleId).then((0,_toast.add)((0,_str.get_string)("scheduledeleted","core_reportbuilder"))).then((()=>{const reportElement=scheduleDelete.closest(reportSelectors.regions.report);return(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()})).catch(_notification.default.exception)})).catch((()=>{}))}})),initialized=!0)}})); +define("core_reportbuilder/schedules",["exports","core/event_dispatcher","core/inplace_editable","core/notification","core/pending","core/prefetch","core/str","core/toast","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/modals","core_reportbuilder/local/repository/schedules"],(function(_exports,_event_dispatcher,_inplace_editable,_notification,_pending,_prefetch,_str,_toast,reportEvents,reportSelectors,_modals,_schedules){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);let initialized=!1;_exports.init=reportId=>{(0,_prefetch.prefetchStrings)("core_reportbuilder",["deleteschedule","deletescheduleconfirm","disableschedule","editscheduledetails","enableschedule","newschedule","schedulecreated","scheduledeleted","schedulesent","scheduleupdated","sendschedule","sendscheduleconfirm"]),(0,_prefetch.prefetchStrings)("core",["confirm","delete"]),initialized||(document.addEventListener("click",(event=>{if(event.target.closest(reportSelectors.actions.scheduleCreate)){event.preventDefault();const scheduleModal=(0,_modals.createScheduleModal)(event.target,(0,_str.getString)("newschedule","core_reportbuilder"),reportId);scheduleModal.addEventListener(scheduleModal.events.FORM_SUBMITTED,(()=>{(0,_str.getString)("schedulecreated","core_reportbuilder").then(_toast.add).then((()=>{const reportElement=document.querySelector(reportSelectors.regions.report);(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement)})).catch(_notification.default.exception)})),scheduleModal.show()}const scheduleToggle=event.target.closest(reportSelectors.actions.scheduleToggle);if(scheduleToggle){const pendingPromise=new _pending.default("core_reportbuilder/schedules:toggle"),scheduleStateToggle=+!Number(scheduleToggle.dataset.state);(0,_schedules.toggleSchedule)(reportId,scheduleToggle.dataset.id,scheduleStateToggle).then((()=>{scheduleToggle.closest("tr").classList.toggle("text-muted"),scheduleToggle.dataset.state=scheduleStateToggle;const stringKey=scheduleStateToggle?"disableschedule":"enableschedule";return(0,_str.getString)(stringKey,"core_reportbuilder")})).then((toggleLabel=>(scheduleToggle.parentElement.querySelector('label[for="'.concat(scheduleToggle.id,'"] > span')).innerHTML=toggleLabel,pendingPromise.resolve()))).catch(_notification.default.exception)}const scheduleEdit=event.target.closest(reportSelectors.actions.scheduleEdit);if(scheduleEdit){event.preventDefault();const triggerElement=scheduleEdit.closest(".dropdown").querySelector(".dropdown-toggle"),scheduleModal=(0,_modals.createScheduleModal)(triggerElement,(0,_str.getString)("editscheduledetails","core_reportbuilder"),reportId,scheduleEdit.dataset.scheduleId);scheduleModal.addEventListener(scheduleModal.events.FORM_SUBMITTED,(()=>{(0,_str.getString)("scheduleupdated","core_reportbuilder").then(_toast.add).then((()=>{const reportElement=scheduleEdit.closest(reportSelectors.regions.report);(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{},reportElement)})).catch(_notification.default.exception)})),scheduleModal.show()}const scheduleSend=event.target.closest(reportSelectors.actions.scheduleSend);if(scheduleSend){event.preventDefault();const triggerElement=scheduleSend.closest(".dropdown").querySelector(".dropdown-toggle");_notification.default.saveCancelPromise((0,_str.getString)("sendschedule","core_reportbuilder"),(0,_str.getString)("sendscheduleconfirm","core_reportbuilder",scheduleSend.dataset.scheduleName),(0,_str.getString)("confirm","core"),{triggerElement:triggerElement}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/schedules:send");return(0,_schedules.sendSchedule)(reportId,scheduleSend.dataset.scheduleId).then((0,_toast.add)((0,_str.getString)("schedulesent","core_reportbuilder"))).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)})).catch((()=>{}))}const scheduleDelete=event.target.closest(reportSelectors.actions.scheduleDelete);if(scheduleDelete){event.preventDefault();const triggerElement=scheduleDelete.closest(".dropdown").querySelector(".dropdown-toggle");_notification.default.saveCancelPromise((0,_str.getString)("deleteschedule","core_reportbuilder"),(0,_str.getString)("deletescheduleconfirm","core_reportbuilder",scheduleDelete.dataset.scheduleName),(0,_str.getString)("delete","core"),{triggerElement:triggerElement}).then((()=>{const pendingPromise=new _pending.default("core_reportbuilder/schedules:delete");return(0,_schedules.deleteSchedule)(reportId,scheduleDelete.dataset.scheduleId).then((0,_toast.add)((0,_str.getString)("scheduledeleted","core_reportbuilder"))).then((()=>{const reportElement=scheduleDelete.closest(reportSelectors.regions.report);return(0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()})).catch(_notification.default.exception)})).catch((()=>{}))}})),initialized=!0)}})); //# sourceMappingURL=schedules.min.js.map \ No newline at end of file diff --git a/reportbuilder/amd/build/schedules.min.js.map b/reportbuilder/amd/build/schedules.min.js.map index 0fe6728560a..a8b823a912a 100644 --- a/reportbuilder/amd/build/schedules.min.js.map +++ b/reportbuilder/amd/build/schedules.min.js.map @@ -1 +1 @@ -{"version":3,"file":"schedules.min.js","sources":["../src/schedules.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 * Report builder audiences\n *\n * @module core_reportbuilder/schedules\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport 'core/inplace_editable';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {createScheduleModal} from 'core_reportbuilder/local/repository/modals';\nimport {deleteSchedule, sendSchedule, toggleSchedule} from 'core_reportbuilder/local/repository/schedules';\n\nlet initialized = false;\n\n/**\n * Initialise schedules tab\n *\n * @param {Number} reportId\n */\nexport const init = reportId => {\n prefetchStrings('core_reportbuilder', [\n 'deleteschedule',\n 'deletescheduleconfirm',\n 'disableschedule',\n 'editscheduledetails',\n 'enableschedule',\n 'newschedule',\n 'schedulecreated',\n 'scheduledeleted',\n 'schedulesent',\n 'scheduleupdated',\n 'sendschedule',\n 'sendscheduleconfirm',\n ]);\n\n prefetchStrings('core', [\n 'confirm',\n 'delete',\n ]);\n\n if (initialized) {\n // We already added the event listeners (can be called multiple times by mustache template).\n return;\n }\n\n document.addEventListener('click', event => {\n\n // Create schedule.\n const scheduleCreate = event.target.closest(reportSelectors.actions.scheduleCreate);\n if (scheduleCreate) {\n event.preventDefault();\n\n const scheduleModal = createScheduleModal(event.target, getString('newschedule', 'core_reportbuilder'), reportId);\n scheduleModal.addEventListener(scheduleModal.events.FORM_SUBMITTED, () => {\n getString('schedulecreated', 'core_reportbuilder')\n .then(addToast)\n .then(() => {\n const reportElement = document.querySelector(reportSelectors.regions.report);\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return;\n })\n .catch(Notification.exception);\n });\n\n scheduleModal.show();\n }\n\n // Toggle schedule.\n const scheduleToggle = event.target.closest(reportSelectors.actions.scheduleToggle);\n if (scheduleToggle) {\n const pendingPromise = new Pending('core_reportbuilder/schedules:toggle');\n const scheduleStateToggle = +!Number(scheduleToggle.dataset.state);\n\n toggleSchedule(reportId, scheduleToggle.dataset.id, scheduleStateToggle)\n .then(() => {\n const tableRow = scheduleToggle.closest('tr');\n tableRow.classList.toggle('text-muted');\n\n scheduleToggle.dataset.state = scheduleStateToggle;\n\n const stringKey = scheduleStateToggle ? 'disableschedule' : 'enableschedule';\n return getString(stringKey, 'core_reportbuilder');\n })\n .then(toggleLabel => {\n const labelContainer = scheduleToggle.parentElement.querySelector(`label[for=\"${scheduleToggle.id}\"] > span`);\n labelContainer.innerHTML = toggleLabel;\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n\n // Edit schedule.\n const scheduleEdit = event.target.closest(reportSelectors.actions.scheduleEdit);\n if (scheduleEdit) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = scheduleEdit.closest('.dropdown').querySelector('.dropdown-toggle');\n const scheduleModal = createScheduleModal(triggerElement, getString('editscheduledetails', 'core_reportbuilder'),\n reportId, scheduleEdit.dataset.scheduleId);\n scheduleModal.addEventListener(scheduleModal.events.FORM_SUBMITTED, () => {\n getString('scheduleupdated', 'core_reportbuilder')\n .then(addToast)\n .then(() => {\n const reportElement = scheduleEdit.closest(reportSelectors.regions.report);\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return;\n })\n .catch(Notification.exception);\n });\n\n scheduleModal.show();\n }\n\n // Send schedule.\n const scheduleSend = event.target.closest(reportSelectors.actions.scheduleSend);\n if (scheduleSend) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = scheduleSend.closest('.dropdown').querySelector('.dropdown-toggle');\n Notification.saveCancelPromise(\n getString('sendschedule', 'core_reportbuilder'),\n getString('sendscheduleconfirm', 'core_reportbuilder', scheduleSend.dataset.scheduleName),\n getString('confirm', 'core'),\n {triggerElement}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/schedules:send');\n\n return sendSchedule(reportId, scheduleSend.dataset.scheduleId)\n .then(addToast(getString('schedulesent', 'core_reportbuilder')))\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n\n // Delete schedule.\n const scheduleDelete = event.target.closest(reportSelectors.actions.scheduleDelete);\n if (scheduleDelete) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = scheduleDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n Notification.saveCancelPromise(\n getString('deleteschedule', 'core_reportbuilder'),\n getString('deletescheduleconfirm', 'core_reportbuilder', scheduleDelete.dataset.scheduleName),\n getString('delete', 'core'),\n {triggerElement}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/schedules:delete');\n\n return deleteSchedule(reportId, scheduleDelete.dataset.scheduleId)\n .then(addToast(getString('scheduledeleted', 'core_reportbuilder')))\n .then(() => {\n const reportElement = scheduleDelete.closest(reportSelectors.regions.report);\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n\n initialized = true;\n};\n"],"names":["initialized","reportId","document","addEventListener","event","target","closest","reportSelectors","actions","scheduleCreate","preventDefault","scheduleModal","events","FORM_SUBMITTED","then","addToast","reportElement","querySelector","regions","report","reportEvents","tableReload","catch","Notification","exception","show","scheduleToggle","pendingPromise","Pending","scheduleStateToggle","Number","dataset","state","id","classList","toggle","stringKey","toggleLabel","parentElement","innerHTML","resolve","scheduleEdit","triggerElement","scheduleId","scheduleSend","saveCancelPromise","scheduleName","scheduleDelete","preservePagination"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"schedules.min.js","sources":["../src/schedules.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 * Report builder audiences\n *\n * @module core_reportbuilder/schedules\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport 'core/inplace_editable';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {createScheduleModal} from 'core_reportbuilder/local/repository/modals';\nimport {deleteSchedule, sendSchedule, toggleSchedule} from 'core_reportbuilder/local/repository/schedules';\n\nlet initialized = false;\n\n/**\n * Initialise schedules tab\n *\n * @param {Number} reportId\n */\nexport const init = reportId => {\n prefetchStrings('core_reportbuilder', [\n 'deleteschedule',\n 'deletescheduleconfirm',\n 'disableschedule',\n 'editscheduledetails',\n 'enableschedule',\n 'newschedule',\n 'schedulecreated',\n 'scheduledeleted',\n 'schedulesent',\n 'scheduleupdated',\n 'sendschedule',\n 'sendscheduleconfirm',\n ]);\n\n prefetchStrings('core', [\n 'confirm',\n 'delete',\n ]);\n\n if (initialized) {\n // We already added the event listeners (can be called multiple times by mustache template).\n return;\n }\n\n document.addEventListener('click', event => {\n\n // Create schedule.\n const scheduleCreate = event.target.closest(reportSelectors.actions.scheduleCreate);\n if (scheduleCreate) {\n event.preventDefault();\n\n const scheduleModal = createScheduleModal(event.target, getString('newschedule', 'core_reportbuilder'), reportId);\n scheduleModal.addEventListener(scheduleModal.events.FORM_SUBMITTED, () => {\n getString('schedulecreated', 'core_reportbuilder')\n .then(addToast)\n .then(() => {\n const reportElement = document.querySelector(reportSelectors.regions.report);\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return;\n })\n .catch(Notification.exception);\n });\n\n scheduleModal.show();\n }\n\n // Toggle schedule.\n const scheduleToggle = event.target.closest(reportSelectors.actions.scheduleToggle);\n if (scheduleToggle) {\n const pendingPromise = new Pending('core_reportbuilder/schedules:toggle');\n const scheduleStateToggle = +!Number(scheduleToggle.dataset.state);\n\n toggleSchedule(reportId, scheduleToggle.dataset.id, scheduleStateToggle)\n .then(() => {\n const tableRow = scheduleToggle.closest('tr');\n tableRow.classList.toggle('text-muted');\n\n scheduleToggle.dataset.state = scheduleStateToggle;\n\n const stringKey = scheduleStateToggle ? 'disableschedule' : 'enableschedule';\n return getString(stringKey, 'core_reportbuilder');\n })\n .then(toggleLabel => {\n const labelContainer = scheduleToggle.parentElement.querySelector(`label[for=\"${scheduleToggle.id}\"] > span`);\n labelContainer.innerHTML = toggleLabel;\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n\n // Edit schedule.\n const scheduleEdit = event.target.closest(reportSelectors.actions.scheduleEdit);\n if (scheduleEdit) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = scheduleEdit.closest('.dropdown').querySelector('.dropdown-toggle');\n const scheduleModal = createScheduleModal(triggerElement, getString('editscheduledetails', 'core_reportbuilder'),\n reportId, scheduleEdit.dataset.scheduleId);\n scheduleModal.addEventListener(scheduleModal.events.FORM_SUBMITTED, () => {\n getString('scheduleupdated', 'core_reportbuilder')\n .then(addToast)\n .then(() => {\n const reportElement = scheduleEdit.closest(reportSelectors.regions.report);\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n return;\n })\n .catch(Notification.exception);\n });\n\n scheduleModal.show();\n }\n\n // Send schedule.\n const scheduleSend = event.target.closest(reportSelectors.actions.scheduleSend);\n if (scheduleSend) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = scheduleSend.closest('.dropdown').querySelector('.dropdown-toggle');\n Notification.saveCancelPromise(\n getString('sendschedule', 'core_reportbuilder'),\n getString('sendscheduleconfirm', 'core_reportbuilder', scheduleSend.dataset.scheduleName),\n getString('confirm', 'core'),\n {triggerElement}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/schedules:send');\n\n return sendSchedule(reportId, scheduleSend.dataset.scheduleId)\n .then(addToast(getString('schedulesent', 'core_reportbuilder')))\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n\n // Delete schedule.\n const scheduleDelete = event.target.closest(reportSelectors.actions.scheduleDelete);\n if (scheduleDelete) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = scheduleDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n Notification.saveCancelPromise(\n getString('deleteschedule', 'core_reportbuilder'),\n getString('deletescheduleconfirm', 'core_reportbuilder', scheduleDelete.dataset.scheduleName),\n getString('delete', 'core'),\n {triggerElement}\n ).then(() => {\n const pendingPromise = new Pending('core_reportbuilder/schedules:delete');\n\n return deleteSchedule(reportId, scheduleDelete.dataset.scheduleId)\n .then(addToast(getString('scheduledeleted', 'core_reportbuilder')))\n .then(() => {\n const reportElement = scheduleDelete.closest(reportSelectors.regions.report);\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n\n initialized = true;\n};\n"],"names":["initialized","reportId","document","addEventListener","event","target","closest","reportSelectors","actions","scheduleCreate","preventDefault","scheduleModal","events","FORM_SUBMITTED","then","addToast","reportElement","querySelector","regions","report","reportEvents","tableReload","catch","Notification","exception","show","scheduleToggle","pendingPromise","Pending","scheduleStateToggle","Number","dataset","state","id","classList","toggle","stringKey","toggleLabel","parentElement","innerHTML","resolve","scheduleEdit","triggerElement","scheduleId","scheduleSend","saveCancelPromise","scheduleName","scheduleDelete","preservePagination"],"mappings":"0wDAqCIA,aAAc,gBAOEC,yCACA,qBAAsB,CAClC,iBACA,wBACA,kBACA,sBACA,iBACA,cACA,kBACA,kBACA,eACA,kBACA,eACA,sDAGY,OAAQ,CACpB,UACA,WAGAD,cAKJE,SAASC,iBAAiB,SAASC,WAGRA,MAAMC,OAAOC,QAAQC,gBAAgBC,QAAQC,gBAChD,CAChBL,MAAMM,uBAEAC,eAAgB,+BAAoBP,MAAMC,QAAQ,kBAAU,cAAe,sBAAuBJ,UACxGU,cAAcR,iBAAiBQ,cAAcC,OAAOC,gBAAgB,wBACtD,kBAAmB,sBACxBC,KAAKC,YACLD,MAAK,WACIE,cAAgBd,SAASe,cAAcV,gBAAgBW,QAAQC,4CACvDC,aAAaC,YAAa,GAAIL,kBAG/CM,MAAMC,sBAAaC,cAG5Bb,cAAcc,aAIZC,eAAiBtB,MAAMC,OAAOC,QAAQC,gBAAgBC,QAAQkB,mBAChEA,eAAgB,OACVC,eAAiB,IAAIC,iBAAQ,uCAC7BC,sBAAwBC,OAAOJ,eAAeK,QAAQC,qCAE7C/B,SAAUyB,eAAeK,QAAQE,GAAIJ,qBAC/Cf,MAAK,KACeY,eAAepB,QAAQ,MAC/B4B,UAAUC,OAAO,cAE1BT,eAAeK,QAAQC,MAAQH,0BAEzBO,UAAYP,oBAAsB,kBAAoB,wBACrD,kBAAUO,UAAW,yBAE/BtB,MAAKuB,cACqBX,eAAeY,cAAcrB,mCAA4BS,eAAeO,iBAChFM,UAAYF,YACpBV,eAAea,aAEzBlB,MAAMC,sBAAaC,iBAItBiB,aAAerC,MAAMC,OAAOC,QAAQC,gBAAgBC,QAAQiC,iBAC9DA,aAAc,CACdrC,MAAMM,uBAGAgC,eAAiBD,aAAanC,QAAQ,aAAaW,cAAc,oBACjEN,eAAgB,+BAAoB+B,gBAAgB,kBAAU,sBAAuB,sBACvFzC,SAAUwC,aAAaV,QAAQY,YACnChC,cAAcR,iBAAiBQ,cAAcC,OAAOC,gBAAgB,wBACtD,kBAAmB,sBACxBC,KAAKC,YACLD,MAAK,WACIE,cAAgByB,aAAanC,QAAQC,gBAAgBW,QAAQC,4CACrDC,aAAaC,YAAa,GAAIL,kBAG/CM,MAAMC,sBAAaC,cAG5Bb,cAAcc,aAIZmB,aAAexC,MAAMC,OAAOC,QAAQC,gBAAgBC,QAAQoC,iBAC9DA,aAAc,CACdxC,MAAMM,uBAGAgC,eAAiBE,aAAatC,QAAQ,aAAaW,cAAc,0CAC1D4B,mBACT,kBAAU,eAAgB,uBAC1B,kBAAU,sBAAuB,qBAAsBD,aAAab,QAAQe,eAC5E,kBAAU,UAAW,QACrB,CAACJ,eAAAA,iBACH5B,MAAK,WACGa,eAAiB,IAAIC,iBAAQ,4CAE5B,2BAAa3B,SAAU2C,aAAab,QAAQY,YAC9C7B,MAAK,eAAS,kBAAU,eAAgB,wBACxCA,MAAK,IAAMa,eAAea,YAC1BlB,MAAMC,sBAAaC,cACzBF,OAAM,eAMPyB,eAAiB3C,MAAMC,OAAOC,QAAQC,gBAAgBC,QAAQuC,mBAChEA,eAAgB,CAChB3C,MAAMM,uBAGAgC,eAAiBK,eAAezC,QAAQ,aAAaW,cAAc,0CAC5D4B,mBACT,kBAAU,iBAAkB,uBAC5B,kBAAU,wBAAyB,qBAAsBE,eAAehB,QAAQe,eAChF,kBAAU,SAAU,QACpB,CAACJ,eAAAA,iBACH5B,MAAK,WACGa,eAAiB,IAAIC,iBAAQ,8CAE5B,6BAAe3B,SAAU8C,eAAehB,QAAQY,YAClD7B,MAAK,eAAS,kBAAU,kBAAmB,wBAC3CA,MAAK,WACIE,cAAgB+B,eAAezC,QAAQC,gBAAgBW,QAAQC,kDACvDC,aAAaC,YAAa,CAAC2B,oBAAoB,GAAOhC,eAC7DW,eAAea,aAEzBlB,MAAMC,sBAAaC,cACzBF,OAAM,aAMjBtB,aAAc"} \ No newline at end of file diff --git a/reportbuilder/amd/src/audience.js b/reportbuilder/amd/src/audience.js index 5b7a54e0e34..4b3817e9e96 100644 --- a/reportbuilder/amd/src/audience.js +++ b/reportbuilder/amd/src/audience.js @@ -28,7 +28,7 @@ import Templates from 'core/templates'; import Notification from 'core/notification'; import Pending from 'core/pending'; import {prefetchStrings} from 'core/prefetch'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import DynamicForm from 'core_form/dynamicform'; import {add as addToast} from 'core/toast'; import {deleteAudience} from 'core_reportbuilder/local/repository/audiences'; diff --git a/reportbuilder/amd/src/editor.js b/reportbuilder/amd/src/editor.js index 029cd196a9c..a06aebb34ee 100644 --- a/reportbuilder/amd/src/editor.js +++ b/reportbuilder/amd/src/editor.js @@ -29,7 +29,7 @@ import {addIconToContainer} from 'core/loadingicon'; import Notification from 'core/notification'; import Pending from 'core/pending'; import Templates from 'core/templates'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {add as addToast} from 'core/toast'; import * as reportSelectors from 'core_reportbuilder/local/selectors'; import {init as columnsEditorInit} from 'core_reportbuilder/local/editor/columns'; diff --git a/reportbuilder/amd/src/filters.js b/reportbuilder/amd/src/filters.js index 39394d11ccd..1ef767bcfa1 100644 --- a/reportbuilder/amd/src/filters.js +++ b/reportbuilder/amd/src/filters.js @@ -25,7 +25,7 @@ import {dispatchEvent} from 'core/event_dispatcher'; import {loadFragment} from 'core/fragment'; import Notification from 'core/notification'; import Pending from 'core/pending'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Templates from 'core/templates'; import {add as addToast} from 'core/toast'; import DynamicForm from 'core_form/dynamicform'; diff --git a/reportbuilder/amd/src/local/editor/card_view.js b/reportbuilder/amd/src/local/editor/card_view.js index 19ebf9c047f..ed256bf6e8f 100644 --- a/reportbuilder/amd/src/local/editor/card_view.js +++ b/reportbuilder/amd/src/local/editor/card_view.js @@ -25,7 +25,7 @@ import DynamicForm from 'core_form/dynamicform'; import {add as addToast} from 'core/toast'; -import {get_string as getString} from "core/str"; +import {getString} from "core/str"; import {subscribe as subscribe} from 'core/pubsub'; import Notification from 'core/notification'; import * as reportEvents from 'core_reportbuilder/local/events'; diff --git a/reportbuilder/amd/src/local/editor/columns.js b/reportbuilder/amd/src/local/editor/columns.js index 3a812b637f3..4a7b9ead0c0 100644 --- a/reportbuilder/amd/src/local/editor/columns.js +++ b/reportbuilder/amd/src/local/editor/columns.js @@ -32,7 +32,7 @@ import Pending from 'core/pending'; import {prefetchStrings} from 'core/prefetch'; import {publish} from 'core/pubsub'; import SortableList from 'core/sortable_list'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {add as addToast} from 'core/toast'; import * as reportEvents from 'core_reportbuilder/local/events'; import * as reportSelectors from 'core_reportbuilder/local/selectors'; diff --git a/reportbuilder/amd/src/local/editor/conditions.js b/reportbuilder/amd/src/local/editor/conditions.js index c6efbbbcd87..6f5bbe75c28 100644 --- a/reportbuilder/amd/src/local/editor/conditions.js +++ b/reportbuilder/amd/src/local/editor/conditions.js @@ -31,7 +31,7 @@ import Notification from 'core/notification'; import Pending from 'core/pending'; import {prefetchStrings} from 'core/prefetch'; import SortableList from 'core/sortable_list'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Templates from 'core/templates'; import {add as addToast} from 'core/toast'; import DynamicForm from 'core_form/dynamicform'; diff --git a/reportbuilder/amd/src/local/editor/filters.js b/reportbuilder/amd/src/local/editor/filters.js index e434693ed43..8b29c9873b0 100644 --- a/reportbuilder/amd/src/local/editor/filters.js +++ b/reportbuilder/amd/src/local/editor/filters.js @@ -30,7 +30,7 @@ import Notification from 'core/notification'; import Pending from 'core/pending'; import {prefetchStrings} from 'core/prefetch'; import SortableList from 'core/sortable_list'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import Templates from 'core/templates'; import {add as addToast} from 'core/toast'; import * as reportSelectors from 'core_reportbuilder/local/selectors'; diff --git a/reportbuilder/amd/src/local/editor/sorting.js b/reportbuilder/amd/src/local/editor/sorting.js index 906a817b211..e22f2900e7f 100644 --- a/reportbuilder/amd/src/local/editor/sorting.js +++ b/reportbuilder/amd/src/local/editor/sorting.js @@ -29,7 +29,7 @@ import Notification from 'core/notification'; import Pending from 'core/pending'; import {subscribe} from 'core/pubsub'; import SortableList from 'core/sortable_list'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {add as addToast} from 'core/toast'; import * as reportSelectors from 'core_reportbuilder/local/selectors'; import {reorderColumnSorting, toggleColumnSorting} from 'core_reportbuilder/local/repository/sorting'; diff --git a/reportbuilder/amd/src/local/repository/modals.js b/reportbuilder/amd/src/local/repository/modals.js index a5e0b2c3d0d..28867f27112 100644 --- a/reportbuilder/amd/src/local/repository/modals.js +++ b/reportbuilder/amd/src/local/repository/modals.js @@ -22,7 +22,7 @@ */ import ModalForm from 'core_form/modalform'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; /** * Return modal instance diff --git a/reportbuilder/amd/src/reports_list.js b/reportbuilder/amd/src/reports_list.js index 65efe42133a..20fad82c6cb 100644 --- a/reportbuilder/amd/src/reports_list.js +++ b/reportbuilder/amd/src/reports_list.js @@ -27,7 +27,7 @@ import {dispatchEvent} from 'core/event_dispatcher'; import Notification from 'core/notification'; import Pending from 'core/pending'; import {prefetchStrings} from 'core/prefetch'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {add as addToast} from 'core/toast'; import * as reportEvents from 'core_reportbuilder/local/events'; import * as reportSelectors from 'core_reportbuilder/local/selectors'; diff --git a/reportbuilder/amd/src/schedules.js b/reportbuilder/amd/src/schedules.js index 28815d85999..2ff77ecc811 100644 --- a/reportbuilder/amd/src/schedules.js +++ b/reportbuilder/amd/src/schedules.js @@ -28,7 +28,7 @@ import 'core/inplace_editable'; import Notification from 'core/notification'; import Pending from 'core/pending'; import {prefetchStrings} from 'core/prefetch'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {add as addToast} from 'core/toast'; import * as reportEvents from 'core_reportbuilder/local/events'; import * as reportSelectors from 'core_reportbuilder/local/selectors'; diff --git a/user/amd/build/comboboxsearch/user.min.js b/user/amd/build/comboboxsearch/user.min.js index 8866f4732f2..73003463c92 100644 --- a/user/amd/build/comboboxsearch/user.min.js +++ b/user/amd/build/comboboxsearch/user.min.js @@ -1,3 +1,3 @@ -define("core_user/comboboxsearch/user",["exports","core/comboboxsearch/search_combobox","core/str","core/templates","jquery","core/notification"],(function(_exports,_search_combobox,_str,_templates,_jquery,_notification){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_search_combobox=_interopRequireDefault(_search_combobox),_jquery=_interopRequireDefault(_jquery),_notification=_interopRequireDefault(_notification);class UserSearch extends _search_combobox.default{constructor(){var _document$querySelect,_document$querySelect2;super(),_defineProperty(this,"courseID",void 0),_defineProperty(this,"groupID",void 0),_defineProperty(this,"bannedFilterFields",["profileimageurlsmall","profileimageurl","id","link","matchingField","matchingFieldName"]),_defineProperty(this,"profilestringmap",null),document.addEventListener("click",(e=>{!e.target.closest(this.selectors.component)&&this.searchDropdown.classList.contains("show")&&this.toggleDropdown()})),this.selectors={...this.selectors,courseid:'[data-region="courseid"]',groupid:'[data-region="groupid"]',resetPageButton:'[data-action="resetpage"]'};const component=document.querySelector(this.componentSelector());this.courseID=component.querySelector(this.selectors.courseid).dataset.courseid,this.groupID=null===(_document$querySelect=document.querySelector(this.selectors.groupid))||void 0===_document$querySelect||null===(_document$querySelect2=_document$querySelect.dataset)||void 0===_document$querySelect2?void 0:_document$querySelect2.groupid}static init(){return new UserSearch}componentSelector(){return".user-search"}dropdownSelector(){return".usersearchdropdown"}triggerSelector(){return".usersearchwidget"}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("core_user/comboboxsearch/resultset",{users:this.getMatchedResults().slice(0,5),hasresults:this.getMatchedResults().length>0,matches:this.getMatchedResults().length,searchterm:this.getSearchTerm(),selectall:this.selectAllResultsLink()});(0,_templates.replaceNodeContents)(this.getHTMLElements().searchDropdown,html,js)}fetchDataset(){throw new Error("fetchDataset() must be implemented in ".concat(this.constructor.name))}async filterDataset(filterableData){return filterableData.filter((user=>Object.keys(user).some((key=>""!==user[key]&&null!==user[key]&&!this.bannedFilterFields.includes(key)&&user[key].toString().toLowerCase().includes(this.getPreppedSearchTerm())))))}async filterMatchDataset(){const stringMap=await this.getStringMap();this.setMatchedResults(this.getMatchedResults().map((user=>{for(const[key,value]of Object.entries(user)){if(null===value)continue;const valueString=value.toString().toLowerCase();if(valueString.includes(this.getPreppedSearchTerm())&&!this.bannedFilterFields.includes(key)){var _stringMap$get;user.matchingFieldName=null!==(_stringMap$get=stringMap.get(key))&&void 0!==_stringMap$get?_stringMap$get:key,user.matchingField=valueString.replace(this.getPreppedSearchTerm(),''.concat(this.getSearchTerm(),"")),user.matchingField="".concat(user.matchingField," (").concat(user.email,")"),user.link=this.selectOneLink(user.id);break}}return user})))}clickHandler(e){super.clickHandler(e).catch(_notification.default.exception),e.target.closest(this.selectors.component)&&e.stopImmediatePropagation(),e.target===this.getHTMLElements().currentViewAll&&0===e.button&&(window.location=this.selectAllResultsLink()),e.target.closest(this.selectors.resetPageButton)&&(window.location=e.target.closest(this.selectors.resetPageButton).href)}keyHandler(e){switch(super.keyHandler(e),e.target!==this.getHTMLElements().currentViewAll||"Enter"!==e.key&&"Space"!==e.key||(window.location=this.selectAllResultsLink()),e.key){case"Enter":case" ":if(document.activeElement===this.getHTMLElements().searchInput&&"Enter"===e.key&&null!==this.selectAllResultsLink()&&(window.location=this.selectAllResultsLink()),document.activeElement===this.getHTMLElements().clearSearchButton){this.closeSearch(!0);break}if(e.target.closest(this.selectors.resetPageButton)){window.location=e.target.closest(this.selectors.resetPageButton).href;break}if(e.target.closest(".dropdown-item")){e.preventDefault(),window.location=e.target.closest(".dropdown-item").href;break}break;case"Escape":this.toggleDropdown(),this.searchInput.focus({preventScroll:!0});break;case"Tab":e.target.closest(this.selectors.clearSearch)&&(this.currentViewAll&&!e.shiftKey?(e.preventDefault(),this.currentViewAll.focus({preventScroll:!0})):this.closeSearch())}}toggleDropdown(){arguments.length>0&&void 0!==arguments[0]&&arguments[0]?(this.searchDropdown.classList.add("show"),(0,_jquery.default)(this.searchDropdown).show(),this.component.setAttribute("aria-expanded","true")):(this.searchDropdown.classList.remove("show"),(0,_jquery.default)(this.searchDropdown).hide(),this.component.setAttribute("aria-expanded","false"))}selectAllResultsLink(){throw new Error("selectAllResultsLink() must be implemented in ".concat(this.constructor.name))}selectOneLink(userID){throw new Error("selectOneLink(".concat(userID,") must be implemented in ").concat(this.constructor.name))}getStringMap(){if(!this.profilestringmap){const requiredStrings=["username","firstname","lastname","email","city","country","department","institution","idnumber","phone1","phone2"];this.profilestringmap=(0,_str.get_strings)(requiredStrings.map((key=>({key:key})))).then((stringArray=>new Map(requiredStrings.map(((key,index)=>[key,stringArray[index]])))))}return this.profilestringmap}}return _exports.default=UserSearch,_exports.default})); +define("core_user/comboboxsearch/user",["exports","core/comboboxsearch/search_combobox","core/str","core/templates","jquery","core/notification"],(function(_exports,_search_combobox,_str,_templates,_jquery,_notification){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_search_combobox=_interopRequireDefault(_search_combobox),_jquery=_interopRequireDefault(_jquery),_notification=_interopRequireDefault(_notification);class UserSearch extends _search_combobox.default{constructor(){var _document$querySelect,_document$querySelect2;super(),_defineProperty(this,"courseID",void 0),_defineProperty(this,"groupID",void 0),_defineProperty(this,"bannedFilterFields",["profileimageurlsmall","profileimageurl","id","link","matchingField","matchingFieldName"]),_defineProperty(this,"profilestringmap",null),document.addEventListener("click",(e=>{!e.target.closest(this.selectors.component)&&this.searchDropdown.classList.contains("show")&&this.toggleDropdown()})),this.selectors={...this.selectors,courseid:'[data-region="courseid"]',groupid:'[data-region="groupid"]',resetPageButton:'[data-action="resetpage"]'};const component=document.querySelector(this.componentSelector());this.courseID=component.querySelector(this.selectors.courseid).dataset.courseid,this.groupID=null===(_document$querySelect=document.querySelector(this.selectors.groupid))||void 0===_document$querySelect||null===(_document$querySelect2=_document$querySelect.dataset)||void 0===_document$querySelect2?void 0:_document$querySelect2.groupid}static init(){return new UserSearch}componentSelector(){return".user-search"}dropdownSelector(){return".usersearchdropdown"}triggerSelector(){return".usersearchwidget"}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("core_user/comboboxsearch/resultset",{users:this.getMatchedResults().slice(0,5),hasresults:this.getMatchedResults().length>0,matches:this.getMatchedResults().length,searchterm:this.getSearchTerm(),selectall:this.selectAllResultsLink()});(0,_templates.replaceNodeContents)(this.getHTMLElements().searchDropdown,html,js)}fetchDataset(){throw new Error("fetchDataset() must be implemented in ".concat(this.constructor.name))}async filterDataset(filterableData){return filterableData.filter((user=>Object.keys(user).some((key=>""!==user[key]&&null!==user[key]&&!this.bannedFilterFields.includes(key)&&user[key].toString().toLowerCase().includes(this.getPreppedSearchTerm())))))}async filterMatchDataset(){const stringMap=await this.getStringMap();this.setMatchedResults(this.getMatchedResults().map((user=>{for(const[key,value]of Object.entries(user)){if(null===value)continue;const valueString=value.toString().toLowerCase();if(valueString.includes(this.getPreppedSearchTerm())&&!this.bannedFilterFields.includes(key)){var _stringMap$get;user.matchingFieldName=null!==(_stringMap$get=stringMap.get(key))&&void 0!==_stringMap$get?_stringMap$get:key,user.matchingField=valueString.replace(this.getPreppedSearchTerm(),''.concat(this.getSearchTerm(),"")),user.matchingField="".concat(user.matchingField," (").concat(user.email,")"),user.link=this.selectOneLink(user.id);break}}return user})))}clickHandler(e){super.clickHandler(e).catch(_notification.default.exception),e.target.closest(this.selectors.component)&&e.stopImmediatePropagation(),e.target===this.getHTMLElements().currentViewAll&&0===e.button&&(window.location=this.selectAllResultsLink()),e.target.closest(this.selectors.resetPageButton)&&(window.location=e.target.closest(this.selectors.resetPageButton).href)}keyHandler(e){switch(super.keyHandler(e),e.target!==this.getHTMLElements().currentViewAll||"Enter"!==e.key&&"Space"!==e.key||(window.location=this.selectAllResultsLink()),e.key){case"Enter":case" ":if(document.activeElement===this.getHTMLElements().searchInput&&"Enter"===e.key&&null!==this.selectAllResultsLink()&&(window.location=this.selectAllResultsLink()),document.activeElement===this.getHTMLElements().clearSearchButton){this.closeSearch(!0);break}if(e.target.closest(this.selectors.resetPageButton)){window.location=e.target.closest(this.selectors.resetPageButton).href;break}if(e.target.closest(".dropdown-item")){e.preventDefault(),window.location=e.target.closest(".dropdown-item").href;break}break;case"Escape":this.toggleDropdown(),this.searchInput.focus({preventScroll:!0});break;case"Tab":e.target.closest(this.selectors.clearSearch)&&(this.currentViewAll&&!e.shiftKey?(e.preventDefault(),this.currentViewAll.focus({preventScroll:!0})):this.closeSearch())}}toggleDropdown(){arguments.length>0&&void 0!==arguments[0]&&arguments[0]?(this.searchDropdown.classList.add("show"),(0,_jquery.default)(this.searchDropdown).show(),this.component.setAttribute("aria-expanded","true")):(this.searchDropdown.classList.remove("show"),(0,_jquery.default)(this.searchDropdown).hide(),this.component.setAttribute("aria-expanded","false"))}selectAllResultsLink(){throw new Error("selectAllResultsLink() must be implemented in ".concat(this.constructor.name))}selectOneLink(userID){throw new Error("selectOneLink(".concat(userID,") must be implemented in ").concat(this.constructor.name))}getStringMap(){if(!this.profilestringmap){const requiredStrings=["username","firstname","lastname","email","city","country","department","institution","idnumber","phone1","phone2"];this.profilestringmap=(0,_str.getStrings)(requiredStrings.map((key=>({key:key})))).then((stringArray=>new Map(requiredStrings.map(((key,index)=>[key,stringArray[index]])))))}return this.profilestringmap}}return _exports.default=UserSearch,_exports.default})); //# sourceMappingURL=user.min.js.map \ No newline at end of file diff --git a/user/amd/build/comboboxsearch/user.min.js.map b/user/amd/build/comboboxsearch/user.min.js.map index 1aa5d0bfd7c..70bb4102407 100644 --- a/user/amd/build/comboboxsearch/user.min.js.map +++ b/user/amd/build/comboboxsearch/user.min.js.map @@ -1 +1 @@ -{"version":3,"file":"user.min.js","sources":["../../src/comboboxsearch/user.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 * Allow the user to search for learners.\n *\n * @module core_user/comboboxsearch/user\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport search_combobox from 'core/comboboxsearch/search_combobox';\nimport {get_strings as getStrings} from 'core/str';\nimport {renderForPromise, replaceNodeContents} from 'core/templates';\nimport $ from 'jquery';\nimport Notification from 'core/notification';\n\nexport default class UserSearch extends search_combobox {\n\n courseID;\n groupID;\n bannedFilterFields = ['profileimageurlsmall', 'profileimageurl', 'id', 'link', 'matchingField', 'matchingFieldName'];\n\n // A map of user profile field names that is human-readable.\n profilestringmap = null;\n\n constructor() {\n super();\n // Register a small click event onto the document since we need to check if they are clicking off the component.\n document.addEventListener('click', (e) => {\n // Since we are handling dropdowns manually, ensure we can close it when clicking off.\n if (!e.target.closest(this.selectors.component) && this.searchDropdown.classList.contains('show')) {\n this.toggleDropdown();\n }\n });\n\n // Define our standard lookups.\n this.selectors = {...this.selectors,\n courseid: '[data-region=\"courseid\"]',\n groupid: '[data-region=\"groupid\"]',\n resetPageButton: '[data-action=\"resetpage\"]',\n };\n\n const component = document.querySelector(this.componentSelector());\n this.courseID = component.querySelector(this.selectors.courseid).dataset.courseid;\n this.groupID = document.querySelector(this.selectors.groupid)?.dataset?.groupid;\n }\n\n static init() {\n return new UserSearch();\n }\n\n /**\n * The overall div that contains the searching widget.\n *\n * @returns {string}\n */\n componentSelector() {\n return '.user-search';\n }\n\n /**\n * The dropdown div that contains the searching widget result space.\n *\n * @returns {string}\n */\n dropdownSelector() {\n return '.usersearchdropdown';\n }\n\n /**\n * The triggering div that contains the searching widget.\n *\n * @returns {string}\n */\n triggerSelector() {\n return '.usersearchwidget';\n }\n\n /**\n * Build the content then replace the node.\n */\n async renderDropdown() {\n const {html, js} = await renderForPromise('core_user/comboboxsearch/resultset', {\n users: this.getMatchedResults().slice(0, 5),\n hasresults: this.getMatchedResults().length > 0,\n matches: this.getMatchedResults().length,\n searchterm: this.getSearchTerm(),\n selectall: this.selectAllResultsLink(),\n });\n replaceNodeContents(this.getHTMLElements().searchDropdown, html, js);\n }\n\n /**\n * Get the data we will be searching against in this component.\n *\n * @returns {Promise<*>}\n */\n fetchDataset() {\n throw new Error(`fetchDataset() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Dictate to the search component how and what we want to match upon.\n *\n * @param {Array} filterableData\n * @returns {Array} The users that match the given criteria.\n */\n async filterDataset(filterableData) {\n return filterableData.filter((user) => Object.keys(user).some((key) => {\n if (user[key] === \"\" || user[key] === null || this.bannedFilterFields.includes(key)) {\n return false;\n }\n return user[key].toString().toLowerCase().includes(this.getPreppedSearchTerm());\n }));\n }\n\n /**\n * Given we have a subset of the dataset, set the field that we matched upon to inform the end user.\n *\n * @returns {Array} The results with the matched fields inserted.\n */\n async filterMatchDataset() {\n const stringMap = await this.getStringMap();\n this.setMatchedResults(\n this.getMatchedResults().map((user) => {\n for (const [key, value] of Object.entries(user)) {\n // Sometimes users have null values in their profile fields.\n if (value === null) {\n continue;\n }\n const valueString = value.toString().toLowerCase();\n if (valueString.includes(this.getPreppedSearchTerm()) && !this.bannedFilterFields.includes(key)) {\n // Ensure we have a good string, otherwise fallback to the key.\n user.matchingFieldName = stringMap.get(key) ?? key;\n user.matchingField = valueString.replace(\n this.getPreppedSearchTerm(),\n `${this.getSearchTerm()}`\n );\n user.matchingField = `${user.matchingField} (${user.email})`;\n user.link = this.selectOneLink(user.id);\n break;\n }\n }\n return user;\n })\n );\n }\n\n /**\n * The handler for when a user interacts with the component.\n *\n * @param {MouseEvent} e The triggering event that we are working with.\n */\n clickHandler(e) {\n super.clickHandler(e).catch(Notification.exception);\n if (e.target.closest(this.selectors.component)) {\n // Forcibly prevent BS events so that we can control the open and close.\n // Really needed because by default input elements cant trigger a dropdown.\n e.stopImmediatePropagation();\n }\n if (e.target === this.getHTMLElements().currentViewAll && e.button === 0) {\n window.location = this.selectAllResultsLink();\n }\n if (e.target.closest(this.selectors.resetPageButton)) {\n window.location = e.target.closest(this.selectors.resetPageButton).href;\n }\n }\n\n /**\n * The handler for when a user presses a key within the component.\n *\n * @param {KeyboardEvent} e The triggering event that we are working with.\n */\n keyHandler(e) {\n super.keyHandler(e);\n\n if (e.target === this.getHTMLElements().currentViewAll && (e.key === 'Enter' || e.key === 'Space')) {\n window.location = this.selectAllResultsLink();\n }\n\n // Switch the key presses to handle keyboard nav.\n switch (e.key) {\n case 'Enter':\n case ' ':\n if (document.activeElement === this.getHTMLElements().searchInput) {\n if (e.key === 'Enter' && this.selectAllResultsLink() !== null) {\n window.location = this.selectAllResultsLink();\n }\n }\n if (document.activeElement === this.getHTMLElements().clearSearchButton) {\n this.closeSearch(true);\n break;\n }\n if (e.target.closest(this.selectors.resetPageButton)) {\n window.location = e.target.closest(this.selectors.resetPageButton).href;\n break;\n }\n if (e.target.closest('.dropdown-item')) {\n e.preventDefault();\n window.location = e.target.closest('.dropdown-item').href;\n break;\n }\n break;\n case 'Escape':\n this.toggleDropdown();\n this.searchInput.focus({preventScroll: true});\n break;\n case 'Tab':\n // If the current focus is on clear search, then check if viewall exists then around tab to it.\n if (e.target.closest(this.selectors.clearSearch)) {\n if (this.currentViewAll && !e.shiftKey) {\n e.preventDefault();\n this.currentViewAll.focus({preventScroll: true});\n } else {\n this.closeSearch();\n }\n }\n break;\n }\n }\n\n /**\n * When called, hide or show the users dropdown.\n *\n * @param {Boolean} on Flag to toggle hiding or showing values.\n */\n toggleDropdown(on = false) {\n if (on) {\n this.searchDropdown.classList.add('show');\n $(this.searchDropdown).show();\n this.component.setAttribute('aria-expanded', 'true');\n } else {\n this.searchDropdown.classList.remove('show');\n $(this.searchDropdown).hide();\n this.component.setAttribute('aria-expanded', 'false');\n }\n }\n\n /**\n * Build up the view all link.\n */\n selectAllResultsLink() {\n throw new Error(`selectAllResultsLink() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Build up the view all link that is dedicated to a particular result.\n *\n * @param {Number} userID The ID of the user selected.\n */\n selectOneLink(userID) {\n throw new Error(`selectOneLink(${userID}) must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Given the set of profile fields we can possibly search, fetch their strings,\n * so we can report to screen readers the field that matched.\n *\n * @returns {Promise}\n */\n getStringMap() {\n if (!this.profilestringmap) {\n const requiredStrings = [\n 'username',\n 'firstname',\n 'lastname',\n 'email',\n 'city',\n 'country',\n 'department',\n 'institution',\n 'idnumber',\n 'phone1',\n 'phone2',\n ];\n this.profilestringmap = getStrings(requiredStrings.map((key) => ({key})))\n .then((stringArray) => new Map(\n requiredStrings.map((key, index) => ([key, stringArray[index]]))\n ));\n }\n return this.profilestringmap;\n }\n}\n"],"names":["UserSearch","search_combobox","constructor","document","addEventListener","e","target","closest","this","selectors","component","searchDropdown","classList","contains","toggleDropdown","courseid","groupid","resetPageButton","querySelector","componentSelector","courseID","dataset","groupID","_document$querySelect","_document$querySelect2","dropdownSelector","triggerSelector","html","js","users","getMatchedResults","slice","hasresults","length","matches","searchterm","getSearchTerm","selectall","selectAllResultsLink","getHTMLElements","fetchDataset","Error","name","filterableData","filter","user","Object","keys","some","key","bannedFilterFields","includes","toString","toLowerCase","getPreppedSearchTerm","stringMap","getStringMap","setMatchedResults","map","value","entries","valueString","matchingFieldName","get","matchingField","replace","email","link","selectOneLink","id","clickHandler","catch","Notification","exception","stopImmediatePropagation","currentViewAll","button","window","location","href","keyHandler","activeElement","searchInput","clearSearchButton","closeSearch","preventDefault","focus","preventScroll","clearSearch","shiftKey","add","show","setAttribute","remove","hide","userID","profilestringmap","requiredStrings","then","stringArray","Map","index"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"user.min.js","sources":["../../src/comboboxsearch/user.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 * Allow the user to search for learners.\n *\n * @module core_user/comboboxsearch/user\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport search_combobox from 'core/comboboxsearch/search_combobox';\nimport {getStrings} from 'core/str';\nimport {renderForPromise, replaceNodeContents} from 'core/templates';\nimport $ from 'jquery';\nimport Notification from 'core/notification';\n\nexport default class UserSearch extends search_combobox {\n\n courseID;\n groupID;\n bannedFilterFields = ['profileimageurlsmall', 'profileimageurl', 'id', 'link', 'matchingField', 'matchingFieldName'];\n\n // A map of user profile field names that is human-readable.\n profilestringmap = null;\n\n constructor() {\n super();\n // Register a small click event onto the document since we need to check if they are clicking off the component.\n document.addEventListener('click', (e) => {\n // Since we are handling dropdowns manually, ensure we can close it when clicking off.\n if (!e.target.closest(this.selectors.component) && this.searchDropdown.classList.contains('show')) {\n this.toggleDropdown();\n }\n });\n\n // Define our standard lookups.\n this.selectors = {...this.selectors,\n courseid: '[data-region=\"courseid\"]',\n groupid: '[data-region=\"groupid\"]',\n resetPageButton: '[data-action=\"resetpage\"]',\n };\n\n const component = document.querySelector(this.componentSelector());\n this.courseID = component.querySelector(this.selectors.courseid).dataset.courseid;\n this.groupID = document.querySelector(this.selectors.groupid)?.dataset?.groupid;\n }\n\n static init() {\n return new UserSearch();\n }\n\n /**\n * The overall div that contains the searching widget.\n *\n * @returns {string}\n */\n componentSelector() {\n return '.user-search';\n }\n\n /**\n * The dropdown div that contains the searching widget result space.\n *\n * @returns {string}\n */\n dropdownSelector() {\n return '.usersearchdropdown';\n }\n\n /**\n * The triggering div that contains the searching widget.\n *\n * @returns {string}\n */\n triggerSelector() {\n return '.usersearchwidget';\n }\n\n /**\n * Build the content then replace the node.\n */\n async renderDropdown() {\n const {html, js} = await renderForPromise('core_user/comboboxsearch/resultset', {\n users: this.getMatchedResults().slice(0, 5),\n hasresults: this.getMatchedResults().length > 0,\n matches: this.getMatchedResults().length,\n searchterm: this.getSearchTerm(),\n selectall: this.selectAllResultsLink(),\n });\n replaceNodeContents(this.getHTMLElements().searchDropdown, html, js);\n }\n\n /**\n * Get the data we will be searching against in this component.\n *\n * @returns {Promise<*>}\n */\n fetchDataset() {\n throw new Error(`fetchDataset() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Dictate to the search component how and what we want to match upon.\n *\n * @param {Array} filterableData\n * @returns {Array} The users that match the given criteria.\n */\n async filterDataset(filterableData) {\n return filterableData.filter((user) => Object.keys(user).some((key) => {\n if (user[key] === \"\" || user[key] === null || this.bannedFilterFields.includes(key)) {\n return false;\n }\n return user[key].toString().toLowerCase().includes(this.getPreppedSearchTerm());\n }));\n }\n\n /**\n * Given we have a subset of the dataset, set the field that we matched upon to inform the end user.\n *\n * @returns {Array} The results with the matched fields inserted.\n */\n async filterMatchDataset() {\n const stringMap = await this.getStringMap();\n this.setMatchedResults(\n this.getMatchedResults().map((user) => {\n for (const [key, value] of Object.entries(user)) {\n // Sometimes users have null values in their profile fields.\n if (value === null) {\n continue;\n }\n const valueString = value.toString().toLowerCase();\n if (valueString.includes(this.getPreppedSearchTerm()) && !this.bannedFilterFields.includes(key)) {\n // Ensure we have a good string, otherwise fallback to the key.\n user.matchingFieldName = stringMap.get(key) ?? key;\n user.matchingField = valueString.replace(\n this.getPreppedSearchTerm(),\n `${this.getSearchTerm()}`\n );\n user.matchingField = `${user.matchingField} (${user.email})`;\n user.link = this.selectOneLink(user.id);\n break;\n }\n }\n return user;\n })\n );\n }\n\n /**\n * The handler for when a user interacts with the component.\n *\n * @param {MouseEvent} e The triggering event that we are working with.\n */\n clickHandler(e) {\n super.clickHandler(e).catch(Notification.exception);\n if (e.target.closest(this.selectors.component)) {\n // Forcibly prevent BS events so that we can control the open and close.\n // Really needed because by default input elements cant trigger a dropdown.\n e.stopImmediatePropagation();\n }\n if (e.target === this.getHTMLElements().currentViewAll && e.button === 0) {\n window.location = this.selectAllResultsLink();\n }\n if (e.target.closest(this.selectors.resetPageButton)) {\n window.location = e.target.closest(this.selectors.resetPageButton).href;\n }\n }\n\n /**\n * The handler for when a user presses a key within the component.\n *\n * @param {KeyboardEvent} e The triggering event that we are working with.\n */\n keyHandler(e) {\n super.keyHandler(e);\n\n if (e.target === this.getHTMLElements().currentViewAll && (e.key === 'Enter' || e.key === 'Space')) {\n window.location = this.selectAllResultsLink();\n }\n\n // Switch the key presses to handle keyboard nav.\n switch (e.key) {\n case 'Enter':\n case ' ':\n if (document.activeElement === this.getHTMLElements().searchInput) {\n if (e.key === 'Enter' && this.selectAllResultsLink() !== null) {\n window.location = this.selectAllResultsLink();\n }\n }\n if (document.activeElement === this.getHTMLElements().clearSearchButton) {\n this.closeSearch(true);\n break;\n }\n if (e.target.closest(this.selectors.resetPageButton)) {\n window.location = e.target.closest(this.selectors.resetPageButton).href;\n break;\n }\n if (e.target.closest('.dropdown-item')) {\n e.preventDefault();\n window.location = e.target.closest('.dropdown-item').href;\n break;\n }\n break;\n case 'Escape':\n this.toggleDropdown();\n this.searchInput.focus({preventScroll: true});\n break;\n case 'Tab':\n // If the current focus is on clear search, then check if viewall exists then around tab to it.\n if (e.target.closest(this.selectors.clearSearch)) {\n if (this.currentViewAll && !e.shiftKey) {\n e.preventDefault();\n this.currentViewAll.focus({preventScroll: true});\n } else {\n this.closeSearch();\n }\n }\n break;\n }\n }\n\n /**\n * When called, hide or show the users dropdown.\n *\n * @param {Boolean} on Flag to toggle hiding or showing values.\n */\n toggleDropdown(on = false) {\n if (on) {\n this.searchDropdown.classList.add('show');\n $(this.searchDropdown).show();\n this.component.setAttribute('aria-expanded', 'true');\n } else {\n this.searchDropdown.classList.remove('show');\n $(this.searchDropdown).hide();\n this.component.setAttribute('aria-expanded', 'false');\n }\n }\n\n /**\n * Build up the view all link.\n */\n selectAllResultsLink() {\n throw new Error(`selectAllResultsLink() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Build up the view all link that is dedicated to a particular result.\n *\n * @param {Number} userID The ID of the user selected.\n */\n selectOneLink(userID) {\n throw new Error(`selectOneLink(${userID}) must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Given the set of profile fields we can possibly search, fetch their strings,\n * so we can report to screen readers the field that matched.\n *\n * @returns {Promise}\n */\n getStringMap() {\n if (!this.profilestringmap) {\n const requiredStrings = [\n 'username',\n 'firstname',\n 'lastname',\n 'email',\n 'city',\n 'country',\n 'department',\n 'institution',\n 'idnumber',\n 'phone1',\n 'phone2',\n ];\n this.profilestringmap = getStrings(requiredStrings.map((key) => ({key})))\n .then((stringArray) => new Map(\n requiredStrings.map((key, index) => ([key, stringArray[index]]))\n ));\n }\n return this.profilestringmap;\n }\n}\n"],"names":["UserSearch","search_combobox","constructor","document","addEventListener","e","target","closest","this","selectors","component","searchDropdown","classList","contains","toggleDropdown","courseid","groupid","resetPageButton","querySelector","componentSelector","courseID","dataset","groupID","_document$querySelect","_document$querySelect2","dropdownSelector","triggerSelector","html","js","users","getMatchedResults","slice","hasresults","length","matches","searchterm","getSearchTerm","selectall","selectAllResultsLink","getHTMLElements","fetchDataset","Error","name","filterableData","filter","user","Object","keys","some","key","bannedFilterFields","includes","toString","toLowerCase","getPreppedSearchTerm","stringMap","getStringMap","setMatchedResults","map","value","entries","valueString","matchingFieldName","get","matchingField","replace","email","link","selectOneLink","id","clickHandler","catch","Notification","exception","stopImmediatePropagation","currentViewAll","button","window","location","href","keyHandler","activeElement","searchInput","clearSearchButton","closeSearch","preventDefault","focus","preventScroll","clearSearch","shiftKey","add","show","setAttribute","remove","hide","userID","profilestringmap","requiredStrings","then","stringArray","Map","index"],"mappings":"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"} \ No newline at end of file diff --git a/user/amd/build/edit_profile_fields.min.js b/user/amd/build/edit_profile_fields.min.js index d4649b11a04..ecb6c46b956 100644 --- a/user/amd/build/edit_profile_fields.min.js +++ b/user/amd/build/edit_profile_fields.min.js @@ -5,6 +5,6 @@ define("core_user/edit_profile_fields",["exports","core_form/modalform","core/st * @module core_user/edit_profile_fields * @copyright 2021 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=(obj=_modalform)&&obj.__esModule?obj:{default:obj};const Selectors_actions={editCategory:'[data-action="editcategory"]',editField:'[data-action="editfield"]',createField:'[data-action="createfield"]'};_exports.init=()=>{document.addEventListener("click",(function(e){let element=e.target.closest(Selectors_actions.editCategory);if(element){e.preventDefault();const title=element.getAttribute("data-id")?(0,_str.get_string)("profileeditcategory","admin",element.getAttribute("data-name")):(0,_str.get_string)("profilecreatenewcategory","admin"),form=new _modalform.default({formClass:"core_user\\form\\profile_category_form",args:{id:element.getAttribute("data-id")},modalConfig:{title:title},returnFocus:element});form.addEventListener(form.events.FORM_SUBMITTED,(()=>window.location.reload())),form.show()}if(element=e.target.closest(Selectors_actions.editField),element){e.preventDefault();const form=new _modalform.default({formClass:"core_user\\form\\profile_field_form",args:{id:element.getAttribute("data-id")},modalConfig:{title:(0,_str.get_string)("profileeditfield","admin",element.getAttribute("data-name"))},returnFocus:element});form.addEventListener(form.events.FORM_SUBMITTED,(()=>window.location.reload())),form.show()}if(element=e.target.closest(Selectors_actions.createField),element){e.preventDefault();const form=new _modalform.default({formClass:"core_user\\form\\profile_field_form",args:{datatype:element.getAttribute("data-datatype"),categoryid:element.getAttribute("data-categoryid")},modalConfig:{title:(0,_str.get_string)("profilecreatenewfield","admin",element.getAttribute("data-datatypename"))},returnFocus:element});form.addEventListener(form.events.FORM_SUBMITTED,(()=>window.location.reload())),form.show()}}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=(obj=_modalform)&&obj.__esModule?obj:{default:obj};const Selectors_actions={editCategory:'[data-action="editcategory"]',editField:'[data-action="editfield"]',createField:'[data-action="createfield"]'};_exports.init=()=>{document.addEventListener("click",(function(e){let element=e.target.closest(Selectors_actions.editCategory);if(element){e.preventDefault();const title=element.getAttribute("data-id")?(0,_str.getString)("profileeditcategory","admin",element.getAttribute("data-name")):(0,_str.getString)("profilecreatenewcategory","admin"),form=new _modalform.default({formClass:"core_user\\form\\profile_category_form",args:{id:element.getAttribute("data-id")},modalConfig:{title:title},returnFocus:element});form.addEventListener(form.events.FORM_SUBMITTED,(()=>window.location.reload())),form.show()}if(element=e.target.closest(Selectors_actions.editField),element){e.preventDefault();const form=new _modalform.default({formClass:"core_user\\form\\profile_field_form",args:{id:element.getAttribute("data-id")},modalConfig:{title:(0,_str.getString)("profileeditfield","admin",element.getAttribute("data-name"))},returnFocus:element});form.addEventListener(form.events.FORM_SUBMITTED,(()=>window.location.reload())),form.show()}if(element=e.target.closest(Selectors_actions.createField),element){e.preventDefault();const form=new _modalform.default({formClass:"core_user\\form\\profile_field_form",args:{datatype:element.getAttribute("data-datatype"),categoryid:element.getAttribute("data-categoryid")},modalConfig:{title:(0,_str.getString)("profilecreatenewfield","admin",element.getAttribute("data-datatypename"))},returnFocus:element});form.addEventListener(form.events.FORM_SUBMITTED,(()=>window.location.reload())),form.show()}}))}})); //# sourceMappingURL=edit_profile_fields.min.js.map \ No newline at end of file diff --git a/user/amd/build/edit_profile_fields.min.js.map b/user/amd/build/edit_profile_fields.min.js.map index 6651b9c1c72..e4835c9aa72 100644 --- a/user/amd/build/edit_profile_fields.min.js.map +++ b/user/amd/build/edit_profile_fields.min.js.map @@ -1 +1 @@ -{"version":3,"file":"edit_profile_fields.min.js","sources":["../src/edit_profile_fields.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\nimport ModalForm from 'core_form/modalform';\nimport {get_string as getString} from 'core/str';\n\n/**\n * User profile fields editor\n *\n * @module core_user/edit_profile_fields\n * @copyright 2021 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst Selectors = {\n actions: {\n editCategory: '[data-action=\"editcategory\"]',\n editField: '[data-action=\"editfield\"]',\n createField: '[data-action=\"createfield\"]',\n },\n};\n\nexport const init = () => {\n document.addEventListener('click', function(e) {\n let element = e.target.closest(Selectors.actions.editCategory);\n if (element) {\n e.preventDefault();\n const title = element.getAttribute('data-id') ?\n getString('profileeditcategory', 'admin', element.getAttribute('data-name')) :\n getString('profilecreatenewcategory', 'admin');\n const form = new ModalForm({\n formClass: 'core_user\\\\form\\\\profile_category_form',\n args: {id: element.getAttribute('data-id')},\n modalConfig: {title},\n returnFocus: element,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n }\n\n element = e.target.closest(Selectors.actions.editField);\n if (element) {\n e.preventDefault();\n const form = new ModalForm({\n formClass: 'core_user\\\\form\\\\profile_field_form',\n args: {id: element.getAttribute('data-id')},\n modalConfig: {title: getString('profileeditfield', 'admin', element.getAttribute('data-name'))},\n returnFocus: element,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n }\n\n element = e.target.closest(Selectors.actions.createField);\n if (element) {\n e.preventDefault();\n const form = new ModalForm({\n formClass: 'core_user\\\\form\\\\profile_field_form',\n args: {datatype: element.getAttribute('data-datatype'), categoryid: element.getAttribute('data-categoryid')},\n modalConfig: {title: getString('profilecreatenewfield', 'admin', element.getAttribute('data-datatypename'))},\n returnFocus: element,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n }\n });\n};\n"],"names":["Selectors","editCategory","editField","createField","document","addEventListener","e","element","target","closest","preventDefault","title","getAttribute","form","ModalForm","formClass","args","id","modalConfig","returnFocus","events","FORM_SUBMITTED","window","location","reload","show","datatype","categoryid"],"mappings":";;;;;;;sJA0BMA,kBACO,CACLC,aAAc,+BACdC,UAAW,4BACXC,YAAa,6CAID,KAChBC,SAASC,iBAAiB,SAAS,SAASC,OACpCC,QAAUD,EAAEE,OAAOC,QAAQT,kBAAkBC,iBAC7CM,QAAS,CACTD,EAAEI,uBACIC,MAAQJ,QAAQK,aAAa,YAC/B,mBAAU,sBAAuB,QAASL,QAAQK,aAAa,eAC/D,mBAAU,2BAA4B,SACpCC,KAAO,IAAIC,mBAAU,CACvBC,UAAW,yCACXC,KAAM,CAACC,GAAIV,QAAQK,aAAa,YAChCM,YAAa,CAACP,MAAAA,OACdQ,YAAaZ,UAEjBM,KAAKR,iBAAiBQ,KAAKO,OAAOC,gBAAgB,IAAMC,OAAOC,SAASC,WACxEX,KAAKY,UAGTlB,QAAUD,EAAEE,OAAOC,QAAQT,kBAAkBE,WACzCK,QAAS,CACTD,EAAEI,uBACIG,KAAO,IAAIC,mBAAU,CACvBC,UAAW,sCACXC,KAAM,CAACC,GAAIV,QAAQK,aAAa,YAChCM,YAAa,CAACP,OAAO,mBAAU,mBAAoB,QAASJ,QAAQK,aAAa,eACjFO,YAAaZ,UAEjBM,KAAKR,iBAAiBQ,KAAKO,OAAOC,gBAAgB,IAAMC,OAAOC,SAASC,WACxEX,KAAKY,UAGTlB,QAAUD,EAAEE,OAAOC,QAAQT,kBAAkBG,aACzCI,QAAS,CACTD,EAAEI,uBACIG,KAAO,IAAIC,mBAAU,CACvBC,UAAW,sCACXC,KAAM,CAACU,SAAUnB,QAAQK,aAAa,iBAAkBe,WAAYpB,QAAQK,aAAa,oBACzFM,YAAa,CAACP,OAAO,mBAAU,wBAAyB,QAASJ,QAAQK,aAAa,uBACtFO,YAAaZ,UAEjBM,KAAKR,iBAAiBQ,KAAKO,OAAOC,gBAAgB,IAAMC,OAAOC,SAASC,WACxEX,KAAKY"} \ No newline at end of file +{"version":3,"file":"edit_profile_fields.min.js","sources":["../src/edit_profile_fields.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\nimport ModalForm from 'core_form/modalform';\nimport {getString} from 'core/str';\n\n/**\n * User profile fields editor\n *\n * @module core_user/edit_profile_fields\n * @copyright 2021 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst Selectors = {\n actions: {\n editCategory: '[data-action=\"editcategory\"]',\n editField: '[data-action=\"editfield\"]',\n createField: '[data-action=\"createfield\"]',\n },\n};\n\nexport const init = () => {\n document.addEventListener('click', function(e) {\n let element = e.target.closest(Selectors.actions.editCategory);\n if (element) {\n e.preventDefault();\n const title = element.getAttribute('data-id') ?\n getString('profileeditcategory', 'admin', element.getAttribute('data-name')) :\n getString('profilecreatenewcategory', 'admin');\n const form = new ModalForm({\n formClass: 'core_user\\\\form\\\\profile_category_form',\n args: {id: element.getAttribute('data-id')},\n modalConfig: {title},\n returnFocus: element,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n }\n\n element = e.target.closest(Selectors.actions.editField);\n if (element) {\n e.preventDefault();\n const form = new ModalForm({\n formClass: 'core_user\\\\form\\\\profile_field_form',\n args: {id: element.getAttribute('data-id')},\n modalConfig: {title: getString('profileeditfield', 'admin', element.getAttribute('data-name'))},\n returnFocus: element,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n }\n\n element = e.target.closest(Selectors.actions.createField);\n if (element) {\n e.preventDefault();\n const form = new ModalForm({\n formClass: 'core_user\\\\form\\\\profile_field_form',\n args: {datatype: element.getAttribute('data-datatype'), categoryid: element.getAttribute('data-categoryid')},\n modalConfig: {title: getString('profilecreatenewfield', 'admin', element.getAttribute('data-datatypename'))},\n returnFocus: element,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n }\n });\n};\n"],"names":["Selectors","editCategory","editField","createField","document","addEventListener","e","element","target","closest","preventDefault","title","getAttribute","form","ModalForm","formClass","args","id","modalConfig","returnFocus","events","FORM_SUBMITTED","window","location","reload","show","datatype","categoryid"],"mappings":";;;;;;;sJA0BMA,kBACO,CACLC,aAAc,+BACdC,UAAW,4BACXC,YAAa,6CAID,KAChBC,SAASC,iBAAiB,SAAS,SAASC,OACpCC,QAAUD,EAAEE,OAAOC,QAAQT,kBAAkBC,iBAC7CM,QAAS,CACTD,EAAEI,uBACIC,MAAQJ,QAAQK,aAAa,YAC/B,kBAAU,sBAAuB,QAASL,QAAQK,aAAa,eAC/D,kBAAU,2BAA4B,SACpCC,KAAO,IAAIC,mBAAU,CACvBC,UAAW,yCACXC,KAAM,CAACC,GAAIV,QAAQK,aAAa,YAChCM,YAAa,CAACP,MAAAA,OACdQ,YAAaZ,UAEjBM,KAAKR,iBAAiBQ,KAAKO,OAAOC,gBAAgB,IAAMC,OAAOC,SAASC,WACxEX,KAAKY,UAGTlB,QAAUD,EAAEE,OAAOC,QAAQT,kBAAkBE,WACzCK,QAAS,CACTD,EAAEI,uBACIG,KAAO,IAAIC,mBAAU,CACvBC,UAAW,sCACXC,KAAM,CAACC,GAAIV,QAAQK,aAAa,YAChCM,YAAa,CAACP,OAAO,kBAAU,mBAAoB,QAASJ,QAAQK,aAAa,eACjFO,YAAaZ,UAEjBM,KAAKR,iBAAiBQ,KAAKO,OAAOC,gBAAgB,IAAMC,OAAOC,SAASC,WACxEX,KAAKY,UAGTlB,QAAUD,EAAEE,OAAOC,QAAQT,kBAAkBG,aACzCI,QAAS,CACTD,EAAEI,uBACIG,KAAO,IAAIC,mBAAU,CACvBC,UAAW,sCACXC,KAAM,CAACU,SAAUnB,QAAQK,aAAa,iBAAkBe,WAAYpB,QAAQK,aAAa,oBACzFM,YAAa,CAACP,OAAO,kBAAU,wBAAyB,QAASJ,QAAQK,aAAa,uBACtFO,YAAaZ,UAEjBM,KAAKR,iBAAiBQ,KAAKO,OAAOC,gBAAgB,IAAMC,OAAOC,SAASC,WACxEX,KAAKY"} \ No newline at end of file diff --git a/user/amd/build/form_user_selector.min.js b/user/amd/build/form_user_selector.min.js index 2cfa5d8ba26..dac1d0f3755 100644 --- a/user/amd/build/form_user_selector.min.js +++ b/user/amd/build/form_user_selector.min.js @@ -5,6 +5,6 @@ define("core_user/form_user_selector",["exports","core/ajax","core/templates","c * @module core_user/form_user_selector * @copyright 2020 David Mudrák * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.processResults=function(selector,results){return Array.isArray(results)?results.map((result=>({value:result.id,label:result.label}))):results},_exports.transport=async function(selector,query,callback,failure){const request={methodname:"core_user_search_identity",args:{query:query}};try{const response=await _ajax.default.call([request])[0];if(response.overflow){const msg=await(0,_str.get_string)("toomanyuserstoshow","core",">"+response.maxusersperpage);callback(msg)}else{let labels=[];response.list.forEach((user=>{labels.push((0,_templates.render)("core_user/form_user_selector_suggestion",user))})),labels=await Promise.all(labels),response.list.forEach(((user,index)=>{user.label=labels[index]})),callback(response.list)}}catch(e){failure(e)}},_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.processResults=function(selector,results){return Array.isArray(results)?results.map((result=>({value:result.id,label:result.label}))):results},_exports.transport=async function(selector,query,callback,failure){const request={methodname:"core_user_search_identity",args:{query:query}};try{const response=await _ajax.default.call([request])[0];if(response.overflow){const msg=await(0,_str.getString)("toomanyuserstoshow","core",">"+response.maxusersperpage);callback(msg)}else{let labels=[];response.list.forEach((user=>{labels.push((0,_templates.render)("core_user/form_user_selector_suggestion",user))})),labels=await Promise.all(labels),response.list.forEach(((user,index)=>{user.label=labels[index]})),callback(response.list)}}catch(e){failure(e)}},_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj}})); //# sourceMappingURL=form_user_selector.min.js.map \ No newline at end of file diff --git a/user/amd/build/form_user_selector.min.js.map b/user/amd/build/form_user_selector.min.js.map index b94f9475fbb..4eb0a0a3bd0 100644 --- a/user/amd/build/form_user_selector.min.js.map +++ b/user/amd/build/form_user_selector.min.js.map @@ -1 +1 @@ -{"version":3,"file":"form_user_selector.min.js","sources":["../src/form_user_selector.js"],"sourcesContent":["// This file is part of Moodle - https://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 * Provides the required functionality for an autocomplete element to select a user.\n *\n * @module core_user/form_user_selector\n * @copyright 2020 David Mudrák \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport {render as renderTemplate} from 'core/templates';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Load the list of users matching the query and render the selector labels for them.\n *\n * @param {String} selector The selector of the auto complete element.\n * @param {String} query The query string.\n * @param {Function} callback A callback function receiving an array of results.\n * @param {Function} failure A function to call in case of failure, receiving the error message.\n */\nexport async function transport(selector, query, callback, failure) {\n\n const request = {\n methodname: 'core_user_search_identity',\n args: {\n query: query\n }\n };\n\n try {\n const response = await Ajax.call([request])[0];\n\n if (response.overflow) {\n const msg = await getString('toomanyuserstoshow', 'core', '>' + response.maxusersperpage);\n callback(msg);\n\n } else {\n let labels = [];\n response.list.forEach(user => {\n labels.push(renderTemplate('core_user/form_user_selector_suggestion', user));\n });\n labels = await Promise.all(labels);\n\n response.list.forEach((user, index) => {\n user.label = labels[index];\n });\n\n callback(response.list);\n }\n\n } catch (e) {\n failure(e);\n }\n}\n\n/**\n * Process the results for auto complete elements.\n *\n * @param {String} selector The selector of the auto complete element.\n * @param {Array} results An array or results returned by {@see transport()}.\n * @return {Array} New array of the selector options.\n */\nexport function processResults(selector, results) {\n\n if (!Array.isArray(results)) {\n return results;\n\n } else {\n return results.map(result => ({value: result.id, label: result.label}));\n }\n}\n"],"names":["selector","results","Array","isArray","map","result","value","id","label","query","callback","failure","request","methodname","args","response","Ajax","call","overflow","msg","maxusersperpage","labels","list","forEach","user","push","Promise","all","index","e"],"mappings":";;;;;;;8FA6E+BA,SAAUC,gBAEhCC,MAAMC,QAAQF,SAIRA,QAAQG,KAAIC,UAAYC,MAAOD,OAAOE,GAAIC,MAAOH,OAAOG,UAHxDP,2CA7CiBD,SAAUS,MAAOC,SAAUC,eAEjDC,QAAU,CACZC,WAAY,4BACZC,KAAM,CACFL,MAAOA,kBAKLM,eAAiBC,cAAKC,KAAK,CAACL,UAAU,MAExCG,SAASG,SAAU,OACbC,UAAY,mBAAU,qBAAsB,OAAQ,IAAMJ,SAASK,iBACzEV,SAASS,SAEN,KACCE,OAAS,GACbN,SAASO,KAAKC,SAAQC,OAClBH,OAAOI,MAAK,qBAAe,0CAA2CD,UAE1EH,aAAeK,QAAQC,IAAIN,QAE3BN,SAASO,KAAKC,SAAQ,CAACC,KAAMI,SACzBJ,KAAKhB,MAAQa,OAAOO,UAGxBlB,SAASK,SAASO,OAGxB,MAAOO,GACLlB,QAAQkB"} \ No newline at end of file +{"version":3,"file":"form_user_selector.min.js","sources":["../src/form_user_selector.js"],"sourcesContent":["// This file is part of Moodle - https://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 * Provides the required functionality for an autocomplete element to select a user.\n *\n * @module core_user/form_user_selector\n * @copyright 2020 David Mudrák \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport {render as renderTemplate} from 'core/templates';\nimport {getString} from 'core/str';\n\n/**\n * Load the list of users matching the query and render the selector labels for them.\n *\n * @param {String} selector The selector of the auto complete element.\n * @param {String} query The query string.\n * @param {Function} callback A callback function receiving an array of results.\n * @param {Function} failure A function to call in case of failure, receiving the error message.\n */\nexport async function transport(selector, query, callback, failure) {\n\n const request = {\n methodname: 'core_user_search_identity',\n args: {\n query: query\n }\n };\n\n try {\n const response = await Ajax.call([request])[0];\n\n if (response.overflow) {\n const msg = await getString('toomanyuserstoshow', 'core', '>' + response.maxusersperpage);\n callback(msg);\n\n } else {\n let labels = [];\n response.list.forEach(user => {\n labels.push(renderTemplate('core_user/form_user_selector_suggestion', user));\n });\n labels = await Promise.all(labels);\n\n response.list.forEach((user, index) => {\n user.label = labels[index];\n });\n\n callback(response.list);\n }\n\n } catch (e) {\n failure(e);\n }\n}\n\n/**\n * Process the results for auto complete elements.\n *\n * @param {String} selector The selector of the auto complete element.\n * @param {Array} results An array or results returned by {@see transport()}.\n * @return {Array} New array of the selector options.\n */\nexport function processResults(selector, results) {\n\n if (!Array.isArray(results)) {\n return results;\n\n } else {\n return results.map(result => ({value: result.id, label: result.label}));\n }\n}\n"],"names":["selector","results","Array","isArray","map","result","value","id","label","query","callback","failure","request","methodname","args","response","Ajax","call","overflow","msg","maxusersperpage","labels","list","forEach","user","push","Promise","all","index","e"],"mappings":";;;;;;;8FA6E+BA,SAAUC,gBAEhCC,MAAMC,QAAQF,SAIRA,QAAQG,KAAIC,UAAYC,MAAOD,OAAOE,GAAIC,MAAOH,OAAOG,UAHxDP,2CA7CiBD,SAAUS,MAAOC,SAAUC,eAEjDC,QAAU,CACZC,WAAY,4BACZC,KAAM,CACFL,MAAOA,kBAKLM,eAAiBC,cAAKC,KAAK,CAACL,UAAU,MAExCG,SAASG,SAAU,OACbC,UAAY,kBAAU,qBAAsB,OAAQ,IAAMJ,SAASK,iBACzEV,SAASS,SAEN,KACCE,OAAS,GACbN,SAASO,KAAKC,SAAQC,OAClBH,OAAOI,MAAK,qBAAe,0CAA2CD,UAE1EH,aAAeK,QAAQC,IAAIN,QAE3BN,SAASO,KAAKC,SAAQ,CAACC,KAAMI,SACzBJ,KAAKhB,MAAQa,OAAOO,UAGxBlB,SAASK,SAASO,OAGxB,MAAOO,GACLlB,QAAQkB"} \ No newline at end of file diff --git a/user/amd/build/private_files.min.js b/user/amd/build/private_files.min.js index 5d51d5e09a2..7fce3eaca9d 100644 --- a/user/amd/build/private_files.min.js +++ b/user/amd/build/private_files.min.js @@ -5,6 +5,6 @@ define("core_user/private_files",["exports","core_form/dynamicform","core_form/m * @module core_user/private_files * @copyright 2020 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.initModal=_exports.initDynamicForm=void 0,_dynamicform=_interopRequireDefault(_dynamicform),_modalform=_interopRequireDefault(_modalform);_exports.initDynamicForm=(containerSelector,formClass)=>{const form=new _dynamicform.default(document.querySelector(containerSelector),formClass);form.addEventListener(form.events.FORM_SUBMITTED,(()=>{form.load(),(0,_str.get_string)("changessaved").then(_toast.add).catch(null)})),form.addEventListener(form.events.CANCEL_BUTTON_PRESSED,(()=>window.location.reload()))};_exports.initModal=(elementSelector,formClass)=>{document.querySelector(elementSelector).addEventListener("click",(function(e){e.preventDefault();const form=new _modalform.default({formClass:formClass,args:{nosubmit:!0},modalConfig:{title:(0,_str.get_string)("privatefilesmanage")},returnFocus:e.target});form.addEventListener(form.events.FORM_SUBMITTED,(()=>window.location.reload())),form.show()}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.initModal=_exports.initDynamicForm=void 0,_dynamicform=_interopRequireDefault(_dynamicform),_modalform=_interopRequireDefault(_modalform);_exports.initDynamicForm=(containerSelector,formClass)=>{const form=new _dynamicform.default(document.querySelector(containerSelector),formClass);form.addEventListener(form.events.FORM_SUBMITTED,(()=>{form.load(),(0,_str.getString)("changessaved").then(_toast.add).catch(null)})),form.addEventListener(form.events.CANCEL_BUTTON_PRESSED,(()=>window.location.reload()))};_exports.initModal=(elementSelector,formClass)=>{document.querySelector(elementSelector).addEventListener("click",(function(e){e.preventDefault();const form=new _modalform.default({formClass:formClass,args:{nosubmit:!0},modalConfig:{title:(0,_str.getString)("privatefilesmanage")},returnFocus:e.target});form.addEventListener(form.events.FORM_SUBMITTED,(()=>window.location.reload())),form.show()}))}})); //# sourceMappingURL=private_files.min.js.map \ No newline at end of file diff --git a/user/amd/build/private_files.min.js.map b/user/amd/build/private_files.min.js.map index 1acf0401686..0d652a26614 100644 --- a/user/amd/build/private_files.min.js.map +++ b/user/amd/build/private_files.min.js.map @@ -1 +1 @@ -{"version":3,"file":"private_files.min.js","sources":["../src/private_files.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 * Module to handle AJAX interactions with user private files\n *\n * @module core_user/private_files\n * @copyright 2020 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport DynamicForm from 'core_form/dynamicform';\nimport ModalForm from 'core_form/modalform';\nimport {get_string as getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\n\n/**\n * Initialize private files form as AJAX form\n *\n * @param {String} containerSelector\n * @param {String} formClass\n */\nexport const initDynamicForm = (containerSelector, formClass) => {\n const form = new DynamicForm(document.querySelector(containerSelector), formClass);\n\n // When form is saved, refresh it to remove validation errors, if any:\n form.addEventListener(form.events.FORM_SUBMITTED, () => {\n form.load();\n getString('changessaved')\n .then(addToast)\n .catch(null);\n });\n\n // Reload the page on cancel.\n form.addEventListener(form.events.CANCEL_BUTTON_PRESSED, () => window.location.reload());\n};\n\n/**\n * Initialize private files form as Modal form\n *\n * @param {String} elementSelector\n * @param {String} formClass\n */\nexport const initModal = (elementSelector, formClass) => {\n document.querySelector(elementSelector).addEventListener('click', function(e) {\n e.preventDefault();\n const form = new ModalForm({\n formClass,\n args: {nosubmit: true},\n modalConfig: {title: getString('privatefilesmanage')},\n returnFocus: e.target,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n });\n};\n"],"names":["containerSelector","formClass","form","DynamicForm","document","querySelector","addEventListener","events","FORM_SUBMITTED","load","then","addToast","catch","CANCEL_BUTTON_PRESSED","window","location","reload","elementSelector","e","preventDefault","ModalForm","args","nosubmit","modalConfig","title","returnFocus","target","show"],"mappings":";;;;;;;yOAiC+B,CAACA,kBAAmBC,mBACzCC,KAAO,IAAIC,qBAAYC,SAASC,cAAcL,mBAAoBC,WAGxEC,KAAKI,iBAAiBJ,KAAKK,OAAOC,gBAAgB,KAC9CN,KAAKO,2BACK,gBACTC,KAAKC,YACLC,MAAM,SAIXV,KAAKI,iBAAiBJ,KAAKK,OAAOM,uBAAuB,IAAMC,OAAOC,SAASC,+BAS1D,CAACC,gBAAiBhB,aACvCG,SAASC,cAAcY,iBAAiBX,iBAAiB,SAAS,SAASY,GACvEA,EAAEC,uBACIjB,KAAO,IAAIkB,mBAAU,CACvBnB,UAAAA,UACAoB,KAAM,CAACC,UAAU,GACjBC,YAAa,CAACC,OAAO,mBAAU,uBAC/BC,YAAaP,EAAEQ,SAEnBxB,KAAKI,iBAAiBJ,KAAKK,OAAOC,gBAAgB,IAAMM,OAAOC,SAASC,WACxEd,KAAKyB"} \ No newline at end of file +{"version":3,"file":"private_files.min.js","sources":["../src/private_files.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 * Module to handle AJAX interactions with user private files\n *\n * @module core_user/private_files\n * @copyright 2020 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport DynamicForm from 'core_form/dynamicform';\nimport ModalForm from 'core_form/modalform';\nimport {getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\n\n/**\n * Initialize private files form as AJAX form\n *\n * @param {String} containerSelector\n * @param {String} formClass\n */\nexport const initDynamicForm = (containerSelector, formClass) => {\n const form = new DynamicForm(document.querySelector(containerSelector), formClass);\n\n // When form is saved, refresh it to remove validation errors, if any:\n form.addEventListener(form.events.FORM_SUBMITTED, () => {\n form.load();\n getString('changessaved')\n .then(addToast)\n .catch(null);\n });\n\n // Reload the page on cancel.\n form.addEventListener(form.events.CANCEL_BUTTON_PRESSED, () => window.location.reload());\n};\n\n/**\n * Initialize private files form as Modal form\n *\n * @param {String} elementSelector\n * @param {String} formClass\n */\nexport const initModal = (elementSelector, formClass) => {\n document.querySelector(elementSelector).addEventListener('click', function(e) {\n e.preventDefault();\n const form = new ModalForm({\n formClass,\n args: {nosubmit: true},\n modalConfig: {title: getString('privatefilesmanage')},\n returnFocus: e.target,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n });\n};\n"],"names":["containerSelector","formClass","form","DynamicForm","document","querySelector","addEventListener","events","FORM_SUBMITTED","load","then","addToast","catch","CANCEL_BUTTON_PRESSED","window","location","reload","elementSelector","e","preventDefault","ModalForm","args","nosubmit","modalConfig","title","returnFocus","target","show"],"mappings":";;;;;;;yOAiC+B,CAACA,kBAAmBC,mBACzCC,KAAO,IAAIC,qBAAYC,SAASC,cAAcL,mBAAoBC,WAGxEC,KAAKI,iBAAiBJ,KAAKK,OAAOC,gBAAgB,KAC9CN,KAAKO,0BACK,gBACTC,KAAKC,YACLC,MAAM,SAIXV,KAAKI,iBAAiBJ,KAAKK,OAAOM,uBAAuB,IAAMC,OAAOC,SAASC,+BAS1D,CAACC,gBAAiBhB,aACvCG,SAASC,cAAcY,iBAAiBX,iBAAiB,SAAS,SAASY,GACvEA,EAAEC,uBACIjB,KAAO,IAAIkB,mBAAU,CACvBnB,UAAAA,UACAoB,KAAM,CAACC,UAAU,GACjBC,YAAa,CAACC,OAAO,kBAAU,uBAC/BC,YAAaP,EAAEQ,SAEnBxB,KAAKI,iBAAiBJ,KAAKK,OAAOC,gBAAgB,IAAMM,OAAOC,SAASC,WACxEd,KAAKyB"} \ No newline at end of file diff --git a/user/amd/src/comboboxsearch/user.js b/user/amd/src/comboboxsearch/user.js index f38e749bbd8..9a99b07220b 100644 --- a/user/amd/src/comboboxsearch/user.js +++ b/user/amd/src/comboboxsearch/user.js @@ -21,7 +21,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ import search_combobox from 'core/comboboxsearch/search_combobox'; -import {get_strings as getStrings} from 'core/str'; +import {getStrings} from 'core/str'; import {renderForPromise, replaceNodeContents} from 'core/templates'; import $ from 'jquery'; import Notification from 'core/notification'; diff --git a/user/amd/src/edit_profile_fields.js b/user/amd/src/edit_profile_fields.js index da3bdeb46eb..6d1df7581b2 100644 --- a/user/amd/src/edit_profile_fields.js +++ b/user/amd/src/edit_profile_fields.js @@ -14,7 +14,7 @@ // along with Moodle. If not, see . import ModalForm from 'core_form/modalform'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; /** * User profile fields editor diff --git a/user/amd/src/form_user_selector.js b/user/amd/src/form_user_selector.js index e4ae85322c9..466a5f7fbac 100644 --- a/user/amd/src/form_user_selector.js +++ b/user/amd/src/form_user_selector.js @@ -23,7 +23,7 @@ import Ajax from 'core/ajax'; import {render as renderTemplate} from 'core/templates'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; /** * Load the list of users matching the query and render the selector labels for them. diff --git a/user/amd/src/private_files.js b/user/amd/src/private_files.js index 0b73757c0bb..9a03b64f571 100644 --- a/user/amd/src/private_files.js +++ b/user/amd/src/private_files.js @@ -22,7 +22,7 @@ */ import DynamicForm from 'core_form/dynamicform'; import ModalForm from 'core_form/modalform'; -import {get_string as getString} from 'core/str'; +import {getString} from 'core/str'; import {add as addToast} from 'core/toast'; /**