MDL-78096 tiny_media: Implement external image URL

This commit is contained in:
meirzamoodle
2024-04-04 09:32:39 +07:00
parent de026219d3
commit 087b286a16
10 changed files with 161 additions and 7 deletions
@@ -5,6 +5,6 @@ define("tiny_media/imagehelpers",["exports","core/templates"],(function(_exports
* @module tiny_media/imagehelpers
* @copyright 2024 Meirza <meirza.arson@moodle.com>
* @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
@@ -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 <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny media plugin image helpers.\n *\n * @module tiny_media/imagehelpers\n * @copyright 2024 Meirza <[email protected]>\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<void>}\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<void>}\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"}
{"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 <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny media plugin image helpers.\n *\n * @module tiny_media/imagehelpers\n * @copyright 2024 Meirza <[email protected]>\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<void>}\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<void>}\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"}
+1 -1
View File
@@ -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
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -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
File diff suppressed because one or more lines are too long
@@ -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');
}
}
};
@@ -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();
}
});
}
}
@@ -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',
@@ -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';