From 087b286a166d504d67f379df8b003a41dbe5a23c Mon Sep 17 00:00:00 2001 From: meirzamoodle Date: Tue, 6 Feb 2024 23:08:51 +0700 Subject: [PATCH] MDL-78096 tiny_media: Implement external image URL --- .../media/amd/build/imagehelpers.min.js | 2 +- .../media/amd/build/imagehelpers.min.js.map | 2 +- .../media/amd/build/imageinsert.min.js | 2 +- .../media/amd/build/imageinsert.min.js.map | 2 +- .../plugins/media/amd/build/selectors.min.js | 2 +- .../media/amd/build/selectors.min.js.map | 2 +- .../plugins/media/amd/src/imagehelpers.js | 44 ++++++++ .../tiny/plugins/media/amd/src/imageinsert.js | 106 ++++++++++++++++++ .../tiny/plugins/media/amd/src/selectors.js | 4 + .../tiny/plugins/media/lang/en/tiny_media.php | 2 +- 10 files changed, 161 insertions(+), 7 deletions(-) diff --git a/lib/editor/tiny/plugins/media/amd/build/imagehelpers.min.js b/lib/editor/tiny/plugins/media/amd/build/imagehelpers.min.js index f139395658d..338bd72b8e1 100644 --- a/lib/editor/tiny/plugins/media/amd/build/imagehelpers.min.js +++ b/lib/editor/tiny/plugins/media/amd/build/imagehelpers.min.js @@ -5,6 +5,6 @@ define("tiny_media/imagehelpers",["exports","core/templates"],(function(_exports * @module tiny_media/imagehelpers * @copyright 2024 Meirza * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.footerImageInsert=_exports.bodyImageInsert=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj};_exports.bodyImageInsert=async(templateContext,root)=>_templates.default.renderForPromise("tiny_media/insert_image_modal_insert",{...templateContext}).then((_ref=>{let{html:html,js:js}=_ref;_templates.default.replaceNodeContents(root.querySelector(".tiny_image_body_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.footerImageInsert=async(templateContext,root)=>_templates.default.renderForPromise("tiny_media/insert_image_modal_insert_footer",{...templateContext}).then((_ref2=>{let{html:html,js:js}=_ref2;_templates.default.replaceNodeContents(root.querySelector(".tiny_image_footer_template"),html,js)})).catch((error=>{window.console.log(error)}))})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showElements=_exports.hideElements=_exports.footerImageInsert=_exports.bodyImageInsert=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj};_exports.bodyImageInsert=async(templateContext,root)=>_templates.default.renderForPromise("tiny_media/insert_image_modal_insert",{...templateContext}).then((_ref=>{let{html:html,js:js}=_ref;_templates.default.replaceNodeContents(root.querySelector(".tiny_image_body_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.footerImageInsert=async(templateContext,root)=>_templates.default.renderForPromise("tiny_media/insert_image_modal_insert_footer",{...templateContext}).then((_ref2=>{let{html:html,js:js}=_ref2;_templates.default.replaceNodeContents(root.querySelector(".tiny_image_footer_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.showElements=(elements,root)=>{if(elements instanceof Array)elements.forEach((elementSelector=>{const element=root.querySelector(elementSelector);element&&element.classList.remove("d-none")}));else{const element=root.querySelector(elements);element&&element.classList.remove("d-none")}};_exports.hideElements=(elements,root)=>{if(elements instanceof Array)elements.forEach((elementSelector=>{const element=root.querySelector(elementSelector);element&&element.classList.add("d-none")}));else{const element=root.querySelector(elements);element&&element.classList.add("d-none")}}})); //# sourceMappingURL=imagehelpers.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/imagehelpers.min.js.map b/lib/editor/tiny/plugins/media/amd/build/imagehelpers.min.js.map index 1ee2bff303a..294d3a87dde 100644 --- a/lib/editor/tiny/plugins/media/amd/build/imagehelpers.min.js.map +++ b/lib/editor/tiny/plugins/media/amd/build/imagehelpers.min.js.map @@ -1 +1 @@ -{"version":3,"file":"imagehelpers.min.js","sources":["../src/imagehelpers.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 helpers.\n *\n * @module tiny_media/imagehelpers\n * @copyright 2024 Meirza \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\n\n/**\n * Renders and inserts the body template for inserting an image into the modal.\n *\n * @param {object} templateContext - The context for rendering the template.\n * @param {HTMLElement} root - The root element where the template will be inserted.\n * @returns {Promise}\n */\nexport const bodyImageInsert = async(templateContext, root) => {\n return Templates.renderForPromise('tiny_media/insert_image_modal_insert', {...templateContext})\n .then(({html, js}) => {\n Templates.replaceNodeContents(root.querySelector('.tiny_image_body_template'), html, js);\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n};\n\n/**\n * Renders and inserts the footer template for inserting an image into the modal.\n *\n * @param {object} templateContext - The context for rendering the template.\n * @param {HTMLElement} root - The root element where the template will be inserted.\n * @returns {Promise}\n */\nexport const footerImageInsert = async(templateContext, root) => {\n return Templates.renderForPromise('tiny_media/insert_image_modal_insert_footer', {...templateContext})\n .then(({html, js}) => {\n Templates.replaceNodeContents(root.querySelector('.tiny_image_footer_template'), html, js);\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n};\n"],"names":["async","templateContext","root","Templates","renderForPromise","then","_ref","html","js","replaceNodeContents","querySelector","catch","error","window","console","log","_ref2"],"mappings":";;;;;;;+MAgC+BA,MAAMC,gBAAiBC,OAC3CC,mBAAUC,iBAAiB,uCAAwC,IAAIH,kBAC7EI,MAAKC,WAACC,KAACA,KAADC,GAAOA,4BACAC,oBAAoBP,KAAKQ,cAAc,6BAA8BH,KAAMC,OAGxFG,OAAMC,QACHC,OAAOC,QAAQC,IAAIH,qCAWMZ,MAAMC,gBAAiBC,OAC7CC,mBAAUC,iBAAiB,8CAA+C,IAAIH,kBACpFI,MAAKW,YAACT,KAACA,KAADC,GAAOA,6BACAC,oBAAoBP,KAAKQ,cAAc,+BAAgCH,KAAMC,OAG1FG,OAAMC,QACHC,OAAOC,QAAQC,IAAIH"} \ No newline at end of file +{"version":3,"file":"imagehelpers.min.js","sources":["../src/imagehelpers.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 helpers.\n *\n * @module tiny_media/imagehelpers\n * @copyright 2024 Meirza \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\n\n/**\n * Renders and inserts the body template for inserting an image into the modal.\n *\n * @param {object} templateContext - The context for rendering the template.\n * @param {HTMLElement} root - The root element where the template will be inserted.\n * @returns {Promise}\n */\nexport const bodyImageInsert = async(templateContext, root) => {\n return Templates.renderForPromise('tiny_media/insert_image_modal_insert', {...templateContext})\n .then(({html, js}) => {\n Templates.replaceNodeContents(root.querySelector('.tiny_image_body_template'), html, js);\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n};\n\n/**\n * Renders and inserts the footer template for inserting an image into the modal.\n *\n * @param {object} templateContext - The context for rendering the template.\n * @param {HTMLElement} root - The root element where the template will be inserted.\n * @returns {Promise}\n */\nexport const footerImageInsert = async(templateContext, root) => {\n return Templates.renderForPromise('tiny_media/insert_image_modal_insert_footer', {...templateContext})\n .then(({html, js}) => {\n Templates.replaceNodeContents(root.querySelector('.tiny_image_footer_template'), html, js);\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n};\n\n/**\n * Show the element(s).\n *\n * @param {string|string[]} elements - The CSS selector for the elements to toggle.\n * @param {object} root - The CSS selector for the elements to toggle.\n */\nexport const showElements = (elements, root) => {\n if (elements instanceof Array) {\n elements.forEach((elementSelector) => {\n const element = root.querySelector(elementSelector);\n if (element) {\n element.classList.remove('d-none');\n }\n });\n } else {\n const element = root.querySelector(elements);\n if (element) {\n element.classList.remove('d-none');\n }\n }\n};\n\n/**\n * Hide the element(s).\n *\n * @param {string|string[]} elements - The CSS selector for the elements to toggle.\n * @param {object} root - The CSS selector for the elements to toggle.\n */\nexport const hideElements = (elements, root) => {\n if (elements instanceof Array) {\n elements.forEach((elementSelector) => {\n const element = root.querySelector(elementSelector);\n if (element) {\n element.classList.add('d-none');\n }\n });\n } else {\n const element = root.querySelector(elements);\n if (element) {\n element.classList.add('d-none');\n }\n }\n};\n"],"names":["async","templateContext","root","Templates","renderForPromise","then","_ref","html","js","replaceNodeContents","querySelector","catch","error","window","console","log","_ref2","elements","Array","forEach","elementSelector","element","classList","remove","add"],"mappings":";;;;;;;2PAgC+BA,MAAMC,gBAAiBC,OAC3CC,mBAAUC,iBAAiB,uCAAwC,IAAIH,kBAC7EI,MAAKC,WAACC,KAACA,KAADC,GAAOA,4BACAC,oBAAoBP,KAAKQ,cAAc,6BAA8BH,KAAMC,OAGxFG,OAAMC,QACHC,OAAOC,QAAQC,IAAIH,qCAWMZ,MAAMC,gBAAiBC,OAC7CC,mBAAUC,iBAAiB,8CAA+C,IAAIH,kBACpFI,MAAKW,YAACT,KAACA,KAADC,GAAOA,6BACAC,oBAAoBP,KAAKQ,cAAc,+BAAgCH,KAAMC,OAG1FG,OAAMC,QACHC,OAAOC,QAAQC,IAAIH,gCAUC,CAACK,SAAUf,WAC/Be,oBAAoBC,MACpBD,SAASE,SAASC,wBACRC,QAAUnB,KAAKQ,cAAcU,iBAC/BC,SACAA,QAAQC,UAAUC,OAAO,iBAG9B,OACGF,QAAUnB,KAAKQ,cAAcO,UAC/BI,SACAA,QAAQC,UAAUC,OAAO,kCAWT,CAACN,SAAUf,WAC/Be,oBAAoBC,MACpBD,SAASE,SAASC,wBACRC,QAAUnB,KAAKQ,cAAcU,iBAC/BC,SACAA,QAAQC,UAAUE,IAAI,iBAG3B,OACGH,QAAUnB,KAAKQ,cAAcO,UAC/BI,SACAA,QAAQC,UAAUE,IAAI"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/imageinsert.min.js b/lib/editor/tiny/plugins/media/amd/build/imageinsert.min.js index d0315dcd499..0333b8e1af3 100644 --- a/lib/editor/tiny/plugins/media/amd/build/imageinsert.min.js +++ b/lib/editor/tiny/plugins/media/amd/build/imageinsert.min.js @@ -1,3 +1,3 @@ -define("tiny_media/imageinsert",["exports","core/prefetch","core/str","./common"],(function(_exports,_prefetch,_str,_common){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.ImageInsert=void 0,(0,_prefetch.prefetchStrings)("tiny_media",["insertimage","enterurl","enterurlor","imageurlrequired","uploading","loading"]);_exports.ImageInsert=class{constructor(root,editor,currentModal,canShowFilePicker,canShowDropZone){var obj,key,value;value=async function(){const langStringKeys=["insertimage","enterurl","enterurlor","imageurlrequired","uploading","loading"],langStringvalues=await(0,_str.getStrings)([...langStringKeys].map((key=>({key:key,component:_common.component}))));this.langStrings=Object.fromEntries(langStringKeys.map(((key,index)=>[key,langStringvalues[index]]))),this.currentModal.setTitle(this.langStrings.insertimage)},(key="init")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.root=root,this.editor=editor,this.currentModal=currentModal,this.canShowFilePicker=canShowFilePicker,this.canShowDropZone=canShowDropZone}}})); +define("tiny_media/imageinsert",["exports","./selectors","core/prefetch","core/str","./common","tiny_media/imagehelpers"],(function(_exports,_selectors,_prefetch,_str,_common,_imagehelpers){var 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.ImageInsert=void 0,_selectors=(obj=_selectors)&&obj.__esModule?obj:{default:obj},(0,_prefetch.prefetchStrings)("tiny_media",["insertimage","enterurl","enterurlor","imageurlrequired","uploading","loading"]);_exports.ImageInsert=class{constructor(root,editor,currentModal,canShowFilePicker,canShowDropZone){_defineProperty(this,"init",(async function(){const langStringKeys=["insertimage","enterurl","enterurlor","imageurlrequired","uploading","loading"],langStringvalues=await(0,_str.getStrings)([...langStringKeys].map((key=>({key:key,component:_common.component}))));this.langStrings=Object.fromEntries(langStringKeys.map(((key,index)=>[key,langStringvalues[index]]))),this.currentModal.setTitle(this.langStrings.insertimage),await this.registerEventListeners()})),_defineProperty(this,"isValidUrl",(urlString=>!!new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3})|localhost)(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*").test(urlString))),_defineProperty(this,"loadPreviewImage",(function(url){this.startImageLoading(),this.currentUrl=url;const image=new Image;image.src=url,image.addEventListener("error",(()=>{this.root.querySelector(_selectors.default.IMAGE.elements.urlWarning).innerHTML=this.langStrings.imageurlrequired,(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),this.currentUrl="",this.stopImageLoading()})),image.addEventListener("load",(()=>{window.console.log(this.currentUrl),this.stopImageLoading()}))})),this.root=root,this.editor=editor,this.currentModal=currentModal,this.canShowFilePicker=canShowFilePicker,this.canShowDropZone=canShowDropZone}toggleUrlButton(){const url=this.root.querySelector(_selectors.default.IMAGE.elements.url).value;this.root.querySelector(_selectors.default.IMAGE.actions.addUrl).disabled=!(""!==url&&this.isValidUrl(url))}urlChanged(){(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.urlWarning,this.root);const input=this.root.querySelector(_selectors.default.IMAGE.elements.url);input.value&&input.value!==this.currentUrl&&this.loadPreviewImage(input.value)}startImageLoading(){(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root);const elementsToHide=[_selectors.default.IMAGE.elements.insertImage,_selectors.default.IMAGE.elements.urlWarning,_selectors.default.IMAGE.elements.modalFooter];(0,_imagehelpers.hideElements)(elementsToHide,this.root)}stopImageLoading(){(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root);const elementsToShow=[_selectors.default.IMAGE.elements.insertImage,_selectors.default.IMAGE.elements.modalFooter];(0,_imagehelpers.showElements)(elementsToShow,this.root)}registerEventListeners(){this.root.addEventListener("click",(async e=>{e.target.closest(_selectors.default.IMAGE.actions.addUrl)&&this.urlChanged()})),this.root.addEventListener("input",(e=>{e.target.closest(_selectors.default.IMAGE.elements.url)&&this.toggleUrlButton()}))}}})); //# sourceMappingURL=imageinsert.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/imageinsert.min.js.map b/lib/editor/tiny/plugins/media/amd/build/imageinsert.min.js.map index b548e43aedb..205b1718e72 100644 --- a/lib/editor/tiny/plugins/media/amd/build/imageinsert.min.js.map +++ b/lib/editor/tiny/plugins/media/amd/build/imageinsert.min.js.map @@ -1 +1 @@ -{"version":3,"file":"imageinsert.min.js","sources":["../src/imageinsert.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 insertion class for Moodle.\n *\n * @module tiny_media/imageinsert\n * @copyright 2024 Meirza \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {prefetchStrings} from 'core/prefetch';\nimport {getStrings} from 'core/str';\nimport {component} from \"./common\";\n\nprefetchStrings('tiny_media', [\n 'insertimage',\n 'enterurl',\n 'enterurlor',\n 'imageurlrequired',\n 'uploading',\n 'loading',\n]);\n\nexport class ImageInsert {\n\n constructor(\n root,\n editor,\n currentModal,\n canShowFilePicker,\n canShowDropZone,\n ) {\n this.root = root;\n this.editor = editor;\n this.currentModal = currentModal;\n this.canShowFilePicker = canShowFilePicker;\n this.canShowDropZone = canShowDropZone;\n }\n\n init = async function() {\n // Get the localization lang strings and turn them into object.\n const langStringKeys = [\n 'insertimage',\n 'enterurl',\n 'enterurlor',\n 'imageurlrequired',\n 'uploading',\n 'loading',\n ];\n const langStringvalues = await getStrings([...langStringKeys].map((key) => ({key, component})));\n\n // Convert array to object.\n this.langStrings = Object.fromEntries(langStringKeys.map((key, index) => [key, langStringvalues[index]]));\n this.currentModal.setTitle(this.langStrings.insertimage);\n };\n}"],"names":["constructor","root","editor","currentModal","canShowFilePicker","canShowDropZone","async","langStringKeys","langStringvalues","map","key","component","langStrings","Object","fromEntries","index","setTitle","this","insertimage"],"mappings":"+OA2BgB,aAAc,CAC1B,cACA,WACA,aACA,mBACA,YACA,uCAKAA,YACIC,KACAC,OACAC,aACAC,kBACAC,yCASGC,uBAEGC,eAAiB,CACnB,cACA,WACA,aACA,mBACA,YACA,WAEEC,uBAAyB,mBAAW,IAAID,gBAAgBE,KAAKC,OAAUA,IAAAA,IAAKC,UAAAA,4BAG7EC,YAAcC,OAAOC,YAAYP,eAAeE,KAAI,CAACC,IAAKK,QAAU,CAACL,IAAKF,iBAAiBO,gBAC3FZ,aAAaa,SAASC,KAAKL,YAAYM,kJArBvCjB,KAAOA,UACPC,OAASA,YACTC,aAAeA,kBACfC,kBAAoBA,uBACpBC,gBAAkBA"} \ No newline at end of file +{"version":3,"file":"imageinsert.min.js","sources":["../src/imageinsert.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 insertion class for Moodle.\n *\n * @module tiny_media/imageinsert\n * @copyright 2024 Meirza \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Selectors from './selectors';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getStrings} from 'core/str';\nimport {component} from \"./common\";\nimport {\n showElements,\n hideElements,\n} from 'tiny_media/imagehelpers';\n\nprefetchStrings('tiny_media', [\n 'insertimage',\n 'enterurl',\n 'enterurlor',\n 'imageurlrequired',\n 'uploading',\n 'loading',\n]);\n\nexport class ImageInsert {\n\n constructor(\n root,\n editor,\n currentModal,\n canShowFilePicker,\n canShowDropZone,\n ) {\n this.root = root;\n this.editor = editor;\n this.currentModal = currentModal;\n this.canShowFilePicker = canShowFilePicker;\n this.canShowDropZone = canShowDropZone;\n }\n\n init = async function() {\n // Get the localization lang strings and turn them into object.\n const langStringKeys = [\n 'insertimage',\n 'enterurl',\n 'enterurlor',\n 'imageurlrequired',\n 'uploading',\n 'loading',\n ];\n const langStringvalues = await getStrings([...langStringKeys].map((key) => ({key, component})));\n\n // Convert array to object.\n this.langStrings = Object.fromEntries(langStringKeys.map((key, index) => [key, langStringvalues[index]]));\n this.currentModal.setTitle(this.langStrings.insertimage);\n await this.registerEventListeners();\n };\n\n /**\n * Enables or disables the URL-related buttons in the footer based on the current URL and input value.\n */\n toggleUrlButton() {\n const urlInput = this.root.querySelector(Selectors.IMAGE.elements.url);\n const url = urlInput.value;\n const addUrl = this.root.querySelector(Selectors.IMAGE.actions.addUrl);\n addUrl.disabled = !(url !== \"\" && this.isValidUrl(url));\n }\n\n /**\n * Check if given string is a valid URL.\n *\n * @param {String} urlString URL the link will point to.\n * @returns {boolean} True is valid, otherwise false.\n */\n isValidUrl = urlString => {\n const urlPattern = new RegExp('^(https?:\\\\/\\\\/)?' + // Protocol.\n '((([a-z\\\\d]([a-z\\\\d-]*[a-z\\\\d])*)\\\\.)+[a-z]{2,}|' + // Domain name.\n '((\\\\d{1,3}\\\\.){3}\\\\d{1,3})|localhost)' + // OR ip (v4) address, localhost.\n '(\\\\:\\\\d+)?(\\\\/[-a-z\\\\d%_.~+]*)*'); // Port and path.\n return !!urlPattern.test(urlString);\n };\n\n /**\n * Handles changes in the image URL input field and loads a preview of the image if the URL has changed.\n */\n urlChanged() {\n hideElements(Selectors.IMAGE.elements.urlWarning, this.root);\n const input = this.root.querySelector(Selectors.IMAGE.elements.url);\n if (input.value && input.value !== this.currentUrl) {\n this.loadPreviewImage(input.value);\n }\n }\n\n /**\n * Loads and displays a preview image based on the provided URL, and handles image loading events.\n *\n * @param {string} url - The URL of the image to load and display.\n */\n loadPreviewImage = function(url) {\n this.startImageLoading();\n this.currentUrl = url;\n const image = new Image();\n image.src = url;\n image.addEventListener('error', () => {\n const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);\n urlWarningLabelEle.innerHTML = this.langStrings.imageurlrequired;\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n this.currentUrl = \"\";\n this.stopImageLoading();\n });\n\n image.addEventListener('load', () => {\n window.console.log(this.currentUrl);\n this.stopImageLoading();\n });\n };\n\n /**\n * Displays the upload loader and disables UI elements while loading a file.\n */\n startImageLoading() {\n showElements(Selectors.IMAGE.elements.loaderIcon, this.root);\n const elementsToHide = [\n Selectors.IMAGE.elements.insertImage,\n Selectors.IMAGE.elements.urlWarning,\n Selectors.IMAGE.elements.modalFooter,\n ];\n hideElements(elementsToHide, this.root);\n }\n\n /**\n * Displays the upload loader and disables UI elements while loading a file.\n */\n stopImageLoading() {\n hideElements(Selectors.IMAGE.elements.loaderIcon, this.root);\n const elementsToShow = [\n Selectors.IMAGE.elements.insertImage,\n Selectors.IMAGE.elements.modalFooter,\n ];\n showElements(elementsToShow, this.root);\n }\n\n registerEventListeners() {\n this.root.addEventListener('click', async(e) => {\n const addUrlEle = e.target.closest(Selectors.IMAGE.actions.addUrl);\n if (addUrlEle) {\n this.urlChanged();\n }\n });\n\n this.root.addEventListener('input', (e) => {\n const urlEle = e.target.closest(Selectors.IMAGE.elements.url);\n if (urlEle) {\n this.toggleUrlButton();\n }\n });\n }\n}"],"names":["constructor","root","editor","currentModal","canShowFilePicker","canShowDropZone","async","langStringKeys","langStringvalues","map","key","component","langStrings","Object","fromEntries","index","setTitle","this","insertimage","registerEventListeners","urlString","RegExp","test","url","startImageLoading","currentUrl","image","Image","src","addEventListener","querySelector","Selectors","IMAGE","elements","urlWarning","innerHTML","imageurlrequired","stopImageLoading","window","console","log","toggleUrlButton","value","actions","addUrl","disabled","isValidUrl","urlChanged","input","loadPreviewImage","loaderIcon","elementsToHide","insertImage","modalFooter","elementsToShow","e","target","closest"],"mappings":"0hBAgCgB,aAAc,CAC1B,cACA,WACA,aACA,mBACA,YACA,uCAKAA,YACIC,KACAC,OACAC,aACAC,kBACAC,8CASGC,uBAEGC,eAAiB,CACnB,cACA,WACA,aACA,mBACA,YACA,WAEEC,uBAAyB,mBAAW,IAAID,gBAAgBE,KAAKC,OAAUA,IAAAA,IAAKC,UAAAA,4BAG7EC,YAAcC,OAAOC,YAAYP,eAAeE,KAAI,CAACC,IAAKK,QAAU,CAACL,IAAKF,iBAAiBO,gBAC3FZ,aAAaa,SAASC,KAAKL,YAAYM,mBACtCD,KAAKE,+DAmBFC,aACU,IAAIC,OAAO,yIAIVC,KAAKF,sDAmBV,SAASG,UACnBC,yBACAC,WAAaF,UACZG,MAAQ,IAAIC,MAClBD,MAAME,IAAML,IACZG,MAAMG,iBAAiB,SAAS,KACDZ,KAAKhB,KAAK6B,cAAcC,mBAAUC,MAAMC,SAASC,YACzDC,UAAYlB,KAAKL,YAAYwB,gDACnCL,mBAAUC,MAAMC,SAASC,WAAYjB,KAAKhB,WAClDwB,WAAa,QACbY,sBAGTX,MAAMG,iBAAiB,QAAQ,KAC3BS,OAAOC,QAAQC,IAAIvB,KAAKQ,iBACnBY,8BAhFJpC,KAAOA,UACPC,OAASA,YACTC,aAAeA,kBACfC,kBAAoBA,uBACpBC,gBAAkBA,gBAwB3BoC,wBAEUlB,IADWN,KAAKhB,KAAK6B,cAAcC,mBAAUC,MAAMC,SAASV,KAC7CmB,MACNzB,KAAKhB,KAAK6B,cAAcC,mBAAUC,MAAMW,QAAQC,QACxDC,WAAqB,KAARtB,KAAcN,KAAK6B,WAAWvB,MAoBtDwB,4CACiBhB,mBAAUC,MAAMC,SAASC,WAAYjB,KAAKhB,YACjD+C,MAAQ/B,KAAKhB,KAAK6B,cAAcC,mBAAUC,MAAMC,SAASV,KAC3DyB,MAAMN,OAASM,MAAMN,QAAUzB,KAAKQ,iBAC/BwB,iBAAiBD,MAAMN,OA+BpClB,mDACiBO,mBAAUC,MAAMC,SAASiB,WAAYjC,KAAKhB,YACjDkD,eAAiB,CACnBpB,mBAAUC,MAAMC,SAASmB,YACzBrB,mBAAUC,MAAMC,SAASC,WACzBH,mBAAUC,MAAMC,SAASoB,4CAEhBF,eAAgBlC,KAAKhB,MAMtCoC,kDACiBN,mBAAUC,MAAMC,SAASiB,WAAYjC,KAAKhB,YACjDqD,eAAiB,CACnBvB,mBAAUC,MAAMC,SAASmB,YACzBrB,mBAAUC,MAAMC,SAASoB,4CAEhBC,eAAgBrC,KAAKhB,MAGtCkB,8BACSlB,KAAK4B,iBAAiB,SAASvB,MAAAA,IACdiD,EAAEC,OAAOC,QAAQ1B,mBAAUC,MAAMW,QAAQC,cAElDG,qBAIR9C,KAAK4B,iBAAiB,SAAU0B,IAClBA,EAAEC,OAAOC,QAAQ1B,mBAAUC,MAAMC,SAASV,WAEhDkB"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/selectors.min.js b/lib/editor/tiny/plugins/media/amd/build/selectors.min.js index 24f1a3d6747..b35891104fc 100644 --- a/lib/editor/tiny/plugins/media/amd/build/selectors.min.js +++ b/lib/editor/tiny/plugins/media/amd/build/selectors.min.js @@ -1,3 +1,3 @@ -define("tiny_media/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={IMAGE:{actions:{submit:".tiny_image_urlentrysubmit",imageBrowser:".openimagebrowser"},elements:{form:"form.tiny_image_form",alignSettings:".tiny_image_button",alt:".tiny_image_altentry",altWarning:".tiny_image_altwarning",height:".tiny_image_heightentry",width:".tiny_image_widthentry",url:".tiny_image_urlentry",urlWarning:".tiny_image_urlwarning",size:".tiny_image_size",presentation:".tiny_image_presentation",constrain:".tiny_image_constrain",customStyle:".tiny_image_customstyle",preview:".tiny_image_preview",previewBox:".tiny_image_preview_box"},styles:{responsive:"img-fluid"}},EMBED:{actions:{submit:".tiny_media_submit",mediaBrowser:".openmediabrowser"},elements:{form:"form.tiny_media_form",source:".tiny_media_source",track:".tiny_media_track",mediaSource:".tiny_media_media_source",linkSource:".tiny_media_link_source",linkSize:".tiny_media_link_size",posterSource:".tiny_media_poster_source",posterSize:".tiny_media_poster_size",displayOptions:".tiny_media_display_options",name:".tiny_media_name_entry",title:".tiny_media_title_entry",url:".tiny_media_url_entry",width:".tiny_media_width_entry",height:".tiny_media_height_entry",trackSource:".tiny_media_track_source",trackKind:".tiny_media_track_kind_entry",trackLabel:".tiny_media_track_label_entry",trackLang:".tiny_media_track_lang_entry",trackDefault:".tiny_media_track_default",mediaControl:".tiny_media_controls",mediaAutoplay:".tiny_media_autoplay",mediaMute:".tiny_media_mute",mediaLoop:".tiny_media_loop",advancedSettings:".tiny_media_advancedsettings",linkTab:'li[data-medium-type="link"]',videoTab:'li[data-medium-type="video"]',audioTab:'li[data-medium-type="audio"]',linkPane:'.tab-pane[data-medium-type="link"]',videoPane:'.tab-pane[data-medium-type="video"]',audioPane:'.tab-pane[data-medium-type="audio"]',trackSubtitlesTab:'li[data-track-kind="subtitles"]',trackCaptionsTab:'li[data-track-kind="captions"]',trackDescriptionsTab:'li[data-track-kind="descriptions"]',trackChaptersTab:'li[data-track-kind="chapters"]',trackMetadataTab:'li[data-track-kind="metadata"]',trackSubtitlesPane:'.tab-pane[data-track-kind="subtitles"]',trackCaptionsPane:'.tab-pane[data-track-kind="captions"]',trackDescriptionsPane:'.tab-pane[data-track-kind="descriptions"]',trackChaptersPane:'.tab-pane[data-track-kind="chapters"]',trackMetadataPane:'.tab-pane[data-track-kind="metadata"]'},mediaTypes:{link:"LINK",video:"VIDEO",audio:"AUDIO"},trackKinds:{subtitles:"SUBTITLES",captions:"CAPTIONS",descriptions:"DESCRIPTIONS",chapters:"CHAPTERS",metadata:"METADATA"}}},_exports.default})); +define("tiny_media/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={IMAGE:{actions:{submit:".tiny_image_urlentrysubmit",imageBrowser:".openimagebrowser",addUrl:".tiny_image_addurl"},elements:{form:"form.tiny_image_form",alignSettings:".tiny_image_button",alt:".tiny_image_altentry",altWarning:".tiny_image_altwarning",height:".tiny_image_heightentry",width:".tiny_image_widthentry",url:".tiny_image_urlentry",urlWarning:".tiny_image_urlwarning",size:".tiny_image_size",presentation:".tiny_image_presentation",constrain:".tiny_image_constrain",customStyle:".tiny_image_customstyle",preview:".tiny_image_preview",previewBox:".tiny_image_preview_box",loaderIcon:".tiny_image_loader",insertImage:".tiny_image_insert_image",modalFooter:".modal-footer"},styles:{responsive:"img-fluid"}},EMBED:{actions:{submit:".tiny_media_submit",mediaBrowser:".openmediabrowser"},elements:{form:"form.tiny_media_form",source:".tiny_media_source",track:".tiny_media_track",mediaSource:".tiny_media_media_source",linkSource:".tiny_media_link_source",linkSize:".tiny_media_link_size",posterSource:".tiny_media_poster_source",posterSize:".tiny_media_poster_size",displayOptions:".tiny_media_display_options",name:".tiny_media_name_entry",title:".tiny_media_title_entry",url:".tiny_media_url_entry",width:".tiny_media_width_entry",height:".tiny_media_height_entry",trackSource:".tiny_media_track_source",trackKind:".tiny_media_track_kind_entry",trackLabel:".tiny_media_track_label_entry",trackLang:".tiny_media_track_lang_entry",trackDefault:".tiny_media_track_default",mediaControl:".tiny_media_controls",mediaAutoplay:".tiny_media_autoplay",mediaMute:".tiny_media_mute",mediaLoop:".tiny_media_loop",advancedSettings:".tiny_media_advancedsettings",linkTab:'li[data-medium-type="link"]',videoTab:'li[data-medium-type="video"]',audioTab:'li[data-medium-type="audio"]',linkPane:'.tab-pane[data-medium-type="link"]',videoPane:'.tab-pane[data-medium-type="video"]',audioPane:'.tab-pane[data-medium-type="audio"]',trackSubtitlesTab:'li[data-track-kind="subtitles"]',trackCaptionsTab:'li[data-track-kind="captions"]',trackDescriptionsTab:'li[data-track-kind="descriptions"]',trackChaptersTab:'li[data-track-kind="chapters"]',trackMetadataTab:'li[data-track-kind="metadata"]',trackSubtitlesPane:'.tab-pane[data-track-kind="subtitles"]',trackCaptionsPane:'.tab-pane[data-track-kind="captions"]',trackDescriptionsPane:'.tab-pane[data-track-kind="descriptions"]',trackChaptersPane:'.tab-pane[data-track-kind="chapters"]',trackMetadataPane:'.tab-pane[data-track-kind="metadata"]'},mediaTypes:{link:"LINK",video:"VIDEO",audio:"AUDIO"},trackKinds:{subtitles:"SUBTITLES",captions:"CAPTIONS",descriptions:"DESCRIPTIONS",chapters:"CHAPTERS",metadata:"METADATA"}}},_exports.default})); //# sourceMappingURL=selectors.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/selectors.min.js.map b/lib/editor/tiny/plugins/media/amd/build/selectors.min.js.map index c33a9b539bb..7edbb159c59 100644 --- a/lib/editor/tiny/plugins/media/amd/build/selectors.min.js.map +++ b/lib/editor/tiny/plugins/media/amd/build/selectors.min.js.map @@ -1 +1 @@ -{"version":3,"file":"selectors.min.js","sources":["../src/selectors.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 helper function to build queryable data selectors.\n *\n * @module tiny_media/selectors\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n IMAGE: {\n actions: {\n submit: '.tiny_image_urlentrysubmit',\n imageBrowser: '.openimagebrowser',\n },\n elements: {\n form: 'form.tiny_image_form',\n alignSettings: '.tiny_image_button',\n alt: '.tiny_image_altentry',\n altWarning: '.tiny_image_altwarning',\n height: '.tiny_image_heightentry',\n width: '.tiny_image_widthentry',\n url: '.tiny_image_urlentry',\n urlWarning: '.tiny_image_urlwarning',\n size: '.tiny_image_size',\n presentation: '.tiny_image_presentation',\n constrain: '.tiny_image_constrain',\n customStyle: '.tiny_image_customstyle',\n preview: '.tiny_image_preview',\n previewBox: '.tiny_image_preview_box',\n },\n styles: {\n responsive: 'img-fluid',\n },\n },\n EMBED: {\n actions: {\n submit: '.tiny_media_submit',\n mediaBrowser: '.openmediabrowser',\n },\n elements: {\n form: 'form.tiny_media_form',\n source: '.tiny_media_source',\n track: '.tiny_media_track',\n mediaSource: '.tiny_media_media_source',\n linkSource: '.tiny_media_link_source',\n linkSize: '.tiny_media_link_size',\n posterSource: '.tiny_media_poster_source',\n posterSize: '.tiny_media_poster_size',\n displayOptions: '.tiny_media_display_options',\n name: '.tiny_media_name_entry',\n title: '.tiny_media_title_entry',\n url: '.tiny_media_url_entry',\n width: '.tiny_media_width_entry',\n height: '.tiny_media_height_entry',\n trackSource: '.tiny_media_track_source',\n trackKind: '.tiny_media_track_kind_entry',\n trackLabel: '.tiny_media_track_label_entry',\n trackLang: '.tiny_media_track_lang_entry',\n trackDefault: '.tiny_media_track_default',\n mediaControl: '.tiny_media_controls',\n mediaAutoplay: '.tiny_media_autoplay',\n mediaMute: '.tiny_media_mute',\n mediaLoop: '.tiny_media_loop',\n advancedSettings: '.tiny_media_advancedsettings',\n linkTab: 'li[data-medium-type=\"link\"]',\n videoTab: 'li[data-medium-type=\"video\"]',\n audioTab: 'li[data-medium-type=\"audio\"]',\n linkPane: '.tab-pane[data-medium-type=\"link\"]',\n videoPane: '.tab-pane[data-medium-type=\"video\"]',\n audioPane: '.tab-pane[data-medium-type=\"audio\"]',\n trackSubtitlesTab: 'li[data-track-kind=\"subtitles\"]',\n trackCaptionsTab: 'li[data-track-kind=\"captions\"]',\n trackDescriptionsTab: 'li[data-track-kind=\"descriptions\"]',\n trackChaptersTab: 'li[data-track-kind=\"chapters\"]',\n trackMetadataTab: 'li[data-track-kind=\"metadata\"]',\n trackSubtitlesPane: '.tab-pane[data-track-kind=\"subtitles\"]',\n trackCaptionsPane: '.tab-pane[data-track-kind=\"captions\"]',\n trackDescriptionsPane: '.tab-pane[data-track-kind=\"descriptions\"]',\n trackChaptersPane: '.tab-pane[data-track-kind=\"chapters\"]',\n trackMetadataPane: '.tab-pane[data-track-kind=\"metadata\"]',\n },\n mediaTypes: {\n link: 'LINK',\n video: 'VIDEO',\n audio: 'AUDIO',\n },\n trackKinds: {\n subtitles: 'SUBTITLES',\n captions: 'CAPTIONS',\n descriptions: 'DESCRIPTIONS',\n chapters: 'CHAPTERS',\n metadata: 'METADATA',\n },\n },\n};\n"],"names":["IMAGE","actions","submit","imageBrowser","elements","form","alignSettings","alt","altWarning","height","width","url","urlWarning","size","presentation","constrain","customStyle","preview","previewBox","styles","responsive","EMBED","mediaBrowser","source","track","mediaSource","linkSource","linkSize","posterSource","posterSize","displayOptions","name","title","trackSource","trackKind","trackLabel","trackLang","trackDefault","mediaControl","mediaAutoplay","mediaMute","mediaLoop","advancedSettings","linkTab","videoTab","audioTab","linkPane","videoPane","audioPane","trackSubtitlesTab","trackCaptionsTab","trackDescriptionsTab","trackChaptersTab","trackMetadataTab","trackSubtitlesPane","trackCaptionsPane","trackDescriptionsPane","trackChaptersPane","trackMetadataPane","mediaTypes","link","video","audio","trackKinds","subtitles","captions","descriptions","chapters","metadata"],"mappings":"sKAuBe,CACXA,MAAO,CACHC,QAAS,CACLC,OAAQ,6BACRC,aAAc,qBAElBC,SAAU,CACNC,KAAM,uBACNC,cAAe,qBACfC,IAAK,uBACLC,WAAY,yBACZC,OAAQ,0BACRC,MAAO,yBACPC,IAAK,uBACLC,WAAY,yBACZC,KAAM,mBACNC,aAAc,2BACdC,UAAW,wBACXC,YAAa,0BACbC,QAAS,sBACTC,WAAY,2BAEhBC,OAAQ,CACJC,WAAY,cAGpBC,MAAO,CACHpB,QAAS,CACLC,OAAQ,qBACRoB,aAAc,qBAElBlB,SAAU,CACNC,KAAM,uBACNkB,OAAQ,qBACRC,MAAO,oBACPC,YAAa,2BACbC,WAAY,0BACZC,SAAU,wBACVC,aAAc,4BACdC,WAAY,0BACZC,eAAgB,8BAChBC,KAAM,yBACNC,MAAO,0BACPrB,IAAK,wBACLD,MAAO,0BACPD,OAAQ,2BACRwB,YAAa,2BACbC,UAAW,+BACXC,WAAY,gCACZC,UAAW,+BACXC,aAAc,4BACdC,aAAc,uBACdC,cAAe,uBACfC,UAAW,mBACXC,UAAW,mBACXC,iBAAkB,+BAClBC,QAAS,8BACTC,SAAU,+BACVC,SAAU,+BACVC,SAAU,qCACVC,UAAW,sCACXC,UAAW,sCACXC,kBAAmB,kCACnBC,iBAAkB,iCAClBC,qBAAsB,qCACtBC,iBAAkB,iCAClBC,iBAAkB,iCAClBC,mBAAoB,yCACpBC,kBAAmB,wCACnBC,sBAAuB,4CACvBC,kBAAmB,wCACnBC,kBAAmB,yCAEvBC,WAAY,CACRC,KAAM,OACNC,MAAO,QACPC,MAAO,SAEXC,WAAY,CACRC,UAAW,YACXC,SAAU,WACVC,aAAc,eACdC,SAAU,WACVC,SAAU"} \ No newline at end of file +{"version":3,"file":"selectors.min.js","sources":["../src/selectors.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 helper function to build queryable data selectors.\n *\n * @module tiny_media/selectors\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n IMAGE: {\n actions: {\n submit: '.tiny_image_urlentrysubmit',\n imageBrowser: '.openimagebrowser',\n addUrl: '.tiny_image_addurl',\n },\n elements: {\n form: 'form.tiny_image_form',\n alignSettings: '.tiny_image_button',\n alt: '.tiny_image_altentry',\n altWarning: '.tiny_image_altwarning',\n height: '.tiny_image_heightentry',\n width: '.tiny_image_widthentry',\n url: '.tiny_image_urlentry',\n urlWarning: '.tiny_image_urlwarning',\n size: '.tiny_image_size',\n presentation: '.tiny_image_presentation',\n constrain: '.tiny_image_constrain',\n customStyle: '.tiny_image_customstyle',\n preview: '.tiny_image_preview',\n previewBox: '.tiny_image_preview_box',\n loaderIcon: '.tiny_image_loader',\n insertImage: '.tiny_image_insert_image',\n modalFooter: '.modal-footer',\n },\n styles: {\n responsive: 'img-fluid',\n },\n },\n EMBED: {\n actions: {\n submit: '.tiny_media_submit',\n mediaBrowser: '.openmediabrowser',\n },\n elements: {\n form: 'form.tiny_media_form',\n source: '.tiny_media_source',\n track: '.tiny_media_track',\n mediaSource: '.tiny_media_media_source',\n linkSource: '.tiny_media_link_source',\n linkSize: '.tiny_media_link_size',\n posterSource: '.tiny_media_poster_source',\n posterSize: '.tiny_media_poster_size',\n displayOptions: '.tiny_media_display_options',\n name: '.tiny_media_name_entry',\n title: '.tiny_media_title_entry',\n url: '.tiny_media_url_entry',\n width: '.tiny_media_width_entry',\n height: '.tiny_media_height_entry',\n trackSource: '.tiny_media_track_source',\n trackKind: '.tiny_media_track_kind_entry',\n trackLabel: '.tiny_media_track_label_entry',\n trackLang: '.tiny_media_track_lang_entry',\n trackDefault: '.tiny_media_track_default',\n mediaControl: '.tiny_media_controls',\n mediaAutoplay: '.tiny_media_autoplay',\n mediaMute: '.tiny_media_mute',\n mediaLoop: '.tiny_media_loop',\n advancedSettings: '.tiny_media_advancedsettings',\n linkTab: 'li[data-medium-type=\"link\"]',\n videoTab: 'li[data-medium-type=\"video\"]',\n audioTab: 'li[data-medium-type=\"audio\"]',\n linkPane: '.tab-pane[data-medium-type=\"link\"]',\n videoPane: '.tab-pane[data-medium-type=\"video\"]',\n audioPane: '.tab-pane[data-medium-type=\"audio\"]',\n trackSubtitlesTab: 'li[data-track-kind=\"subtitles\"]',\n trackCaptionsTab: 'li[data-track-kind=\"captions\"]',\n trackDescriptionsTab: 'li[data-track-kind=\"descriptions\"]',\n trackChaptersTab: 'li[data-track-kind=\"chapters\"]',\n trackMetadataTab: 'li[data-track-kind=\"metadata\"]',\n trackSubtitlesPane: '.tab-pane[data-track-kind=\"subtitles\"]',\n trackCaptionsPane: '.tab-pane[data-track-kind=\"captions\"]',\n trackDescriptionsPane: '.tab-pane[data-track-kind=\"descriptions\"]',\n trackChaptersPane: '.tab-pane[data-track-kind=\"chapters\"]',\n trackMetadataPane: '.tab-pane[data-track-kind=\"metadata\"]',\n },\n mediaTypes: {\n link: 'LINK',\n video: 'VIDEO',\n audio: 'AUDIO',\n },\n trackKinds: {\n subtitles: 'SUBTITLES',\n captions: 'CAPTIONS',\n descriptions: 'DESCRIPTIONS',\n chapters: 'CHAPTERS',\n metadata: 'METADATA',\n },\n },\n};\n"],"names":["IMAGE","actions","submit","imageBrowser","addUrl","elements","form","alignSettings","alt","altWarning","height","width","url","urlWarning","size","presentation","constrain","customStyle","preview","previewBox","loaderIcon","insertImage","modalFooter","styles","responsive","EMBED","mediaBrowser","source","track","mediaSource","linkSource","linkSize","posterSource","posterSize","displayOptions","name","title","trackSource","trackKind","trackLabel","trackLang","trackDefault","mediaControl","mediaAutoplay","mediaMute","mediaLoop","advancedSettings","linkTab","videoTab","audioTab","linkPane","videoPane","audioPane","trackSubtitlesTab","trackCaptionsTab","trackDescriptionsTab","trackChaptersTab","trackMetadataTab","trackSubtitlesPane","trackCaptionsPane","trackDescriptionsPane","trackChaptersPane","trackMetadataPane","mediaTypes","link","video","audio","trackKinds","subtitles","captions","descriptions","chapters","metadata"],"mappings":"sKAuBe,CACXA,MAAO,CACHC,QAAS,CACLC,OAAQ,6BACRC,aAAc,oBACdC,OAAQ,sBAEZC,SAAU,CACNC,KAAM,uBACNC,cAAe,qBACfC,IAAK,uBACLC,WAAY,yBACZC,OAAQ,0BACRC,MAAO,yBACPC,IAAK,uBACLC,WAAY,yBACZC,KAAM,mBACNC,aAAc,2BACdC,UAAW,wBACXC,YAAa,0BACbC,QAAS,sBACTC,WAAY,0BACZC,WAAY,qBACZC,YAAa,2BACbC,YAAa,iBAEjBC,OAAQ,CACJC,WAAY,cAGpBC,MAAO,CACHxB,QAAS,CACLC,OAAQ,qBACRwB,aAAc,qBAElBrB,SAAU,CACNC,KAAM,uBACNqB,OAAQ,qBACRC,MAAO,oBACPC,YAAa,2BACbC,WAAY,0BACZC,SAAU,wBACVC,aAAc,4BACdC,WAAY,0BACZC,eAAgB,8BAChBC,KAAM,yBACNC,MAAO,0BACPxB,IAAK,wBACLD,MAAO,0BACPD,OAAQ,2BACR2B,YAAa,2BACbC,UAAW,+BACXC,WAAY,gCACZC,UAAW,+BACXC,aAAc,4BACdC,aAAc,uBACdC,cAAe,uBACfC,UAAW,mBACXC,UAAW,mBACXC,iBAAkB,+BAClBC,QAAS,8BACTC,SAAU,+BACVC,SAAU,+BACVC,SAAU,qCACVC,UAAW,sCACXC,UAAW,sCACXC,kBAAmB,kCACnBC,iBAAkB,iCAClBC,qBAAsB,qCACtBC,iBAAkB,iCAClBC,iBAAkB,iCAClBC,mBAAoB,yCACpBC,kBAAmB,wCACnBC,sBAAuB,4CACvBC,kBAAmB,wCACnBC,kBAAmB,yCAEvBC,WAAY,CACRC,KAAM,OACNC,MAAO,QACPC,MAAO,SAEXC,WAAY,CACRC,UAAW,YACXC,SAAU,WACVC,aAAc,eACdC,SAAU,WACVC,SAAU"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/src/imagehelpers.js b/lib/editor/tiny/plugins/media/amd/src/imagehelpers.js index c857254df38..fd7e5fb20c9 100644 --- a/lib/editor/tiny/plugins/media/amd/src/imagehelpers.js +++ b/lib/editor/tiny/plugins/media/amd/src/imagehelpers.js @@ -58,3 +58,47 @@ export const footerImageInsert = async(templateContext, root) => { window.console.log(error); }); }; + +/** + * Show the element(s). + * + * @param {string|string[]} elements - The CSS selector for the elements to toggle. + * @param {object} root - The CSS selector for the elements to toggle. + */ +export const showElements = (elements, root) => { + if (elements instanceof Array) { + elements.forEach((elementSelector) => { + const element = root.querySelector(elementSelector); + if (element) { + element.classList.remove('d-none'); + } + }); + } else { + const element = root.querySelector(elements); + if (element) { + element.classList.remove('d-none'); + } + } +}; + +/** + * Hide the element(s). + * + * @param {string|string[]} elements - The CSS selector for the elements to toggle. + * @param {object} root - The CSS selector for the elements to toggle. + */ +export const hideElements = (elements, root) => { + if (elements instanceof Array) { + elements.forEach((elementSelector) => { + const element = root.querySelector(elementSelector); + if (element) { + element.classList.add('d-none'); + } + }); + } else { + const element = root.querySelector(elements); + if (element) { + element.classList.add('d-none'); + } + } +}; diff --git a/lib/editor/tiny/plugins/media/amd/src/imageinsert.js b/lib/editor/tiny/plugins/media/amd/src/imageinsert.js index 9f83418bf9a..a394e183af7 100644 --- a/lib/editor/tiny/plugins/media/amd/src/imageinsert.js +++ b/lib/editor/tiny/plugins/media/amd/src/imageinsert.js @@ -21,9 +21,14 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +import Selectors from './selectors'; import {prefetchStrings} from 'core/prefetch'; import {getStrings} from 'core/str'; import {component} from "./common"; +import { + showElements, + hideElements, +} from 'tiny_media/imagehelpers'; prefetchStrings('tiny_media', [ 'insertimage', @@ -65,5 +70,106 @@ export class ImageInsert { // Convert array to object. this.langStrings = Object.fromEntries(langStringKeys.map((key, index) => [key, langStringvalues[index]])); this.currentModal.setTitle(this.langStrings.insertimage); + await this.registerEventListeners(); }; + + /** + * Enables or disables the URL-related buttons in the footer based on the current URL and input value. + */ + toggleUrlButton() { + const urlInput = this.root.querySelector(Selectors.IMAGE.elements.url); + const url = urlInput.value; + const addUrl = this.root.querySelector(Selectors.IMAGE.actions.addUrl); + addUrl.disabled = !(url !== "" && this.isValidUrl(url)); + } + + /** + * Check if given string is a valid URL. + * + * @param {String} urlString URL the link will point to. + * @returns {boolean} True is valid, otherwise false. + */ + isValidUrl = urlString => { + const urlPattern = new RegExp('^(https?:\\/\\/)?' + // Protocol. + '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // Domain name. + '((\\d{1,3}\\.){3}\\d{1,3})|localhost)' + // OR ip (v4) address, localhost. + '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'); // Port and path. + return !!urlPattern.test(urlString); + }; + + /** + * Handles changes in the image URL input field and loads a preview of the image if the URL has changed. + */ + urlChanged() { + hideElements(Selectors.IMAGE.elements.urlWarning, this.root); + const input = this.root.querySelector(Selectors.IMAGE.elements.url); + if (input.value && input.value !== this.currentUrl) { + this.loadPreviewImage(input.value); + } + } + + /** + * Loads and displays a preview image based on the provided URL, and handles image loading events. + * + * @param {string} url - The URL of the image to load and display. + */ + loadPreviewImage = function(url) { + this.startImageLoading(); + this.currentUrl = url; + const image = new Image(); + image.src = url; + image.addEventListener('error', () => { + const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning); + urlWarningLabelEle.innerHTML = this.langStrings.imageurlrequired; + showElements(Selectors.IMAGE.elements.urlWarning, this.root); + this.currentUrl = ""; + this.stopImageLoading(); + }); + + image.addEventListener('load', () => { + window.console.log(this.currentUrl); + this.stopImageLoading(); + }); + }; + + /** + * Displays the upload loader and disables UI elements while loading a file. + */ + startImageLoading() { + showElements(Selectors.IMAGE.elements.loaderIcon, this.root); + const elementsToHide = [ + Selectors.IMAGE.elements.insertImage, + Selectors.IMAGE.elements.urlWarning, + Selectors.IMAGE.elements.modalFooter, + ]; + hideElements(elementsToHide, this.root); + } + + /** + * Displays the upload loader and disables UI elements while loading a file. + */ + stopImageLoading() { + hideElements(Selectors.IMAGE.elements.loaderIcon, this.root); + const elementsToShow = [ + Selectors.IMAGE.elements.insertImage, + Selectors.IMAGE.elements.modalFooter, + ]; + showElements(elementsToShow, this.root); + } + + registerEventListeners() { + this.root.addEventListener('click', async(e) => { + const addUrlEle = e.target.closest(Selectors.IMAGE.actions.addUrl); + if (addUrlEle) { + this.urlChanged(); + } + }); + + this.root.addEventListener('input', (e) => { + const urlEle = e.target.closest(Selectors.IMAGE.elements.url); + if (urlEle) { + this.toggleUrlButton(); + } + }); + } } \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/src/selectors.js b/lib/editor/tiny/plugins/media/amd/src/selectors.js index af039012a19..2c197cba9e1 100644 --- a/lib/editor/tiny/plugins/media/amd/src/selectors.js +++ b/lib/editor/tiny/plugins/media/amd/src/selectors.js @@ -26,6 +26,7 @@ export default { actions: { submit: '.tiny_image_urlentrysubmit', imageBrowser: '.openimagebrowser', + addUrl: '.tiny_image_addurl', }, elements: { form: 'form.tiny_image_form', @@ -42,6 +43,9 @@ export default { customStyle: '.tiny_image_customstyle', preview: '.tiny_image_preview', previewBox: '.tiny_image_preview_box', + loaderIcon: '.tiny_image_loader', + insertImage: '.tiny_image_insert_image', + modalFooter: '.modal-footer', }, styles: { responsive: 'img-fluid', diff --git a/lib/editor/tiny/plugins/media/lang/en/tiny_media.php b/lib/editor/tiny/plugins/media/lang/en/tiny_media.php index a6444d70049..f27b56e4cf1 100644 --- a/lib/editor/tiny/plugins/media/lang/en/tiny_media.php +++ b/lib/editor/tiny/plugins/media/lang/en/tiny_media.php @@ -63,7 +63,7 @@ $string['height'] = 'Height'; $string['helplinktext'] = 'Media helper'; $string['imagebuttontitle'] = 'Image'; $string['imageproperties'] = 'Image properties'; -$string['imageurlrequired'] = 'An image must have a URL.'; +$string['imageurlrequired'] = 'An image must have a valid URL.'; $string['insertimage'] = 'Insert image'; $string['label'] = 'Label'; $string['languagesavailable'] = 'Languages available';