From cbda104d1464c72b3cdb77c93cde9f601a9d94bd Mon Sep 17 00:00:00 2001 From: toanlamt Date: Thu, 15 May 2025 15:11:54 +0700 Subject: [PATCH] MDL-85050 Javascript: core/dropzone Fix link & box click event conflict --- lib/amd/build/dropzone.min.js | 2 +- lib/amd/build/dropzone.min.js.map | 2 +- lib/amd/src/dropzone.js | 12 +++--------- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/lib/amd/build/dropzone.min.js b/lib/amd/build/dropzone.min.js index 80647e7bbb5..204a4269005 100644 --- a/lib/amd/build/dropzone.min.js +++ b/lib/amd/build/dropzone.min.js @@ -1,3 +1,3 @@ -define("core/dropzone",["exports","core/str","core/log","core/prefetch","core/templates"],(function(_exports,_str,_log,_prefetch,_templates){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,_log=_interopRequireDefault(_log),_templates=_interopRequireDefault(_templates);var _default=class{constructor(dropZoneElement,fileTypes,callback){_defineProperty(this,"dropZoneElement",void 0),_defineProperty(this,"fileTypes",void 0),_defineProperty(this,"callback",void 0),_defineProperty(this,"dropZoneLabel",""),(0,_prefetch.prefetchString)("core","addfilesdrop"),this.dropZoneElement=dropZoneElement,this.fileTypes=fileTypes,this.callback=callback}init(){return this.dropZoneElement.addEventListener("dragover",(e=>{const dropZone=this.getDropZoneFromEvent(e);dropZone&&(e.preventDefault(),dropZone.classList.add("dragover"))})),this.dropZoneElement.addEventListener("dragleave",(e=>{const dropZone=this.getDropZoneFromEvent(e);dropZone&&(e.preventDefault(),dropZone.classList.remove("dragover"))})),this.dropZoneElement.addEventListener("drop",(e=>{const dropZone=this.getDropZoneFromEvent(e);dropZone&&(e.preventDefault(),dropZone.classList.remove("dragover"),this.callback(e.dataTransfer.files))})),this.dropZoneElement.addEventListener("click",(e=>{this.getDropZoneContainerFromEvent(e)&&this.getFileElementFromEvent(e).click()})),this.dropZoneElement.addEventListener("click",(e=>{e.target.closest(".dropzone-sr-only-focusable")&&this.getFileElementFromEvent(e).click()})),this.dropZoneElement.addEventListener("change",(e=>{const fileInput=this.getFileElementFromEvent(e);fileInput&&(e.preventDefault(),this.callback(fileInput.files))})),this.renderDropZone(this.dropZoneElement,this.fileTypes),_log.default.info("Dropzone has been initialized!"),this}getDropZoneFromEvent(e){return e.target.closest(".dropzone")}getDropZoneContainerFromEvent(e){return e.target.closest(".dropzone-container")}getFileElementFromEvent(e){return e.target.closest(".dropzone-container").querySelector(".drop-zone-fileinput")}setLabel(label){this.dropZoneLabel=label}getLabel(){return this.dropZoneLabel}async renderDropZone(dropZoneElement,fileTypes){this.getLabel()||this.setLabel(await(0,_str.getString)("addfilesdrop","core"));const dropZoneLabel=this.getLabel();dropZoneElement.innerHTML=await _templates.default.render("core/dropzone",{label:dropZoneLabel,filetypes:fileTypes})}};return _exports.default=_default,_exports.default})); +define("core/dropzone",["exports","core/str","core/log","core/prefetch","core/templates"],(function(_exports,_str,_log,_prefetch,_templates){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,_log=_interopRequireDefault(_log),_templates=_interopRequireDefault(_templates);var _default=class{constructor(dropZoneElement,fileTypes,callback){_defineProperty(this,"dropZoneElement",void 0),_defineProperty(this,"fileTypes",void 0),_defineProperty(this,"callback",void 0),_defineProperty(this,"dropZoneLabel",""),(0,_prefetch.prefetchString)("core","addfilesdrop"),this.dropZoneElement=dropZoneElement,this.fileTypes=fileTypes,this.callback=callback}init(){return this.dropZoneElement.addEventListener("dragover",(e=>{const dropZone=this.getDropZoneFromEvent(e);dropZone&&(e.preventDefault(),dropZone.classList.add("dragover"))})),this.dropZoneElement.addEventListener("dragleave",(e=>{const dropZone=this.getDropZoneFromEvent(e);dropZone&&(e.preventDefault(),dropZone.classList.remove("dragover"))})),this.dropZoneElement.addEventListener("drop",(e=>{const dropZone=this.getDropZoneFromEvent(e);dropZone&&(e.preventDefault(),dropZone.classList.remove("dragover"),this.callback(e.dataTransfer.files))})),this.dropZoneElement.addEventListener("click",(e=>{(e.target.closest(".dropzone-sr-only-focusable")||this.getDropZoneContainerFromEvent(e))&&this.getFileElementFromEvent(e).click()})),this.dropZoneElement.addEventListener("change",(e=>{const fileInput=this.getFileElementFromEvent(e);fileInput&&(e.preventDefault(),this.callback(fileInput.files))})),this.renderDropZone(this.dropZoneElement,this.fileTypes),_log.default.info("Dropzone has been initialized!"),this}getDropZoneFromEvent(e){return e.target.closest(".dropzone")}getDropZoneContainerFromEvent(e){return e.target.closest(".dropzone-container")}getFileElementFromEvent(e){return e.target.closest(".dropzone-container").querySelector(".drop-zone-fileinput")}setLabel(label){this.dropZoneLabel=label}getLabel(){return this.dropZoneLabel}async renderDropZone(dropZoneElement,fileTypes){this.getLabel()||this.setLabel(await(0,_str.getString)("addfilesdrop","core"));const dropZoneLabel=this.getLabel();dropZoneElement.innerHTML=await _templates.default.render("core/dropzone",{label:dropZoneLabel,filetypes:fileTypes})}};return _exports.default=_default,_exports.default})); //# sourceMappingURL=dropzone.min.js.map \ No newline at end of file diff --git a/lib/amd/build/dropzone.min.js.map b/lib/amd/build/dropzone.min.js.map index 51b9ce41126..3884d4d1454 100644 --- a/lib/amd/build/dropzone.min.js.map +++ b/lib/amd/build/dropzone.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dropzone.min.js","sources":["../src/dropzone.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 to handle dropzone.\n *\n * @module core/dropzone\n * @copyright 2024 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.4\n */\n\nimport {getString} from 'core/str';\nimport Log from 'core/log';\nimport {prefetchString} from 'core/prefetch';\nimport Templates from 'core/templates';\n\n/**\n * A dropzone.\n *\n * @class core/dropzone\n */\nconst DropZone = class {\n\n /**\n * The element to render the dropzone.\n * @type {Element}\n */\n dropZoneElement;\n\n /**\n * The file types that are allowed to be uploaded.\n * @type {String}\n */\n fileTypes;\n\n /**\n * The function to call when a file is dropped.\n * @type {CallableFunction}\n */\n callback;\n\n /**\n * The label to display in the dropzone.\n * @type {string}\n */\n dropZoneLabel = '';\n\n /**\n * Constructor.\n *\n * @param {Element} dropZoneElement The element to render the dropzone.\n * @param {String} fileTypes The file types that are allowed to be uploaded. Example: image/*\n * @param {CallableFunction} callback The function to call when a file is dropped.\n */\n constructor(dropZoneElement, fileTypes, callback) {\n prefetchString('core', 'addfilesdrop');\n this.dropZoneElement = dropZoneElement;\n this.fileTypes = fileTypes;\n this.callback = callback;\n }\n\n /**\n * Initialise the dropzone.\n *\n * @returns {DropZone}\n */\n init() {\n this.dropZoneElement.addEventListener('dragover', (e) => {\n const dropZone = this.getDropZoneFromEvent(e);\n if (!dropZone) {\n return;\n }\n e.preventDefault();\n dropZone.classList.add('dragover');\n });\n this.dropZoneElement.addEventListener('dragleave', (e) => {\n const dropZone = this.getDropZoneFromEvent(e);\n if (!dropZone) {\n return;\n }\n e.preventDefault();\n dropZone.classList.remove('dragover');\n });\n this.dropZoneElement.addEventListener('drop', (e) => {\n const dropZone = this.getDropZoneFromEvent(e);\n if (!dropZone) {\n return;\n }\n e.preventDefault();\n dropZone.classList.remove('dragover');\n this.callback(e.dataTransfer.files);\n });\n this.dropZoneElement.addEventListener('click', (e) => {\n const dropZoneContainer = this.getDropZoneContainerFromEvent(e);\n if (!dropZoneContainer) {\n return;\n }\n this.getFileElementFromEvent(e).click();\n });\n this.dropZoneElement.addEventListener('click', (e) => {\n const dropZoneLabel = e.target.closest('.dropzone-sr-only-focusable');\n if (!dropZoneLabel) {\n return;\n }\n this.getFileElementFromEvent(e).click();\n });\n this.dropZoneElement.addEventListener('change', (e) => {\n const fileInput = this.getFileElementFromEvent(e);\n if (fileInput) {\n e.preventDefault();\n this.callback(fileInput.files);\n }\n });\n this.renderDropZone(this.dropZoneElement, this.fileTypes);\n Log.info('Dropzone has been initialized!');\n return this;\n }\n\n /**\n * Get the dropzone.\n *\n * @param {Event} e The event.\n * @returns {HTMLElement|bool}\n */\n getDropZoneFromEvent(e) {\n return e.target.closest('.dropzone');\n }\n\n /**\n * Get the dropzone container.\n *\n * @param {Event} e The event.\n * @returns {HTMLElement|bool}\n */\n getDropZoneContainerFromEvent(e) {\n return e.target.closest('.dropzone-container');\n }\n\n /**\n * Get the file element.\n *\n * @param {Event} e The event.\n * @returns {HTMLElement|bool}\n */\n getFileElementFromEvent(e) {\n return e.target.closest('.dropzone-container').querySelector('.drop-zone-fileinput');\n }\n\n /**\n * Set the label to display in the dropzone.\n *\n * @param {String} label The label to display in the dropzone.\n */\n setLabel(label) {\n this.dropZoneLabel = label;\n }\n\n /**\n * Get the label to display in the dropzone.\n *\n * @return {String} The label to display in the dropzone.\n */\n getLabel() {\n return this.dropZoneLabel;\n }\n\n /**\n * Render the dropzone.\n *\n * @param {Element} dropZoneElement The element to render the dropzone.\n * @param {String} fileTypes The file types that are allowed to be uploaded.\n * @returns {Promise}\n */\n async renderDropZone(dropZoneElement, fileTypes) {\n if (!this.getLabel()) {\n // Use the default one.\n this.setLabel(await getString('addfilesdrop', 'core'));\n }\n const dropZoneLabel = this.getLabel();\n dropZoneElement.innerHTML = await Templates.render('core/dropzone', {\n label: dropZoneLabel,\n filetypes: fileTypes,\n });\n }\n};\n\nexport default DropZone;\n"],"names":["constructor","dropZoneElement","fileTypes","callback","init","addEventListener","e","dropZone","this","getDropZoneFromEvent","preventDefault","classList","add","remove","dataTransfer","files","getDropZoneContainerFromEvent","getFileElementFromEvent","click","target","closest","fileInput","renderDropZone","info","querySelector","setLabel","label","dropZoneLabel","getLabel","innerHTML","Templates","render","filetypes"],"mappings":"gjBAkCiB,MAiCbA,YAAYC,gBAAiBC,UAAWC,+KATxB,iCAUG,OAAQ,qBAClBF,gBAAkBA,qBAClBC,UAAYA,eACZC,SAAWA,SAQpBC,mBACSH,gBAAgBI,iBAAiB,YAAaC,UACzCC,SAAWC,KAAKC,qBAAqBH,GACtCC,WAGLD,EAAEI,iBACFH,SAASI,UAAUC,IAAI,qBAEtBX,gBAAgBI,iBAAiB,aAAcC,UAC1CC,SAAWC,KAAKC,qBAAqBH,GACtCC,WAGLD,EAAEI,iBACFH,SAASI,UAAUE,OAAO,qBAEzBZ,gBAAgBI,iBAAiB,QAASC,UACrCC,SAAWC,KAAKC,qBAAqBH,GACtCC,WAGLD,EAAEI,iBACFH,SAASI,UAAUE,OAAO,iBACrBV,SAASG,EAAEQ,aAAaC,gBAE5Bd,gBAAgBI,iBAAiB,SAAUC,IAClBE,KAAKQ,8BAA8BV,SAIxDW,wBAAwBX,GAAGY,gBAE/BjB,gBAAgBI,iBAAiB,SAAUC,IACtBA,EAAEa,OAAOC,QAAQ,qCAIlCH,wBAAwBX,GAAGY,gBAE/BjB,gBAAgBI,iBAAiB,UAAWC,UACvCe,UAAYb,KAAKS,wBAAwBX,GAC3Ce,YACAf,EAAEI,sBACGP,SAASkB,UAAUN,gBAG3BO,eAAed,KAAKP,gBAAiBO,KAAKN,wBAC3CqB,KAAK,kCACFf,KASXC,qBAAqBH,UACVA,EAAEa,OAAOC,QAAQ,aAS5BJ,8BAA8BV,UACnBA,EAAEa,OAAOC,QAAQ,uBAS5BH,wBAAwBX,UACbA,EAAEa,OAAOC,QAAQ,uBAAuBI,cAAc,wBAQjEC,SAASC,YACAC,cAAgBD,MAQzBE,kBACWpB,KAAKmB,mCAUK1B,gBAAiBC,WAC7BM,KAAKoB,iBAEDH,eAAe,kBAAU,eAAgB,eAE5CE,cAAgBnB,KAAKoB,WAC3B3B,gBAAgB4B,gBAAkBC,mBAAUC,OAAO,gBAAiB,CAChEL,MAAOC,cACPK,UAAW9B"} \ No newline at end of file +{"version":3,"file":"dropzone.min.js","sources":["../src/dropzone.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 to handle dropzone.\n *\n * @module core/dropzone\n * @copyright 2024 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.4\n */\n\nimport {getString} from 'core/str';\nimport Log from 'core/log';\nimport {prefetchString} from 'core/prefetch';\nimport Templates from 'core/templates';\n\n/**\n * A dropzone.\n *\n * @class core/dropzone\n */\nconst DropZone = class {\n\n /**\n * The element to render the dropzone.\n * @type {Element}\n */\n dropZoneElement;\n\n /**\n * The file types that are allowed to be uploaded.\n * @type {String}\n */\n fileTypes;\n\n /**\n * The function to call when a file is dropped.\n * @type {CallableFunction}\n */\n callback;\n\n /**\n * The label to display in the dropzone.\n * @type {string}\n */\n dropZoneLabel = '';\n\n /**\n * Constructor.\n *\n * @param {Element} dropZoneElement The element to render the dropzone.\n * @param {String} fileTypes The file types that are allowed to be uploaded. Example: image/*\n * @param {CallableFunction} callback The function to call when a file is dropped.\n */\n constructor(dropZoneElement, fileTypes, callback) {\n prefetchString('core', 'addfilesdrop');\n this.dropZoneElement = dropZoneElement;\n this.fileTypes = fileTypes;\n this.callback = callback;\n }\n\n /**\n * Initialise the dropzone.\n *\n * @returns {DropZone}\n */\n init() {\n this.dropZoneElement.addEventListener('dragover', (e) => {\n const dropZone = this.getDropZoneFromEvent(e);\n if (!dropZone) {\n return;\n }\n e.preventDefault();\n dropZone.classList.add('dragover');\n });\n this.dropZoneElement.addEventListener('dragleave', (e) => {\n const dropZone = this.getDropZoneFromEvent(e);\n if (!dropZone) {\n return;\n }\n e.preventDefault();\n dropZone.classList.remove('dragover');\n });\n this.dropZoneElement.addEventListener('drop', (e) => {\n const dropZone = this.getDropZoneFromEvent(e);\n if (!dropZone) {\n return;\n }\n e.preventDefault();\n dropZone.classList.remove('dragover');\n this.callback(e.dataTransfer.files);\n });\n this.dropZoneElement.addEventListener('click', (e) => {\n const targetDropZoneElement = e.target.closest('.dropzone-sr-only-focusable') ||\n this.getDropZoneContainerFromEvent(e);\n if (!targetDropZoneElement) {\n return;\n }\n this.getFileElementFromEvent(e).click();\n });\n this.dropZoneElement.addEventListener('change', (e) => {\n const fileInput = this.getFileElementFromEvent(e);\n if (fileInput) {\n e.preventDefault();\n this.callback(fileInput.files);\n }\n });\n this.renderDropZone(this.dropZoneElement, this.fileTypes);\n Log.info('Dropzone has been initialized!');\n return this;\n }\n\n /**\n * Get the dropzone.\n *\n * @param {Event} e The event.\n * @returns {HTMLElement|bool}\n */\n getDropZoneFromEvent(e) {\n return e.target.closest('.dropzone');\n }\n\n /**\n * Get the dropzone container.\n *\n * @param {Event} e The event.\n * @returns {HTMLElement|bool}\n */\n getDropZoneContainerFromEvent(e) {\n return e.target.closest('.dropzone-container');\n }\n\n /**\n * Get the file element.\n *\n * @param {Event} e The event.\n * @returns {HTMLElement|bool}\n */\n getFileElementFromEvent(e) {\n return e.target.closest('.dropzone-container').querySelector('.drop-zone-fileinput');\n }\n\n /**\n * Set the label to display in the dropzone.\n *\n * @param {String} label The label to display in the dropzone.\n */\n setLabel(label) {\n this.dropZoneLabel = label;\n }\n\n /**\n * Get the label to display in the dropzone.\n *\n * @return {String} The label to display in the dropzone.\n */\n getLabel() {\n return this.dropZoneLabel;\n }\n\n /**\n * Render the dropzone.\n *\n * @param {Element} dropZoneElement The element to render the dropzone.\n * @param {String} fileTypes The file types that are allowed to be uploaded.\n * @returns {Promise}\n */\n async renderDropZone(dropZoneElement, fileTypes) {\n if (!this.getLabel()) {\n // Use the default one.\n this.setLabel(await getString('addfilesdrop', 'core'));\n }\n const dropZoneLabel = this.getLabel();\n dropZoneElement.innerHTML = await Templates.render('core/dropzone', {\n label: dropZoneLabel,\n filetypes: fileTypes,\n });\n }\n};\n\nexport default DropZone;\n"],"names":["constructor","dropZoneElement","fileTypes","callback","init","addEventListener","e","dropZone","this","getDropZoneFromEvent","preventDefault","classList","add","remove","dataTransfer","files","target","closest","getDropZoneContainerFromEvent","getFileElementFromEvent","click","fileInput","renderDropZone","info","querySelector","setLabel","label","dropZoneLabel","getLabel","innerHTML","Templates","render","filetypes"],"mappings":"gjBAkCiB,MAiCbA,YAAYC,gBAAiBC,UAAWC,+KATxB,iCAUG,OAAQ,qBAClBF,gBAAkBA,qBAClBC,UAAYA,eACZC,SAAWA,SAQpBC,mBACSH,gBAAgBI,iBAAiB,YAAaC,UACzCC,SAAWC,KAAKC,qBAAqBH,GACtCC,WAGLD,EAAEI,iBACFH,SAASI,UAAUC,IAAI,qBAEtBX,gBAAgBI,iBAAiB,aAAcC,UAC1CC,SAAWC,KAAKC,qBAAqBH,GACtCC,WAGLD,EAAEI,iBACFH,SAASI,UAAUE,OAAO,qBAEzBZ,gBAAgBI,iBAAiB,QAASC,UACrCC,SAAWC,KAAKC,qBAAqBH,GACtCC,WAGLD,EAAEI,iBACFH,SAASI,UAAUE,OAAO,iBACrBV,SAASG,EAAEQ,aAAaC,gBAE5Bd,gBAAgBI,iBAAiB,SAAUC,KACdA,EAAEU,OAAOC,QAAQ,gCAC3CT,KAAKU,8BAA8BZ,UAIlCa,wBAAwBb,GAAGc,gBAE/BnB,gBAAgBI,iBAAiB,UAAWC,UACvCe,UAAYb,KAAKW,wBAAwBb,GAC3Ce,YACAf,EAAEI,sBACGP,SAASkB,UAAUN,gBAG3BO,eAAed,KAAKP,gBAAiBO,KAAKN,wBAC3CqB,KAAK,kCACFf,KASXC,qBAAqBH,UACVA,EAAEU,OAAOC,QAAQ,aAS5BC,8BAA8BZ,UACnBA,EAAEU,OAAOC,QAAQ,uBAS5BE,wBAAwBb,UACbA,EAAEU,OAAOC,QAAQ,uBAAuBO,cAAc,wBAQjEC,SAASC,YACAC,cAAgBD,MAQzBE,kBACWpB,KAAKmB,mCAUK1B,gBAAiBC,WAC7BM,KAAKoB,iBAEDH,eAAe,kBAAU,eAAgB,eAE5CE,cAAgBnB,KAAKoB,WAC3B3B,gBAAgB4B,gBAAkBC,mBAAUC,OAAO,gBAAiB,CAChEL,MAAOC,cACPK,UAAW9B"} \ No newline at end of file diff --git a/lib/amd/src/dropzone.js b/lib/amd/src/dropzone.js index 9bc8d99d1d6..27117ae4dae 100644 --- a/lib/amd/src/dropzone.js +++ b/lib/amd/src/dropzone.js @@ -104,15 +104,9 @@ const DropZone = class { this.callback(e.dataTransfer.files); }); this.dropZoneElement.addEventListener('click', (e) => { - const dropZoneContainer = this.getDropZoneContainerFromEvent(e); - if (!dropZoneContainer) { - return; - } - this.getFileElementFromEvent(e).click(); - }); - this.dropZoneElement.addEventListener('click', (e) => { - const dropZoneLabel = e.target.closest('.dropzone-sr-only-focusable'); - if (!dropZoneLabel) { + const targetDropZoneElement = e.target.closest('.dropzone-sr-only-focusable') || + this.getDropZoneContainerFromEvent(e); + if (!targetDropZoneElement) { return; } this.getFileElementFromEvent(e).click();