From db71019e06e6d0587cda528d9a0cbe81bd9251c2 Mon Sep 17 00:00:00 2001 From: Stevani Andolo Date: Thu, 1 Aug 2024 10:57:02 +0800 Subject: [PATCH] MDL-78428 tiny_media: Image code refactor In this commit we also deprecated a lang string of "browserepositoriesimage,tiny_media" --- .../tiny/plugins/media/amd/build/image.min.js | 2 +- .../plugins/media/amd/build/image.min.js.map | 2 +- .../media/amd/build/imagedetails.min.js | 2 +- .../media/amd/build/imagedetails.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 +- .../tiny/plugins/media/amd/src/image.js | 23 ++- .../plugins/media/amd/src/imagedetails.js | 164 +++--------------- .../tiny/plugins/media/amd/src/imageinsert.js | 35 ++-- .../tiny/plugins/media/amd/src/selectors.js | 12 ++ .../tiny/plugins/media/lang/en/deprecated.txt | 1 + .../tiny/plugins/media/lang/en/tiny_media.php | 2 +- 14 files changed, 71 insertions(+), 182 deletions(-) diff --git a/lib/editor/tiny/plugins/media/amd/build/image.min.js b/lib/editor/tiny/plugins/media/amd/build/image.min.js index 5f1461319fb..dce7e0f6ef4 100644 --- a/lib/editor/tiny/plugins/media/amd/build/image.min.js +++ b/lib/editor/tiny/plugins/media/amd/build/image.min.js @@ -1,3 +1,3 @@ -define("tiny_media/image",["exports","./selectors","./imagemodal","./options","editor_tiny/options","tiny_media/imageinsert","tiny_media/imagedetails","core/prefetch","core/str","tiny_media/imagehelpers"],(function(_exports,_selectors,_imagemodal,_options,_options2,_imageinsert,_imagedetails,_prefetch,_str,_imagehelpers){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,_selectors=_interopRequireDefault(_selectors),_imagemodal=_interopRequireDefault(_imagemodal),(0,_prefetch.prefetchStrings)("tiny_media",["imageurlrequired","sizecustom_help"]);return _exports.default=class{constructor(editor){_defineProperty(this,"canShowFilePicker",!1),_defineProperty(this,"editor",null),_defineProperty(this,"currentModal",null),_defineProperty(this,"root",null),_defineProperty(this,"loadInsertImage",(async function(){const templateContext={elementid:this.editor.id,showfilepicker:this.canShowFilePicker,showdropzone:this.canShowDropZone};Promise.all([(0,_imagehelpers.bodyImageInsert)(templateContext,this.root),(0,_imagehelpers.footerImageInsert)(templateContext,this.root)]).then((()=>{new _imageinsert.ImageInsert(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone).init()})).catch((error=>{window.console.log(error)}))})),_defineProperty(this,"loadPreviewImage",(async function(url){this.startImageLoading();const image=new Image;image.src=url,image.addEventListener("error",(async()=>{this.root.querySelector(_selectors.default.IMAGE.elements.urlWarning).innerHTML=await(0,_str.getString)("imageurlrequired","tiny_media"),(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),this.stopImageLoading()})),image.addEventListener("load",(async()=>{const currentImageData=await this.getCurrentImageData();let templateContext=await this.getTemplateContext(currentImageData);templateContext.sizecustomhelpicon={text:await(0,_str.getString)("sizecustom_help","tiny_media")},templateContext.maxlengthalt=_imagehelpers.MAX_LENGTH_ALT,Promise.all([(0,_imagehelpers.bodyImageDetails)(templateContext,this.root),(0,_imagehelpers.footerImageDetails)(templateContext,this.root)]).then((()=>{this.stopImageLoading()})).then((()=>{new _imagedetails.ImageDetails(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone,url,image).init()})).catch((error=>{window.console.log(error)}))}))}));const permissions=(0,_options.getImagePermissions)(editor),options=(0,_options2.getFilePicker)(editor,"image");this.canShowFilePicker=permissions.filepicker&&void 0!==options&&Object.keys(options.repositories).length>0,this.canShowDropZone=void 0!==options&&Object.values(options.repositories).some((repository=>"upload"===repository.type)),this.editor=editor}async displayDialogue(){const currentImageData=await this.getCurrentImageData();this.currentModal=await _imagemodal.default.create(),this.root=this.currentModal.getRoot()[0],currentImageData&¤tImageData.src?this.loadPreviewImage(currentImageData.src):this.loadInsertImage()}async getTemplateContext(data){return{elementid:this.editor.id,showfilepicker:this.canShowFilePicker,...data}}async getCurrentImageData(){const selectedImageProperties=this.getSelectedImageProperties();if(!selectedImageProperties)return{};const properties={...selectedImageProperties};return properties.src&&(properties.haspreview=!0),properties.alt||(properties.presentation=!0),properties}getSelectedImageProperties(){const image=this.getSelectedImage();if(!image)return this.selectedImage=null,null;const properties={src:null,alt:null,width:null,height:null,presentation:!1,customStyle:""};this.selectedImage=image,properties.customStyle=image.style.cssText;const width=(image=>(0,_imagehelpers.isPercentageValue)(String(image.width))?image.width:parseInt(image.width,10))(image);0!==width&&(properties.width=width);const height=(image=>(0,_imagehelpers.isPercentageValue)(String(image.height))?image.height:parseInt(image.height,10))(image);return 0!==height&&(properties.height=height),properties.src=image.getAttribute("src"),properties.alt=image.getAttribute("alt")||"",properties.presentation="presentation"===image.getAttribute("role"),properties}getSelectedImage(){const imgElm=this.editor.selection.getNode(),figureElm=this.editor.dom.getParent(imgElm,"figure.image");return figureElm?this.editor.dom.select("img",figureElm)[0]:imgElm&&("IMG"!==imgElm.nodeName.toUpperCase()||this.isPlaceholderImage(imgElm))?null:imgElm}isPlaceholderImage(imgElm){return"IMG"===imgElm.nodeName.toUpperCase()&&(imgElm.hasAttribute("data-mce-object")||imgElm.hasAttribute("data-mce-placeholder"))}startImageLoading(){(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root),(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.insertImage,this.root)}stopImageLoading(){(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root),(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.insertImage,this.root)}},_exports.default})); +define("tiny_media/image",["exports","./selectors","./imagemodal","./options","editor_tiny/options","tiny_media/imageinsert","tiny_media/imagedetails","core/prefetch","core/str","./helpers","./imagehelpers"],(function(_exports,_selectors,_imagemodal,_options,_options2,_imageinsert,_imagedetails,_prefetch,_str,_helpers,_imagehelpers){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,_selectors=_interopRequireDefault(_selectors),_imagemodal=_interopRequireDefault(_imagemodal),(0,_prefetch.prefetchStrings)("tiny_media",["imageurlrequired","sizecustom_help"]);return _exports.default=class{constructor(editor){_defineProperty(this,"canShowFilePicker",!1),_defineProperty(this,"editor",null),_defineProperty(this,"currentModal",null),_defineProperty(this,"root",null),_defineProperty(this,"loadInsertImage",(async function(){const templateContext={elementid:this.editor.id,showfilepicker:this.canShowFilePicker,showdropzone:this.canShowDropZone,bodyTemplate:_selectors.default.IMAGE.template.body.insertImageBody,footerTemplate:_selectors.default.IMAGE.template.footer.insertImageFooter,selector:_selectors.default.IMAGE.type};Promise.all([(0,_helpers.body)(templateContext,this.root),(0,_helpers.footer)(templateContext,this.root)]).then((()=>{new _imageinsert.ImageInsert(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone).init()})).catch((error=>{window.console.log(error)}))})),_defineProperty(this,"loadPreviewImage",(async function(url){this.startImageLoading();const image=new Image;image.src=url,image.addEventListener("error",(async()=>{this.root.querySelector(_selectors.default.IMAGE.elements.urlWarning).innerHTML=await(0,_str.getString)("imageurlrequired","tiny_media"),(0,_helpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),this.stopImageLoading()})),image.addEventListener("load",(async()=>{const currentImageData=await this.getCurrentImageData();let templateContext=await this.getTemplateContext(currentImageData);templateContext.sizecustomhelpicon={text:await(0,_str.getString)("sizecustom_help","tiny_media")},templateContext.bodyTemplate=_selectors.default.IMAGE.template.body.insertImageDetailsBody,templateContext.footerTemplate=_selectors.default.IMAGE.template.footer.insertImageDetailsFooter,templateContext.selector=_selectors.default.IMAGE.type,templateContext.maxlengthalt=_imagehelpers.MAX_LENGTH_ALT,Promise.all([(0,_helpers.body)(templateContext,this.root),(0,_helpers.footer)(templateContext,this.root)]).then((()=>{this.stopImageLoading()})).then((()=>{new _imagedetails.ImageDetails(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone,url,image).init()})).catch((error=>{window.console.log(error)}))}))}));const permissions=(0,_options.getImagePermissions)(editor),options=(0,_options2.getFilePicker)(editor,"image");this.canShowFilePicker=permissions.filepicker&&void 0!==options&&Object.keys(options.repositories).length>0,this.canShowDropZone=void 0!==options&&Object.values(options.repositories).some((repository=>"upload"===repository.type)),this.editor=editor}async displayDialogue(){const currentImageData=await this.getCurrentImageData();this.currentModal=await _imagemodal.default.create(),this.root=this.currentModal.getRoot()[0],currentImageData&¤tImageData.src?this.loadPreviewImage(currentImageData.src):this.loadInsertImage()}async getTemplateContext(data){return{elementid:this.editor.id,showfilepicker:this.canShowFilePicker,...data}}async getCurrentImageData(){const selectedImageProperties=this.getSelectedImageProperties();if(!selectedImageProperties)return{};const properties={...selectedImageProperties};return properties.src&&(properties.haspreview=!0),properties.alt||(properties.presentation=!0),properties}getSelectedImageProperties(){const image=this.getSelectedImage();if(!image)return this.selectedImage=null,null;const properties={src:null,alt:null,width:null,height:null,presentation:!1,customStyle:""};this.selectedImage=image,properties.customStyle=image.style.cssText;const width=(image=>(0,_helpers.isPercentageValue)(String(image.width))?image.width:parseInt(image.width,10))(image);0!==width&&(properties.width=width);const height=(image=>(0,_helpers.isPercentageValue)(String(image.height))?image.height:parseInt(image.height,10))(image);return 0!==height&&(properties.height=height),properties.src=image.getAttribute("src"),properties.alt=image.getAttribute("alt")||"",properties.presentation="presentation"===image.getAttribute("role"),properties}getSelectedImage(){const imgElm=this.editor.selection.getNode(),figureElm=this.editor.dom.getParent(imgElm,"figure.image");return figureElm?this.editor.dom.select("img",figureElm)[0]:imgElm&&("IMG"!==imgElm.nodeName.toUpperCase()||this.isPlaceholderImage(imgElm))?null:imgElm}isPlaceholderImage(imgElm){return"IMG"===imgElm.nodeName.toUpperCase()&&(imgElm.hasAttribute("data-mce-object")||imgElm.hasAttribute("data-mce-placeholder"))}startImageLoading(){(0,_helpers.showElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root),(0,_helpers.hideElements)(_selectors.default.IMAGE.elements.insertImage,this.root)}stopImageLoading(){(0,_helpers.hideElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root),(0,_helpers.showElements)(_selectors.default.IMAGE.elements.insertImage,this.root)}},_exports.default})); //# sourceMappingURL=image.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/image.min.js.map b/lib/editor/tiny/plugins/media/amd/build/image.min.js.map index ee9e90c690b..74b62ee9249 100644 --- a/lib/editor/tiny/plugins/media/amd/build/image.min.js.map +++ b/lib/editor/tiny/plugins/media/amd/build/image.min.js.map @@ -1 +1 @@ -{"version":3,"file":"image.min.js","sources":["../src/image.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Tiny Media plugin Image class for Moodle.\n *\n * @module tiny_media/image\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Selectors from './selectors';\nimport ImageModal from './imagemodal';\nimport {getImagePermissions} from './options';\nimport {getFilePicker} from 'editor_tiny/options';\nimport {ImageInsert} from 'tiny_media/imageinsert';\nimport {ImageDetails} from 'tiny_media/imagedetails';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport {\n bodyImageInsert,\n footerImageInsert,\n bodyImageDetails,\n footerImageDetails,\n showElements,\n hideElements,\n isPercentageValue,\n MAX_LENGTH_ALT,\n} from 'tiny_media/imagehelpers';\n\nprefetchStrings('tiny_media', [\n 'imageurlrequired',\n 'sizecustom_help',\n]);\n\nexport default class MediaImage {\n canShowFilePicker = false;\n editor = null;\n currentModal = null;\n /**\n * @type {HTMLElement|null} The root element.\n */\n root = null;\n\n constructor(editor) {\n const permissions = getImagePermissions(editor);\n const options = getFilePicker(editor, 'image');\n // Indicates whether the file picker can be shown.\n this.canShowFilePicker = permissions.filepicker\n && (typeof options !== 'undefined')\n && Object.keys(options.repositories).length > 0;\n // Indicates whether the drop zone area can be shown.\n this.canShowDropZone = (typeof options !== 'undefined') &&\n Object.values(options.repositories).some(repository => repository.type === 'upload');\n\n this.editor = editor;\n }\n\n async displayDialogue() {\n const currentImageData = await this.getCurrentImageData();\n this.currentModal = await ImageModal.create();\n this.root = this.currentModal.getRoot()[0];\n if (currentImageData && currentImageData.src) {\n this.loadPreviewImage(currentImageData.src);\n } else {\n this.loadInsertImage();\n }\n }\n\n /**\n * Displays an insert image view asynchronously.\n *\n * @returns {Promise}\n */\n loadInsertImage = async function() {\n const templateContext = {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n showdropzone: this.canShowDropZone,\n };\n\n Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])\n .then(() => {\n const imageinsert = new ImageInsert(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n );\n imageinsert.init();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n };\n\n async getTemplateContext(data) {\n return {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n ...data,\n };\n }\n\n async getCurrentImageData() {\n const selectedImageProperties = this.getSelectedImageProperties();\n if (!selectedImageProperties) {\n return {};\n }\n\n const properties = {...selectedImageProperties};\n\n if (properties.src) {\n properties.haspreview = true;\n }\n\n if (!properties.alt) {\n properties.presentation = true;\n }\n\n return properties;\n }\n\n /**\n * Asynchronously loads and previews an image from the provided URL.\n *\n * @param {string} url - The URL of the image to load and preview.\n * @returns {Promise}\n */\n loadPreviewImage = async function(url) {\n this.startImageLoading();\n const image = new Image();\n image.src = url;\n image.addEventListener('error', async() => {\n const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);\n urlWarningLabelEle.innerHTML = await getString('imageurlrequired', 'tiny_media');\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n this.stopImageLoading();\n });\n\n image.addEventListener('load', async() => {\n const currentImageData = await this.getCurrentImageData();\n let templateContext = await this.getTemplateContext(currentImageData);\n templateContext.sizecustomhelpicon = {text: await getString('sizecustom_help', 'tiny_media')};\n templateContext.maxlengthalt = MAX_LENGTH_ALT;\n Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)])\n .then(() => {\n this.stopImageLoading();\n return;\n })\n .then(() => {\n const imagedetails = new ImageDetails(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n url,\n image,\n );\n imagedetails.init();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n });\n };\n\n getSelectedImageProperties() {\n const image = this.getSelectedImage();\n if (!image) {\n this.selectedImage = null;\n return null;\n }\n\n const properties = {\n src: null,\n alt: null,\n width: null,\n height: null,\n presentation: false,\n customStyle: '', // Custom CSS styles applied to the image.\n };\n\n const getImageHeight = (image) => {\n if (!isPercentageValue(String(image.height))) {\n return parseInt(image.height, 10);\n }\n\n return image.height;\n };\n\n const getImageWidth = (image) => {\n if (!isPercentageValue(String(image.width))) {\n return parseInt(image.width, 10);\n }\n\n return image.width;\n };\n\n // Get the current selection.\n this.selectedImage = image;\n\n properties.customStyle = image.style.cssText;\n\n const width = getImageWidth(image);\n if (width !== 0) {\n properties.width = width;\n }\n\n const height = getImageHeight(image);\n if (height !== 0) {\n properties.height = height;\n }\n\n properties.src = image.getAttribute('src');\n properties.alt = image.getAttribute('alt') || '';\n properties.presentation = (image.getAttribute('role') === 'presentation');\n\n return properties;\n }\n\n getSelectedImage() {\n const imgElm = this.editor.selection.getNode();\n const figureElm = this.editor.dom.getParent(imgElm, 'figure.image');\n if (figureElm) {\n return this.editor.dom.select('img', figureElm)[0];\n }\n\n if (imgElm && (imgElm.nodeName.toUpperCase() !== 'IMG' || this.isPlaceholderImage(imgElm))) {\n return null;\n }\n return imgElm;\n }\n\n isPlaceholderImage(imgElm) {\n if (imgElm.nodeName.toUpperCase() !== 'IMG') {\n return false;\n }\n\n return (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder'));\n }\n\n /**\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 hideElements(Selectors.IMAGE.elements.insertImage, 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 showElements(Selectors.IMAGE.elements.insertImage, this.root);\n }\n}\n"],"names":["constructor","editor","async","templateContext","elementid","this","id","showfilepicker","canShowFilePicker","showdropzone","canShowDropZone","Promise","all","root","then","ImageInsert","currentModal","init","catch","error","window","console","log","url","startImageLoading","image","Image","src","addEventListener","querySelector","Selectors","IMAGE","elements","urlWarning","innerHTML","stopImageLoading","currentImageData","getCurrentImageData","getTemplateContext","sizecustomhelpicon","text","maxlengthalt","MAX_LENGTH_ALT","ImageDetails","permissions","options","filepicker","Object","keys","repositories","length","values","some","repository","type","ImageModal","create","getRoot","loadPreviewImage","loadInsertImage","data","selectedImageProperties","getSelectedImageProperties","properties","haspreview","alt","presentation","getSelectedImage","selectedImage","width","height","customStyle","style","cssText","String","parseInt","getImageWidth","getImageHeight","getAttribute","imgElm","selection","getNode","figureElm","dom","getParent","select","nodeName","toUpperCase","isPlaceholderImage","hasAttribute","loaderIcon","insertImage"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"image.min.js","sources":["../src/image.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Tiny Media plugin Image class for Moodle.\n *\n * @module tiny_media/image\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Selectors from './selectors';\nimport ImageModal from './imagemodal';\nimport {getImagePermissions} from './options';\nimport {getFilePicker} from 'editor_tiny/options';\nimport {ImageInsert} from 'tiny_media/imageinsert';\nimport {ImageDetails} from 'tiny_media/imagedetails';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport {\n body,\n footer,\n hideElements,\n showElements,\n isPercentageValue,\n} from './helpers';\nimport {MAX_LENGTH_ALT} from './imagehelpers';\n\nprefetchStrings('tiny_media', [\n 'imageurlrequired',\n 'sizecustom_help',\n]);\n\nexport default class MediaImage {\n canShowFilePicker = false;\n editor = null;\n currentModal = null;\n /**\n * @type {HTMLElement|null} The root element.\n */\n root = null;\n\n constructor(editor) {\n const permissions = getImagePermissions(editor);\n const options = getFilePicker(editor, 'image');\n // Indicates whether the file picker can be shown.\n this.canShowFilePicker = permissions.filepicker\n && (typeof options !== 'undefined')\n && Object.keys(options.repositories).length > 0;\n // Indicates whether the drop zone area can be shown.\n this.canShowDropZone = (typeof options !== 'undefined') &&\n Object.values(options.repositories).some(repository => repository.type === 'upload');\n\n this.editor = editor;\n }\n\n async displayDialogue() {\n const currentImageData = await this.getCurrentImageData();\n this.currentModal = await ImageModal.create();\n this.root = this.currentModal.getRoot()[0];\n if (currentImageData && currentImageData.src) {\n this.loadPreviewImage(currentImageData.src);\n } else {\n this.loadInsertImage();\n }\n }\n\n /**\n * Displays an insert image view asynchronously.\n *\n * @returns {Promise}\n */\n loadInsertImage = async function() {\n const templateContext = {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n showdropzone: this.canShowDropZone,\n bodyTemplate: Selectors.IMAGE.template.body.insertImageBody,\n footerTemplate: Selectors.IMAGE.template.footer.insertImageFooter,\n selector: Selectors.IMAGE.type,\n };\n\n Promise.all([body(templateContext, this.root), footer(templateContext, this.root)])\n .then(() => {\n const imageinsert = new ImageInsert(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n );\n imageinsert.init();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n };\n\n async getTemplateContext(data) {\n return {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n ...data,\n };\n }\n\n async getCurrentImageData() {\n const selectedImageProperties = this.getSelectedImageProperties();\n if (!selectedImageProperties) {\n return {};\n }\n\n const properties = {...selectedImageProperties};\n\n if (properties.src) {\n properties.haspreview = true;\n }\n\n if (!properties.alt) {\n properties.presentation = true;\n }\n\n return properties;\n }\n\n /**\n * Asynchronously loads and previews an image from the provided URL.\n *\n * @param {string} url - The URL of the image to load and preview.\n * @returns {Promise}\n */\n loadPreviewImage = async function(url) {\n this.startImageLoading();\n const image = new Image();\n image.src = url;\n image.addEventListener('error', async() => {\n const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);\n urlWarningLabelEle.innerHTML = await getString('imageurlrequired', 'tiny_media');\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n this.stopImageLoading();\n });\n\n image.addEventListener('load', async() => {\n const currentImageData = await this.getCurrentImageData();\n let templateContext = await this.getTemplateContext(currentImageData);\n templateContext.sizecustomhelpicon = {text: await getString('sizecustom_help', 'tiny_media')};\n templateContext.bodyTemplate = Selectors.IMAGE.template.body.insertImageDetailsBody;\n templateContext.footerTemplate = Selectors.IMAGE.template.footer.insertImageDetailsFooter;\n templateContext.selector = Selectors.IMAGE.type;\n templateContext.maxlengthalt = MAX_LENGTH_ALT;\n\n Promise.all([body(templateContext, this.root), footer(templateContext, this.root)])\n .then(() => {\n this.stopImageLoading();\n return;\n })\n .then(() => {\n const imagedetails = new ImageDetails(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n url,\n image,\n );\n imagedetails.init();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n });\n };\n\n getSelectedImageProperties() {\n const image = this.getSelectedImage();\n if (!image) {\n this.selectedImage = null;\n return null;\n }\n\n const properties = {\n src: null,\n alt: null,\n width: null,\n height: null,\n presentation: false,\n customStyle: '', // Custom CSS styles applied to the image.\n };\n\n const getImageHeight = (image) => {\n if (!isPercentageValue(String(image.height))) {\n return parseInt(image.height, 10);\n }\n\n return image.height;\n };\n\n const getImageWidth = (image) => {\n if (!isPercentageValue(String(image.width))) {\n return parseInt(image.width, 10);\n }\n\n return image.width;\n };\n\n // Get the current selection.\n this.selectedImage = image;\n\n properties.customStyle = image.style.cssText;\n\n const width = getImageWidth(image);\n if (width !== 0) {\n properties.width = width;\n }\n\n const height = getImageHeight(image);\n if (height !== 0) {\n properties.height = height;\n }\n\n properties.src = image.getAttribute('src');\n properties.alt = image.getAttribute('alt') || '';\n properties.presentation = (image.getAttribute('role') === 'presentation');\n\n return properties;\n }\n\n getSelectedImage() {\n const imgElm = this.editor.selection.getNode();\n const figureElm = this.editor.dom.getParent(imgElm, 'figure.image');\n if (figureElm) {\n return this.editor.dom.select('img', figureElm)[0];\n }\n\n if (imgElm && (imgElm.nodeName.toUpperCase() !== 'IMG' || this.isPlaceholderImage(imgElm))) {\n return null;\n }\n return imgElm;\n }\n\n isPlaceholderImage(imgElm) {\n if (imgElm.nodeName.toUpperCase() !== 'IMG') {\n return false;\n }\n\n return (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder'));\n }\n\n /**\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 hideElements(Selectors.IMAGE.elements.insertImage, 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 showElements(Selectors.IMAGE.elements.insertImage, this.root);\n }\n}\n"],"names":["constructor","editor","async","templateContext","elementid","this","id","showfilepicker","canShowFilePicker","showdropzone","canShowDropZone","bodyTemplate","Selectors","IMAGE","template","body","insertImageBody","footerTemplate","footer","insertImageFooter","selector","type","Promise","all","root","then","ImageInsert","currentModal","init","catch","error","window","console","log","url","startImageLoading","image","Image","src","addEventListener","querySelector","elements","urlWarning","innerHTML","stopImageLoading","currentImageData","getCurrentImageData","getTemplateContext","sizecustomhelpicon","text","insertImageDetailsBody","insertImageDetailsFooter","maxlengthalt","MAX_LENGTH_ALT","ImageDetails","permissions","options","filepicker","Object","keys","repositories","length","values","some","repository","ImageModal","create","getRoot","loadPreviewImage","loadInsertImage","data","selectedImageProperties","getSelectedImageProperties","properties","haspreview","alt","presentation","getSelectedImage","selectedImage","width","height","customStyle","style","cssText","String","parseInt","getImageWidth","getImageHeight","getAttribute","imgElm","selection","getNode","figureElm","dom","getParent","select","nodeName","toUpperCase","isPlaceholderImage","hasAttribute","loaderIcon","insertImage"],"mappings":"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"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/imagedetails.min.js b/lib/editor/tiny/plugins/media/amd/build/imagedetails.min.js index 70b10090068..188fa0c6027 100644 --- a/lib/editor/tiny/plugins/media/amd/build/imagedetails.min.js +++ b/lib/editor/tiny/plugins/media/amd/build/imagedetails.min.js @@ -1,3 +1,3 @@ -define("tiny_media/imagedetails",["exports","core/config","core/modal_events","core/notification","core/pending","./selectors","core/templates","core/str","tiny_media/imageinsert","tiny_media/imagehelpers"],(function(_exports,_config,_modal_events,_notification,_pending,_selectors,_templates,_str,_imageinsert,_imagehelpers){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.ImageDetails=void 0,_config=_interopRequireDefault(_config),_modal_events=_interopRequireDefault(_modal_events),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_selectors=_interopRequireDefault(_selectors),_templates=_interopRequireDefault(_templates);_exports.ImageDetails=class{constructor(root,editor,currentModal,canShowFilePicker,canShowDropZone,currentUrl,_image){_defineProperty(this,"DEFAULTS",{WIDTH:160,HEIGHT:160}),_defineProperty(this,"rawImageDimensions",null),_defineProperty(this,"init",(function(){this.currentModal.setTitle((0,_str.getString)("imagedetails","tiny_media")),this.imageTypeChecked(),this.presentationChanged(),this.storeImageDimensions(this.image),this.setImageDimensions(),this.registerEventListeners()})),_defineProperty(this,"loadInsertImage",(async function(){const templateContext={elementid:this.editor.id,showfilepicker:this.canShowFilePicker,showdropzone:this.canShowDropZone};Promise.all([(0,_imagehelpers.bodyImageInsert)(templateContext,this.root),(0,_imagehelpers.footerImageInsert)(templateContext,this.root)]).then((()=>{new _imageinsert.ImageInsert(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone).init()})).catch((error=>{window.console.log(error)}))})),_defineProperty(this,"setImageDimensions",(()=>{const imagePreviewBox=this.root.querySelector(_selectors.default.IMAGE.elements.previewBox),image=this.root.querySelector(_selectors.default.IMAGE.elements.preview),widthField=this.root.querySelector(_selectors.default.IMAGE.elements.width),heightField=this.root.querySelector(_selectors.default.IMAGE.elements.height),updateImageDimensions=()=>{const boxWidth=imagePreviewBox.clientWidth,boxHeight=imagePreviewBox.clientHeight,dimensions=this.fitSquareIntoBox(widthField.value,heightField.value,boxWidth,boxHeight);image.style.width="".concat(dimensions.width,"px"),image.style.height="".concat(dimensions.height,"px")};0===imagePreviewBox.clientWidth?this.currentModal.getRoot().on(_modal_events.default.shown,(()=>{updateImageDimensions()})):updateImageDimensions()})),_defineProperty(this,"fitSquareIntoBox",((squareWidth,squareHeight,boxWidth,boxHeight)=>{if(squareWidth(""===element.value&&(element.value=this.rawImageDimensions.width),element.value))(this.root.querySelector(_selectors.default.IMAGE.elements.width)),currentHeight=(element=>(""===element.value&&(element.value=this.rawImageDimensions.height),element.value))(this.root.querySelector(_selectors.default.IMAGE.elements.height)),preview=this.root.querySelector(_selectors.default.IMAGE.elements.preview);preview.setAttribute("src",image.src),preview.style.display="";const constrain=this.root.querySelector(_selectors.default.IMAGE.elements.constrain);if((0,_imagehelpers.isPercentageValue)(currentWidth)&&(0,_imagehelpers.isPercentageValue)(currentHeight))constrain.checked=currentWidth===currentHeight;else if(0===image.width||0===image.height)constrain.disabled="disabled";else{const widthRatio=Math.round(100*parseInt(currentWidth,10)/image.width),heightRatio=Math.round(100*parseInt(currentHeight,10)/image.height);constrain.checked=widthRatio===heightRatio}((currentWidth,currentHeight)=>{this.rawImageDimensions.width===currentWidth&&this.rawImageDimensions.height===currentHeight?(this.currentWidth=this.rawImageDimensions.width,this.currentHeight=this.rawImageDimensions.height,this.sizeChecked("original")):(this.currentWidth=currentWidth,this.currentHeight=currentHeight,this.sizeChecked("custom"))})(Number(currentWidth),Number(currentHeight))}sizeChecked(option){const widthInput=this.root.querySelector(_selectors.default.IMAGE.elements.width),heightInput=this.root.querySelector(_selectors.default.IMAGE.elements.height);if("original"===option)this.sizeOriginalChecked(),widthInput.value=this.rawImageDimensions.width,heightInput.value=this.rawImageDimensions.height;else if("custom"===option&&(this.sizeCustomChecked(),widthInput.value=this.currentWidth,heightInput.value=this.currentHeight,this.currentWidth===this.rawImageDimensions.width&&this.currentHeight===this.rawImageDimensions.height)){this.root.querySelector(_selectors.default.IMAGE.elements.constrain).checked=!0}this.autoAdjustSize()}autoAdjustSize(){let forceHeight=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!this.rawImageDimensions)return;const widthField=this.root.querySelector(_selectors.default.IMAGE.elements.width),heightField=this.root.querySelector(_selectors.default.IMAGE.elements.height),normalizeFieldData=fieldData=>(fieldData.isPercentageValue=!!(0,_imagehelpers.isPercentageValue)(fieldData.field.value),fieldData.isPercentageValue?(fieldData.percentValue=parseInt(fieldData.field.value,10),fieldData.pixelSize=this.rawImageDimensions[fieldData.type]/100*fieldData.percentValue):(fieldData.pixelSize=parseInt(fieldData.field.value,10),fieldData.percentValue=fieldData.pixelSize/this.rawImageDimensions[fieldData.type]*100),fieldData),getKeyField=()=>{const currentValue=forceHeight?{field:heightField,type:"height"}:{field:widthField,type:"width"};return""===currentValue.field.value&&(currentValue.field.value=this.rawImageDimensions[currentValue.type]),normalizeFieldData(currentValue)};if(this.root.querySelector(_selectors.default.IMAGE.elements.constrain).checked){const keyField=getKeyField(),relativeField=normalizeFieldData(forceHeight?{field:widthField,type:"width"}:{field:heightField,type:"height"});keyField.isPercentageValue?(relativeField.field.value=keyField.field.value,relativeField.percentValue=keyField.percentValue):(relativeField.pixelSize=Math.round(keyField.pixelSize/this.rawImageDimensions[keyField.type]*this.rawImageDimensions[relativeField.type]),relativeField.field.value=relativeField.pixelSize)}this.currentWidth=Number(widthField.value)!==this.rawImageDimensions.width?widthField.value:this.currentWidth,this.currentHeight=Number(heightField.value)!==this.rawImageDimensions.height?heightField.value:this.currentHeight}sizeOriginalChecked(){this.root.querySelector(_selectors.default.IMAGE.elements.sizeOriginal).checked=!0,this.root.querySelector(_selectors.default.IMAGE.elements.sizeCustom).checked=!1,(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.properties,this.root)}sizeCustomChecked(){this.root.querySelector(_selectors.default.IMAGE.elements.sizeOriginal).checked=!1,this.root.querySelector(_selectors.default.IMAGE.elements.sizeCustom).checked=!0,(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.properties,this.root)}presentationChanged(){const presentation=this.root.querySelector(_selectors.default.IMAGE.elements.presentation);this.root.querySelector(_selectors.default.IMAGE.elements.alt).disabled=presentation.checked,this.handleKeyupCharacterCount()}imageTypeChecked(){const isExternalUrl=!1===new RegExp("".concat(_config.default.wwwroot)).test(this.currentUrl);if((0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.url,this.root),isExternalUrl)this.setFilenameLabel(decodeURI(this.currentUrl));else{const filename=this.currentUrl.split("/").pop().split("?")[0];this.setFilenameLabel(decodeURI(filename))}}setFilenameLabel(label){const urlLabelEle=this.root.querySelector(_selectors.default.IMAGE.elements.fileNameLabel);urlLabelEle&&(urlLabelEle.innerHTML=label,urlLabelEle.setAttribute("title",label))}toggleAriaInvalid(selectors,predicate){selectors.forEach((selector=>{this.root.querySelectorAll(selector).forEach((element=>element.setAttribute("aria-invalid",predicate)))}))}hasErrorUrlField(){const urlError=""===this.currentUrl;return urlError?(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root):(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.url],urlError),urlError}hasErrorAltField(){const alt=this.root.querySelector(_selectors.default.IMAGE.elements.alt).value,presentation=this.root.querySelector(_selectors.default.IMAGE.elements.presentation).checked,imageAltError=""===alt&&!presentation;return imageAltError?(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.altWarning,this.root):(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.urlWaaltWarningrning,this.root),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.alt,_selectors.default.IMAGE.elements.presentation],imageAltError),imageAltError}updateWarning(){const urlError=this.hasErrorUrlField(),imageAltError=this.hasErrorAltField();return urlError||imageAltError}getImageContext(){if(this.updateWarning())return null;const classList=[],constrain=this.root.querySelector(_selectors.default.IMAGE.elements.constrain).checked,sizeOriginal=this.root.querySelector(_selectors.default.IMAGE.elements.sizeOriginal).checked;return constrain||sizeOriginal?classList.push(_selectors.default.IMAGE.styles.responsive):classList.pop(_selectors.default.IMAGE.styles.responsive),{url:this.currentUrl,alt:this.root.querySelector(_selectors.default.IMAGE.elements.alt).value,width:this.root.querySelector(_selectors.default.IMAGE.elements.width).value,height:this.root.querySelector(_selectors.default.IMAGE.elements.height).value,presentation:this.root.querySelector(_selectors.default.IMAGE.elements.presentation).checked,customStyle:this.root.querySelector(_selectors.default.IMAGE.elements.customStyle).value,classlist:classList.join(" ")}}setImage(){const pendingPromise=new _pending.default("tiny_media:setImage");if(""===this.currentUrl)return;if(this.updateWarning())return void pendingPromise.resolve();const width=this.root.querySelector(_selectors.default.IMAGE.elements.width).value;if(!(0,_imagehelpers.isPercentageValue)(width)&&isNaN(parseInt(width,10)))return this.root.querySelector(_selectors.default.IMAGE.elements.width).focus(),void pendingPromise.resolve();const height=this.root.querySelector(_selectors.default.IMAGE.elements.height).value;if(!(0,_imagehelpers.isPercentageValue)(height)&&isNaN(parseInt(height,10)))return this.root.querySelector(_selectors.default.IMAGE.elements.height).focus(),void pendingPromise.resolve();_templates.default.render("tiny_media/image",this.getImageContext()).then((html=>(this.editor.insertContent(html),this.currentModal.destroy(),pendingPromise.resolve(),html))).catch((error=>{window.console.log(error)}))}deleteImage(){_notification.default.deleteCancelPromise((0,_str.getString)("deleteimage","tiny_media"),(0,_str.getString)("deleteimagewarning","tiny_media")).then((()=>{(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.altWarning,this.root),this.loadInsertImage()})).catch((error=>{window.console.log(error)}))}registerEventListeners(){this.root.querySelector(_selectors.default.IMAGE.actions.submit).addEventListener("click",(e=>{e.preventDefault(),this.setImage()}));const deleteImageEle=this.root.querySelector(_selectors.default.IMAGE.actions.deleteImage);deleteImageEle.addEventListener("click",(()=>{this.deleteImage()})),deleteImageEle.addEventListener("keydown",(e=>{"Enter"===e.key&&this.deleteImage()})),this.root.addEventListener("change",(e=>{e.target.closest(_selectors.default.IMAGE.elements.presentation)&&this.presentationChanged();e.target.closest(_selectors.default.IMAGE.elements.constrain)&&this.autoAdjustSize();e.target.closest(_selectors.default.IMAGE.elements.sizeOriginal)&&this.sizeChecked("original");e.target.closest(_selectors.default.IMAGE.elements.sizeCustom)&&this.sizeChecked("custom")})),this.root.addEventListener("blur",(e=>{if(e.target.nodeType===Node.ELEMENT_NODE){e.target.closest(_selectors.default.IMAGE.elements.presentation)&&this.presentationChanged()}}),!0),this.root.addEventListener("keyup",(e=>{e.target.closest(_selectors.default.IMAGE.elements.alt)&&this.handleKeyupCharacterCount()})),this.root.addEventListener("input",(e=>{const widthEle=e.target.closest(_selectors.default.IMAGE.elements.width);widthEle&&(widthEle.value=""===widthEle.value?0:Number(widthEle.value),this.autoAdjustSize());const heightEle=e.target.closest(_selectors.default.IMAGE.elements.height);heightEle&&(heightEle.value=""===heightEle.value?0:Number(heightEle.value),this.autoAdjustSize(!0))}))}handleKeyupCharacterCount(){const alt=this.root.querySelector(_selectors.default.IMAGE.elements.alt).value;this.root.querySelector("#currentcount").innerHTML=alt.length}}})); +define("tiny_media/imagedetails",["exports","core/config","core/modal_events","core/notification","core/pending","./selectors","core/templates","core/str","tiny_media/imageinsert","./mediabase","./helpers"],(function(_exports,_config,_modal_events,_notification,_pending,_selectors,_templates,_str,_imageinsert,_mediabase,_helpers){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.ImageDetails=void 0,_config=_interopRequireDefault(_config),_modal_events=_interopRequireDefault(_modal_events),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_selectors=_interopRequireDefault(_selectors),_templates=_interopRequireDefault(_templates);class ImageDetails extends _mediabase.MediaBase{constructor(root,editor,currentModal,canShowFilePicker,canShowDropZone,currentUrl,_image){super(),_defineProperty(this,"DEFAULTS",{WIDTH:160,HEIGHT:160}),_defineProperty(this,"selectorType","IMAGE"),_defineProperty(this,"mediaDimensions",null),_defineProperty(this,"init",(function(){this.currentModal.setTitle((0,_str.getString)("imagedetails","tiny_media")),this.imageTypeChecked(),this.presentationChanged(),this.storeImageDimensions(this.image),this.setImageDimensions(),this.registerEventListeners()})),_defineProperty(this,"loadInsertImage",(async function(){const templateContext={elementid:this.editor.id,showfilepicker:this.canShowFilePicker,showdropzone:this.canShowDropZone,bodyTemplate:_selectors.default.IMAGE.template.body.insertImageBody,footerTemplate:_selectors.default.IMAGE.template.footer.insertImageFooter,selector:"IMAGE"};Promise.all([(0,_helpers.body)(templateContext,this.root),(0,_helpers.footer)(templateContext,this.root)]).then((()=>{new _imageinsert.ImageInsert(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone).init()})).catch((error=>{window.console.log(error)}))})),_defineProperty(this,"setImageDimensions",(()=>{const imagePreviewBox=this.root.querySelector(_selectors.default.IMAGE.elements.previewBox),image=this.root.querySelector(_selectors.default.IMAGE.elements.preview),widthField=this.root.querySelector(_selectors.default.IMAGE.elements.width),heightField=this.root.querySelector(_selectors.default.IMAGE.elements.height),updateImageDimensions=()=>{const boxWidth=imagePreviewBox.clientWidth,boxHeight=imagePreviewBox.clientHeight,dimensions=this.fitSquareIntoBox(widthField.value,heightField.value,boxWidth,boxHeight);image.style.width="".concat(dimensions.width,"px"),image.style.height="".concat(dimensions.height,"px")};0===imagePreviewBox.clientWidth?this.currentModal.getRoot().on(_modal_events.default.shown,(()=>{updateImageDimensions()})):updateImageDimensions()})),_defineProperty(this,"fitSquareIntoBox",((squareWidth,squareHeight,boxWidth,boxHeight)=>{if(squareWidth(""===element.value&&(element.value=this.mediaDimensions.width),element.value))(this.root.querySelector(_selectors.default.IMAGE.elements.width)),currentHeight=(element=>(""===element.value&&(element.value=this.mediaDimensions.height),element.value))(this.root.querySelector(_selectors.default.IMAGE.elements.height)),preview=this.root.querySelector(_selectors.default.IMAGE.elements.preview);preview.setAttribute("src",image.src),preview.style.display="";const constrain=this.root.querySelector(_selectors.default.IMAGE.elements.constrain);if((0,_helpers.isPercentageValue)(currentWidth)&&(0,_helpers.isPercentageValue)(currentHeight))constrain.checked=currentWidth===currentHeight;else if(0===image.width||0===image.height)constrain.disabled="disabled";else{const widthRatio=Math.round(100*parseInt(currentWidth,10)/image.width),heightRatio=Math.round(100*parseInt(currentHeight,10)/image.height);constrain.checked=widthRatio===heightRatio}((currentWidth,currentHeight)=>{this.mediaDimensions.width===currentWidth&&this.mediaDimensions.height===currentHeight?(this.currentWidth=this.mediaDimensions.width,this.currentHeight=this.mediaDimensions.height,this.sizeChecked("original")):(this.currentWidth=currentWidth,this.currentHeight=currentHeight,this.sizeChecked("custom"))})(Number(currentWidth),Number(currentHeight))}presentationChanged(){const presentation=this.root.querySelector(_selectors.default.IMAGE.elements.presentation);this.root.querySelector(_selectors.default.IMAGE.elements.alt).disabled=presentation.checked,this.handleKeyupCharacterCount()}imageTypeChecked(){const isExternalUrl=!1===new RegExp("".concat(_config.default.wwwroot)).test(this.currentUrl);if((0,_helpers.hideElements)(_selectors.default.IMAGE.elements.url,this.root),isExternalUrl)this.setFilenameLabel(decodeURI(this.currentUrl));else{const filename=this.currentUrl.split("/").pop().split("?")[0];this.setFilenameLabel(decodeURI(filename))}}setFilenameLabel(label){const urlLabelEle=this.root.querySelector(_selectors.default.IMAGE.elements.fileNameLabel);urlLabelEle&&(urlLabelEle.innerHTML=label,urlLabelEle.setAttribute("title",label))}toggleAriaInvalid(selectors,predicate){selectors.forEach((selector=>{this.root.querySelectorAll(selector).forEach((element=>element.setAttribute("aria-invalid",predicate)))}))}hasErrorUrlField(){const urlError=""===this.currentUrl;return urlError?(0,_helpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root):(0,_helpers.hideElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.url],urlError),urlError}hasErrorAltField(){const alt=this.root.querySelector(_selectors.default.IMAGE.elements.alt).value,presentation=this.root.querySelector(_selectors.default.IMAGE.elements.presentation).checked,imageAltError=""===alt&&!presentation;return imageAltError?(0,_helpers.showElements)(_selectors.default.IMAGE.elements.altWarning,this.root):(0,_helpers.hideElements)(_selectors.default.IMAGE.elements.altWarning,this.root),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.alt,_selectors.default.IMAGE.elements.presentation],imageAltError),imageAltError}updateWarning(){const urlError=this.hasErrorUrlField(),imageAltError=this.hasErrorAltField();return urlError||imageAltError}getImageContext(){if(this.updateWarning())return null;const classList=[],constrain=this.root.querySelector(_selectors.default.IMAGE.elements.constrain).checked,sizeOriginal=this.root.querySelector(_selectors.default.IMAGE.elements.sizeOriginal).checked;return constrain||sizeOriginal?classList.push(_selectors.default.IMAGE.styles.responsive):classList.pop(_selectors.default.IMAGE.styles.responsive),{url:this.currentUrl,alt:this.root.querySelector(_selectors.default.IMAGE.elements.alt).value,width:this.root.querySelector(_selectors.default.IMAGE.elements.width).value,height:this.root.querySelector(_selectors.default.IMAGE.elements.height).value,presentation:this.root.querySelector(_selectors.default.IMAGE.elements.presentation).checked,customStyle:this.root.querySelector(_selectors.default.IMAGE.elements.customStyle).value,classlist:classList.join(" ")}}setImage(){const pendingPromise=new _pending.default("tiny_media:setImage");if(""===this.currentUrl)return;if(this.updateWarning())return void pendingPromise.resolve();const width=this.root.querySelector(_selectors.default.IMAGE.elements.width).value;if(!(0,_helpers.isPercentageValue)(width)&&isNaN(parseInt(width,10)))return this.root.querySelector(_selectors.default.IMAGE.elements.width).focus(),void pendingPromise.resolve();const height=this.root.querySelector(_selectors.default.IMAGE.elements.height).value;if(!(0,_helpers.isPercentageValue)(height)&&isNaN(parseInt(height,10)))return this.root.querySelector(_selectors.default.IMAGE.elements.height).focus(),void pendingPromise.resolve();_templates.default.render("tiny_media/image",this.getImageContext()).then((html=>(this.editor.insertContent(html),this.currentModal.destroy(),pendingPromise.resolve(),html))).catch((error=>{window.console.log(error)}))}deleteImage(){_notification.default.deleteCancelPromise((0,_str.getString)("deleteimage","tiny_media"),(0,_str.getString)("deleteimagewarning","tiny_media")).then((()=>{(0,_helpers.hideElements)(_selectors.default.IMAGE.elements.altWarning,this.root),this.loadInsertImage()})).catch((error=>{window.console.log(error)}))}registerEventListeners(){this.root.querySelector(_selectors.default.IMAGE.actions.submit).addEventListener("click",(e=>{e.preventDefault(),this.setImage()}));const deleteImageEle=this.root.querySelector(_selectors.default.IMAGE.actions.deleteImage);deleteImageEle.addEventListener("click",(()=>{this.deleteImage()})),deleteImageEle.addEventListener("keydown",(e=>{"Enter"===e.key&&this.deleteImage()})),this.root.addEventListener("change",(e=>{e.target.closest(_selectors.default.IMAGE.elements.presentation)&&this.presentationChanged();e.target.closest(_selectors.default.IMAGE.elements.constrain)&&this.autoAdjustSize();e.target.closest(_selectors.default.IMAGE.elements.sizeOriginal)&&this.sizeChecked("original");e.target.closest(_selectors.default.IMAGE.elements.sizeCustom)&&this.sizeChecked("custom")})),this.root.addEventListener("blur",(e=>{if(e.target.nodeType===Node.ELEMENT_NODE){e.target.closest(_selectors.default.IMAGE.elements.presentation)&&this.presentationChanged()}}),!0),this.root.addEventListener("keyup",(e=>{e.target.closest(_selectors.default.IMAGE.elements.alt)&&this.handleKeyupCharacterCount()})),this.root.addEventListener("input",(e=>{const widthEle=e.target.closest(_selectors.default.IMAGE.elements.width);widthEle&&(widthEle.value=""===widthEle.value?0:Number(widthEle.value),this.autoAdjustSize());const heightEle=e.target.closest(_selectors.default.IMAGE.elements.height);heightEle&&(heightEle.value=""===heightEle.value?0:Number(heightEle.value),this.autoAdjustSize(!0))}))}handleKeyupCharacterCount(){const alt=this.root.querySelector(_selectors.default.IMAGE.elements.alt).value;this.root.querySelector("#currentcount").innerHTML=alt.length}}_exports.ImageDetails=ImageDetails})); //# sourceMappingURL=imagedetails.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/imagedetails.min.js.map b/lib/editor/tiny/plugins/media/amd/build/imagedetails.min.js.map index 0491a9c9c6a..ab261dcd35b 100644 --- a/lib/editor/tiny/plugins/media/amd/build/imagedetails.min.js.map +++ b/lib/editor/tiny/plugins/media/amd/build/imagedetails.min.js.map @@ -1 +1 @@ -{"version":3,"file":"imagedetails.min.js","sources":["../src/imagedetails.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 details class for Moodle.\n *\n * @module tiny_media/imagedetails\n * @copyright 2024 Meirza \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Config from 'core/config';\nimport ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Selectors from './selectors';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport {ImageInsert} from 'tiny_media/imageinsert';\nimport {\n bodyImageInsert,\n footerImageInsert,\n showElements,\n hideElements,\n isPercentageValue,\n} from 'tiny_media/imagehelpers';\n\nexport class ImageDetails {\n DEFAULTS = {\n WIDTH: 160,\n HEIGHT: 160,\n };\n\n rawImageDimensions = null;\n\n constructor(\n root,\n editor,\n currentModal,\n canShowFilePicker,\n canShowDropZone,\n currentUrl,\n image,\n ) {\n this.root = root;\n this.editor = editor;\n this.currentModal = currentModal;\n this.canShowFilePicker = canShowFilePicker;\n this.canShowDropZone = canShowDropZone;\n this.currentUrl = currentUrl;\n this.image = image;\n }\n\n init = function() {\n this.currentModal.setTitle(getString('imagedetails', 'tiny_media'));\n this.imageTypeChecked();\n this.presentationChanged();\n this.storeImageDimensions(this.image);\n this.setImageDimensions();\n this.registerEventListeners();\n };\n\n /**\n * Loads and displays a preview image based on the provided URL, and handles image loading events.\n */\n loadInsertImage = async function() {\n const templateContext = {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n showdropzone: this.canShowDropZone,\n };\n\n Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])\n .then(() => {\n const imageinsert = new ImageInsert(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n );\n imageinsert.init();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n };\n\n storeImageDimensions(image) {\n // Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG).\n this.rawImageDimensions = {\n width: image.width || this.DEFAULTS.WIDTH,\n height: image.height || this.DEFAULTS.HEIGHT,\n };\n\n const getCurrentWidth = (element) => {\n if (element.value === '') {\n element.value = this.rawImageDimensions.width;\n }\n return element.value;\n };\n\n const getCurrentHeight = (element) => {\n if (element.value === '') {\n element.value = this.rawImageDimensions.height;\n }\n return element.value;\n };\n\n const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width);\n const currentWidth = getCurrentWidth(widthInput);\n\n const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height);\n const currentHeight = getCurrentHeight(heightInput);\n\n const preview = this.root.querySelector(Selectors.IMAGE.elements.preview);\n preview.setAttribute('src', image.src);\n preview.style.display = '';\n\n // Ensure the checkbox always in unchecked status when an image loads at first.\n const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain);\n if (isPercentageValue(currentWidth) && isPercentageValue(currentHeight)) {\n constrain.checked = currentWidth === currentHeight;\n } else if (image.width === 0 || image.height === 0) {\n // If we don't have both dimensions of the image, we can't auto-size it, so disable control.\n constrain.disabled = 'disabled';\n } else {\n // This is the same as comparing to 3 decimal places.\n const widthRatio = Math.round(100 * parseInt(currentWidth, 10) / image.width);\n const heightRatio = Math.round(100 * parseInt(currentHeight, 10) / image.height);\n constrain.checked = widthRatio === heightRatio;\n }\n\n /**\n * Sets the selected size option based on current width and height values.\n *\n * @param {number} currentWidth - The current width value.\n * @param {number} currentHeight - The current height value.\n */\n const setSelectedSize = (currentWidth, currentHeight) => {\n if (this.rawImageDimensions.width === currentWidth &&\n this.rawImageDimensions.height === currentHeight\n ) {\n this.currentWidth = this.rawImageDimensions.width;\n this.currentHeight = this.rawImageDimensions.height;\n this.sizeChecked('original');\n } else {\n this.currentWidth = currentWidth;\n this.currentHeight = currentHeight;\n this.sizeChecked('custom');\n }\n };\n\n setSelectedSize(Number(currentWidth), Number(currentHeight));\n }\n\n /**\n * Handles the selection of image size options and updates the form inputs accordingly.\n *\n * @param {string} option - The selected image size option (\"original\" or \"custom\").\n */\n sizeChecked(option) {\n const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width);\n const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height);\n if (option === \"original\") {\n this.sizeOriginalChecked();\n widthInput.value = this.rawImageDimensions.width;\n heightInput.value = this.rawImageDimensions.height;\n } else if (option === \"custom\") {\n this.sizeCustomChecked();\n widthInput.value = this.currentWidth;\n heightInput.value = this.currentHeight;\n\n // If the current size is equal to the original size, then check the Keep proportion checkbox.\n if (this.currentWidth === this.rawImageDimensions.width && this.currentHeight === this.rawImageDimensions.height) {\n const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain);\n constrainField.checked = true;\n }\n }\n this.autoAdjustSize();\n }\n\n autoAdjustSize(forceHeight = false) {\n // If we do not know the image size, do not do anything.\n if (!this.rawImageDimensions) {\n return;\n }\n\n const widthField = this.root.querySelector(Selectors.IMAGE.elements.width);\n const heightField = this.root.querySelector(Selectors.IMAGE.elements.height);\n\n const normalizeFieldData = (fieldData) => {\n fieldData.isPercentageValue = !!isPercentageValue(fieldData.field.value);\n if (fieldData.isPercentageValue) {\n fieldData.percentValue = parseInt(fieldData.field.value, 10);\n fieldData.pixelSize = this.rawImageDimensions[fieldData.type] / 100 * fieldData.percentValue;\n } else {\n fieldData.pixelSize = parseInt(fieldData.field.value, 10);\n fieldData.percentValue = fieldData.pixelSize / this.rawImageDimensions[fieldData.type] * 100;\n }\n\n return fieldData;\n };\n\n const getKeyField = () => {\n const getValue = () => {\n if (forceHeight) {\n return {\n field: heightField,\n type: 'height',\n };\n } else {\n return {\n field: widthField,\n type: 'width',\n };\n }\n };\n\n const currentValue = getValue();\n if (currentValue.field.value === '') {\n currentValue.field.value = this.rawImageDimensions[currentValue.type];\n }\n\n return normalizeFieldData(currentValue);\n };\n\n const getRelativeField = () => {\n if (forceHeight) {\n return normalizeFieldData({\n field: widthField,\n type: 'width',\n });\n } else {\n return normalizeFieldData({\n field: heightField,\n type: 'height',\n });\n }\n };\n\n // Now update with the new values.\n const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain);\n if (constrainField.checked) {\n const keyField = getKeyField();\n const relativeField = getRelativeField();\n // We are keeping the image in proportion.\n // Calculate the size for the relative field.\n if (keyField.isPercentageValue) {\n // In proportion, so the percentages are the same.\n relativeField.field.value = keyField.field.value;\n relativeField.percentValue = keyField.percentValue;\n } else {\n relativeField.pixelSize = Math.round(\n keyField.pixelSize / this.rawImageDimensions[keyField.type] * this.rawImageDimensions[relativeField.type]\n );\n relativeField.field.value = relativeField.pixelSize;\n }\n }\n\n // Store the custom width and height to reuse.\n this.currentWidth = Number(widthField.value) !== this.rawImageDimensions.width ? widthField.value : this.currentWidth;\n this.currentHeight = Number(heightField.value) !== this.rawImageDimensions.height ? heightField.value : this.currentHeight;\n }\n\n /**\n * Sets the dimensions of the image preview element based on user input and constraints.\n */\n setImageDimensions = () => {\n const imagePreviewBox = this.root.querySelector(Selectors.IMAGE.elements.previewBox);\n const image = this.root.querySelector(Selectors.IMAGE.elements.preview);\n const widthField = this.root.querySelector(Selectors.IMAGE.elements.width);\n const heightField = this.root.querySelector(Selectors.IMAGE.elements.height);\n\n const updateImageDimensions = () => {\n // Get the latest dimensions of the preview box for responsiveness.\n const boxWidth = imagePreviewBox.clientWidth;\n const boxHeight = imagePreviewBox.clientHeight;\n // Get the new width and height for the image.\n const dimensions = this.fitSquareIntoBox(widthField.value, heightField.value, boxWidth, boxHeight);\n image.style.width = `${dimensions.width}px`;\n image.style.height = `${dimensions.height}px`;\n };\n // If the client size is zero, then get the new dimensions once the modal is shown.\n if (imagePreviewBox.clientWidth === 0) {\n // Call the shown event.\n this.currentModal.getRoot().on(ModalEvents.shown, () => {\n updateImageDimensions();\n });\n } else {\n updateImageDimensions();\n }\n };\n\n /**\n * Handles the selection of the \"Original Size\" option and updates the form elements accordingly.\n */\n sizeOriginalChecked() {\n this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = true;\n this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = false;\n hideElements(Selectors.IMAGE.elements.properties, this.root);\n }\n\n /**\n * Handles the selection of the \"Custom Size\" option and updates the form elements accordingly.\n */\n sizeCustomChecked() {\n this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = false;\n this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = true;\n showElements(Selectors.IMAGE.elements.properties, this.root);\n }\n\n /**\n * Handles changes in the image presentation checkbox and enables/disables the image alt text input accordingly.\n */\n presentationChanged() {\n const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation);\n const alt = this.root.querySelector(Selectors.IMAGE.elements.alt);\n alt.disabled = presentation.checked;\n\n // Counting the image description characters.\n this.handleKeyupCharacterCount();\n }\n\n /**\n * This function checks whether an image URL is local (within the same website's domain) or external (from an external source).\n * Depending on the result, it dynamically updates the visibility and content of HTML elements in a user interface.\n * If the image is local then we only show it's filename.\n * If the image is external then it will show full URL and it can be updated.\n */\n imageTypeChecked() {\n const regex = new RegExp(`${Config.wwwroot}`);\n\n // True if the URL is from external, otherwise false.\n const isExternalUrl = regex.test(this.currentUrl) === false;\n\n // Hide the URL input.\n hideElements(Selectors.IMAGE.elements.url, this.root);\n\n if (!isExternalUrl) {\n // Split the URL by '/' to get an array of segments.\n const segments = this.currentUrl.split('/');\n // Get the last segment, which should be the filename.\n const filename = segments.pop().split('?')[0];\n // Show the file name.\n this.setFilenameLabel(decodeURI(filename));\n } else {\n\n this.setFilenameLabel(decodeURI(this.currentUrl));\n }\n }\n\n /**\n * Set the string for the URL label element.\n *\n * @param {string} label - The label text to set.\n */\n setFilenameLabel(label) {\n const urlLabelEle = this.root.querySelector(Selectors.IMAGE.elements.fileNameLabel);\n if (urlLabelEle) {\n urlLabelEle.innerHTML = label;\n urlLabelEle.setAttribute(\"title\", label);\n }\n }\n\n toggleAriaInvalid(selectors, predicate) {\n selectors.forEach((selector) => {\n const elements = this.root.querySelectorAll(selector);\n elements.forEach((element) => element.setAttribute('aria-invalid', predicate));\n });\n }\n\n hasErrorUrlField() {\n const urlError = this.currentUrl === '';\n if (urlError) {\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n } else {\n hideElements(Selectors.IMAGE.elements.urlWarning, this.root);\n }\n this.toggleAriaInvalid([Selectors.IMAGE.elements.url], urlError);\n\n return urlError;\n }\n\n hasErrorAltField() {\n const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value;\n const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation).checked;\n const imageAltError = alt === '' && !presentation;\n if (imageAltError) {\n showElements(Selectors.IMAGE.elements.altWarning, this.root);\n } else {\n hideElements(Selectors.IMAGE.elements.urlWaaltWarningrning, this.root);\n }\n this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError);\n\n return imageAltError;\n }\n\n updateWarning() {\n const urlError = this.hasErrorUrlField();\n const imageAltError = this.hasErrorAltField();\n\n return urlError || imageAltError;\n }\n\n getImageContext() {\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n return null;\n }\n\n const classList = [];\n const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain).checked;\n const sizeOriginal = this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked;\n if (constrain || sizeOriginal) {\n // If the Auto size checkbox is checked or the Original size is checked, then apply the responsive class.\n classList.push(Selectors.IMAGE.styles.responsive);\n } else {\n // Otherwise, remove it.\n classList.pop(Selectors.IMAGE.styles.responsive);\n }\n\n return {\n url: this.currentUrl,\n alt: this.root.querySelector(Selectors.IMAGE.elements.alt).value,\n width: this.root.querySelector(Selectors.IMAGE.elements.width).value,\n height: this.root.querySelector(Selectors.IMAGE.elements.height).value,\n presentation: this.root.querySelector(Selectors.IMAGE.elements.presentation).checked,\n customStyle: this.root.querySelector(Selectors.IMAGE.elements.customStyle).value,\n classlist: classList.join(' '),\n };\n }\n\n setImage() {\n const pendingPromise = new Pending('tiny_media:setImage');\n const url = this.currentUrl;\n if (url === '') {\n return;\n }\n\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n pendingPromise.resolve();\n return;\n }\n\n // Check for invalid width or height.\n const width = this.root.querySelector(Selectors.IMAGE.elements.width).value;\n if (!isPercentageValue(width) && isNaN(parseInt(width, 10))) {\n this.root.querySelector(Selectors.IMAGE.elements.width).focus();\n pendingPromise.resolve();\n return;\n }\n\n const height = this.root.querySelector(Selectors.IMAGE.elements.height).value;\n if (!isPercentageValue(height) && isNaN(parseInt(height, 10))) {\n this.root.querySelector(Selectors.IMAGE.elements.height).focus();\n pendingPromise.resolve();\n return;\n }\n\n Templates.render('tiny_media/image', this.getImageContext())\n .then((html) => {\n this.editor.insertContent(html);\n this.currentModal.destroy();\n pendingPromise.resolve();\n\n return html;\n })\n .catch(error => {\n window.console.log(error);\n });\n }\n\n /**\n * Deletes the image after confirming with the user and loads the insert image page.\n */\n deleteImage() {\n Notification.deleteCancelPromise(\n getString('deleteimage', 'tiny_media'),\n getString('deleteimagewarning', 'tiny_media'),\n ).then(() => {\n hideElements(Selectors.IMAGE.elements.altWarning, this.root);\n // Removing the image in the preview will bring the user to the insert page.\n this.loadInsertImage();\n return;\n }).catch(error => {\n window.console.log(error);\n });\n }\n\n registerEventListeners() {\n const submitAction = this.root.querySelector(Selectors.IMAGE.actions.submit);\n submitAction.addEventListener('click', (e) => {\n e.preventDefault();\n this.setImage();\n });\n\n const deleteImageEle = this.root.querySelector(Selectors.IMAGE.actions.deleteImage);\n deleteImageEle.addEventListener('click', () => {\n this.deleteImage();\n });\n deleteImageEle.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\") {\n this.deleteImage();\n }\n });\n\n this.root.addEventListener('change', (e) => {\n const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);\n if (presentationEle) {\n this.presentationChanged();\n }\n\n const constrainEle = e.target.closest(Selectors.IMAGE.elements.constrain);\n if (constrainEle) {\n this.autoAdjustSize();\n }\n\n const sizeOriginalEle = e.target.closest(Selectors.IMAGE.elements.sizeOriginal);\n if (sizeOriginalEle) {\n this.sizeChecked('original');\n }\n\n const sizeCustomEle = e.target.closest(Selectors.IMAGE.elements.sizeCustom);\n if (sizeCustomEle) {\n this.sizeChecked('custom');\n }\n });\n\n this.root.addEventListener('blur', (e) => {\n if (e.target.nodeType === Node.ELEMENT_NODE) {\n\n const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);\n if (presentationEle) {\n this.presentationChanged();\n }\n }\n }, true);\n\n // Character count.\n this.root.addEventListener('keyup', (e) => {\n const altEle = e.target.closest(Selectors.IMAGE.elements.alt);\n if (altEle) {\n this.handleKeyupCharacterCount();\n }\n });\n\n this.root.addEventListener('input', (e) => {\n const widthEle = e.target.closest(Selectors.IMAGE.elements.width);\n if (widthEle) {\n // Avoid empty value.\n widthEle.value = widthEle.value === \"\" ? 0 : Number(widthEle.value);\n this.autoAdjustSize();\n }\n\n const heightEle = e.target.closest(Selectors.IMAGE.elements.height);\n if (heightEle) {\n // Avoid empty value.\n heightEle.value = heightEle.value === \"\" ? 0 : Number(heightEle.value);\n this.autoAdjustSize(true);\n }\n });\n }\n\n handleKeyupCharacterCount() {\n const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value;\n const current = this.root.querySelector('#currentcount');\n current.innerHTML = alt.length;\n }\n\n /**\n * Calculates the dimensions to fit a square into a specified box while maintaining aspect ratio.\n *\n * @param {number} squareWidth - The width of the square.\n * @param {number} squareHeight - The height of the square.\n * @param {number} boxWidth - The width of the box.\n * @param {number} boxHeight - The height of the box.\n * @returns {Object} An object with the new width and height of the square to fit in the box.\n */\n fitSquareIntoBox = (squareWidth, squareHeight, boxWidth, boxHeight) => {\n if (squareWidth < boxWidth && squareHeight < boxHeight) {\n // If the square is smaller than the box, keep its dimensions.\n return {\n width: squareWidth,\n height: squareHeight,\n };\n }\n // Calculate the scaling factor based on the minimum scaling required to fit in the box.\n const widthScaleFactor = boxWidth / squareWidth;\n const heightScaleFactor = boxHeight / squareHeight;\n const minScaleFactor = Math.min(widthScaleFactor, heightScaleFactor);\n // Scale the square's dimensions based on the aspect ratio and the minimum scaling factor.\n const newWidth = squareWidth * minScaleFactor;\n const newHeight = squareHeight * minScaleFactor;\n return {\n width: newWidth,\n height: newHeight,\n };\n };\n}\n"],"names":["constructor","root","editor","currentModal","canShowFilePicker","canShowDropZone","currentUrl","image","WIDTH","HEIGHT","setTitle","imageTypeChecked","presentationChanged","storeImageDimensions","this","setImageDimensions","registerEventListeners","async","templateContext","elementid","id","showfilepicker","showdropzone","Promise","all","then","ImageInsert","init","catch","error","window","console","log","imagePreviewBox","querySelector","Selectors","IMAGE","elements","previewBox","preview","widthField","width","heightField","height","updateImageDimensions","boxWidth","clientWidth","boxHeight","clientHeight","dimensions","fitSquareIntoBox","value","style","getRoot","on","ModalEvents","shown","squareWidth","squareHeight","widthScaleFactor","heightScaleFactor","minScaleFactor","Math","min","rawImageDimensions","DEFAULTS","currentWidth","element","getCurrentWidth","currentHeight","getCurrentHeight","setAttribute","src","display","constrain","checked","disabled","widthRatio","round","parseInt","heightRatio","sizeChecked","setSelectedSize","Number","option","widthInput","heightInput","sizeOriginalChecked","sizeCustomChecked","autoAdjustSize","forceHeight","normalizeFieldData","fieldData","isPercentageValue","field","percentValue","pixelSize","type","getKeyField","currentValue","keyField","relativeField","sizeOriginal","sizeCustom","properties","presentation","alt","handleKeyupCharacterCount","isExternalUrl","RegExp","Config","wwwroot","test","url","setFilenameLabel","decodeURI","filename","split","pop","label","urlLabelEle","fileNameLabel","innerHTML","toggleAriaInvalid","selectors","predicate","forEach","selector","querySelectorAll","hasErrorUrlField","urlError","urlWarning","hasErrorAltField","imageAltError","altWarning","urlWaaltWarningrning","updateWarning","getImageContext","classList","push","styles","responsive","customStyle","classlist","join","setImage","pendingPromise","Pending","resolve","isNaN","focus","render","html","insertContent","destroy","deleteImage","deleteCancelPromise","loadInsertImage","actions","submit","addEventListener","e","preventDefault","deleteImageEle","key","target","closest","nodeType","Node","ELEMENT_NODE","widthEle","heightEle","length"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"imagedetails.min.js","sources":["../src/imagedetails.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 details class for Moodle.\n *\n * @module tiny_media/imagedetails\n * @copyright 2024 Meirza \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Config from 'core/config';\nimport ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Selectors from './selectors';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport {ImageInsert} from 'tiny_media/imageinsert';\nimport {MediaBase} from './mediabase';\nimport {\n body,\n footer,\n hideElements,\n showElements,\n isPercentageValue,\n} from './helpers';\n\nexport class ImageDetails extends MediaBase {\n DEFAULTS = {\n WIDTH: 160,\n HEIGHT: 160,\n };\n\n selectorType = 'IMAGE';\n\n mediaDimensions = null;\n\n constructor(\n root,\n editor,\n currentModal,\n canShowFilePicker,\n canShowDropZone,\n currentUrl,\n image,\n ) {\n super();\n this.root = root;\n this.editor = editor;\n this.currentModal = currentModal;\n this.canShowFilePicker = canShowFilePicker;\n this.canShowDropZone = canShowDropZone;\n this.currentUrl = currentUrl;\n this.image = image;\n }\n\n init = function() {\n this.currentModal.setTitle(getString('imagedetails', 'tiny_media'));\n this.imageTypeChecked();\n this.presentationChanged();\n this.storeImageDimensions(this.image);\n this.setImageDimensions();\n this.registerEventListeners();\n };\n\n /**\n * Loads and displays a preview image based on the provided URL, and handles image loading events.\n */\n loadInsertImage = async function() {\n const templateContext = {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n showdropzone: this.canShowDropZone,\n bodyTemplate: Selectors.IMAGE.template.body.insertImageBody,\n footerTemplate: Selectors.IMAGE.template.footer.insertImageFooter,\n selector: 'IMAGE',\n };\n\n Promise.all([body(templateContext, this.root), footer(templateContext, this.root)])\n .then(() => {\n const imageinsert = new ImageInsert(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n );\n imageinsert.init();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n };\n\n storeImageDimensions(image) {\n // Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG).\n this.mediaDimensions = {\n width: image.width || this.DEFAULTS.WIDTH,\n height: image.height || this.DEFAULTS.HEIGHT,\n };\n\n const getCurrentWidth = (element) => {\n if (element.value === '') {\n element.value = this.mediaDimensions.width;\n }\n return element.value;\n };\n\n const getCurrentHeight = (element) => {\n if (element.value === '') {\n element.value = this.mediaDimensions.height;\n }\n return element.value;\n };\n\n const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width);\n const currentWidth = getCurrentWidth(widthInput);\n\n const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height);\n const currentHeight = getCurrentHeight(heightInput);\n\n const preview = this.root.querySelector(Selectors.IMAGE.elements.preview);\n preview.setAttribute('src', image.src);\n preview.style.display = '';\n\n // Ensure the checkbox always in unchecked status when an image loads at first.\n const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain);\n if (isPercentageValue(currentWidth) && isPercentageValue(currentHeight)) {\n constrain.checked = currentWidth === currentHeight;\n } else if (image.width === 0 || image.height === 0) {\n // If we don't have both dimensions of the image, we can't auto-size it, so disable control.\n constrain.disabled = 'disabled';\n } else {\n // This is the same as comparing to 3 decimal places.\n const widthRatio = Math.round(100 * parseInt(currentWidth, 10) / image.width);\n const heightRatio = Math.round(100 * parseInt(currentHeight, 10) / image.height);\n constrain.checked = widthRatio === heightRatio;\n }\n\n /**\n * Sets the selected size option based on current width and height values.\n *\n * @param {number} currentWidth - The current width value.\n * @param {number} currentHeight - The current height value.\n */\n const setSelectedSize = (currentWidth, currentHeight) => {\n if (this.mediaDimensions.width === currentWidth &&\n this.mediaDimensions.height === currentHeight\n ) {\n this.currentWidth = this.mediaDimensions.width;\n this.currentHeight = this.mediaDimensions.height;\n this.sizeChecked('original');\n } else {\n this.currentWidth = currentWidth;\n this.currentHeight = currentHeight;\n this.sizeChecked('custom');\n }\n };\n\n setSelectedSize(Number(currentWidth), Number(currentHeight));\n }\n\n /**\n * Sets the dimensions of the image preview element based on user input and constraints.\n */\n setImageDimensions = () => {\n const imagePreviewBox = this.root.querySelector(Selectors.IMAGE.elements.previewBox);\n const image = this.root.querySelector(Selectors.IMAGE.elements.preview);\n const widthField = this.root.querySelector(Selectors.IMAGE.elements.width);\n const heightField = this.root.querySelector(Selectors.IMAGE.elements.height);\n\n const updateImageDimensions = () => {\n // Get the latest dimensions of the preview box for responsiveness.\n const boxWidth = imagePreviewBox.clientWidth;\n const boxHeight = imagePreviewBox.clientHeight;\n // Get the new width and height for the image.\n const dimensions = this.fitSquareIntoBox(widthField.value, heightField.value, boxWidth, boxHeight);\n image.style.width = `${dimensions.width}px`;\n image.style.height = `${dimensions.height}px`;\n };\n // If the client size is zero, then get the new dimensions once the modal is shown.\n if (imagePreviewBox.clientWidth === 0) {\n // Call the shown event.\n this.currentModal.getRoot().on(ModalEvents.shown, () => {\n updateImageDimensions();\n });\n } else {\n updateImageDimensions();\n }\n };\n\n /**\n * Handles changes in the image presentation checkbox and enables/disables the image alt text input accordingly.\n */\n presentationChanged() {\n const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation);\n const alt = this.root.querySelector(Selectors.IMAGE.elements.alt);\n alt.disabled = presentation.checked;\n\n // Counting the image description characters.\n this.handleKeyupCharacterCount();\n }\n\n /**\n * This function checks whether an image URL is local (within the same website's domain) or external (from an external source).\n * Depending on the result, it dynamically updates the visibility and content of HTML elements in a user interface.\n * If the image is local then we only show it's filename.\n * If the image is external then it will show full URL and it can be updated.\n */\n imageTypeChecked() {\n const regex = new RegExp(`${Config.wwwroot}`);\n\n // True if the URL is from external, otherwise false.\n const isExternalUrl = regex.test(this.currentUrl) === false;\n\n // Hide the URL input.\n hideElements(Selectors.IMAGE.elements.url, this.root);\n\n if (!isExternalUrl) {\n // Split the URL by '/' to get an array of segments.\n const segments = this.currentUrl.split('/');\n // Get the last segment, which should be the filename.\n const filename = segments.pop().split('?')[0];\n // Show the file name.\n this.setFilenameLabel(decodeURI(filename));\n } else {\n\n this.setFilenameLabel(decodeURI(this.currentUrl));\n }\n }\n\n /**\n * Set the string for the URL label element.\n *\n * @param {string} label - The label text to set.\n */\n setFilenameLabel(label) {\n const urlLabelEle = this.root.querySelector(Selectors.IMAGE.elements.fileNameLabel);\n if (urlLabelEle) {\n urlLabelEle.innerHTML = label;\n urlLabelEle.setAttribute(\"title\", label);\n }\n }\n\n toggleAriaInvalid(selectors, predicate) {\n selectors.forEach((selector) => {\n const elements = this.root.querySelectorAll(selector);\n elements.forEach((element) => element.setAttribute('aria-invalid', predicate));\n });\n }\n\n hasErrorUrlField() {\n const urlError = this.currentUrl === '';\n if (urlError) {\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n } else {\n hideElements(Selectors.IMAGE.elements.urlWarning, this.root);\n }\n this.toggleAriaInvalid([Selectors.IMAGE.elements.url], urlError);\n\n return urlError;\n }\n\n hasErrorAltField() {\n const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value;\n const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation).checked;\n const imageAltError = alt === '' && !presentation;\n if (imageAltError) {\n showElements(Selectors.IMAGE.elements.altWarning, this.root);\n } else {\n hideElements(Selectors.IMAGE.elements.altWarning, this.root);\n }\n this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError);\n\n return imageAltError;\n }\n\n updateWarning() {\n const urlError = this.hasErrorUrlField();\n const imageAltError = this.hasErrorAltField();\n\n return urlError || imageAltError;\n }\n\n getImageContext() {\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n return null;\n }\n\n const classList = [];\n const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain).checked;\n const sizeOriginal = this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked;\n if (constrain || sizeOriginal) {\n // If the Auto size checkbox is checked or the Original size is checked, then apply the responsive class.\n classList.push(Selectors.IMAGE.styles.responsive);\n } else {\n // Otherwise, remove it.\n classList.pop(Selectors.IMAGE.styles.responsive);\n }\n\n return {\n url: this.currentUrl,\n alt: this.root.querySelector(Selectors.IMAGE.elements.alt).value,\n width: this.root.querySelector(Selectors.IMAGE.elements.width).value,\n height: this.root.querySelector(Selectors.IMAGE.elements.height).value,\n presentation: this.root.querySelector(Selectors.IMAGE.elements.presentation).checked,\n customStyle: this.root.querySelector(Selectors.IMAGE.elements.customStyle).value,\n classlist: classList.join(' '),\n };\n }\n\n setImage() {\n const pendingPromise = new Pending('tiny_media:setImage');\n const url = this.currentUrl;\n if (url === '') {\n return;\n }\n\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n pendingPromise.resolve();\n return;\n }\n\n // Check for invalid width or height.\n const width = this.root.querySelector(Selectors.IMAGE.elements.width).value;\n if (!isPercentageValue(width) && isNaN(parseInt(width, 10))) {\n this.root.querySelector(Selectors.IMAGE.elements.width).focus();\n pendingPromise.resolve();\n return;\n }\n\n const height = this.root.querySelector(Selectors.IMAGE.elements.height).value;\n if (!isPercentageValue(height) && isNaN(parseInt(height, 10))) {\n this.root.querySelector(Selectors.IMAGE.elements.height).focus();\n pendingPromise.resolve();\n return;\n }\n\n Templates.render('tiny_media/image', this.getImageContext())\n .then((html) => {\n this.editor.insertContent(html);\n this.currentModal.destroy();\n pendingPromise.resolve();\n\n return html;\n })\n .catch(error => {\n window.console.log(error);\n });\n }\n\n /**\n * Deletes the image after confirming with the user and loads the insert image page.\n */\n deleteImage() {\n Notification.deleteCancelPromise(\n getString('deleteimage', 'tiny_media'),\n getString('deleteimagewarning', 'tiny_media'),\n ).then(() => {\n hideElements(Selectors.IMAGE.elements.altWarning, this.root);\n // Removing the image in the preview will bring the user to the insert page.\n this.loadInsertImage();\n return;\n }).catch(error => {\n window.console.log(error);\n });\n }\n\n registerEventListeners() {\n const submitAction = this.root.querySelector(Selectors.IMAGE.actions.submit);\n submitAction.addEventListener('click', (e) => {\n e.preventDefault();\n this.setImage();\n });\n\n const deleteImageEle = this.root.querySelector(Selectors.IMAGE.actions.deleteImage);\n deleteImageEle.addEventListener('click', () => {\n this.deleteImage();\n });\n deleteImageEle.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\") {\n this.deleteImage();\n }\n });\n\n this.root.addEventListener('change', (e) => {\n const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);\n if (presentationEle) {\n this.presentationChanged();\n }\n\n const constrainEle = e.target.closest(Selectors.IMAGE.elements.constrain);\n if (constrainEle) {\n this.autoAdjustSize();\n }\n\n const sizeOriginalEle = e.target.closest(Selectors.IMAGE.elements.sizeOriginal);\n if (sizeOriginalEle) {\n this.sizeChecked('original');\n }\n\n const sizeCustomEle = e.target.closest(Selectors.IMAGE.elements.sizeCustom);\n if (sizeCustomEle) {\n this.sizeChecked('custom');\n }\n });\n\n this.root.addEventListener('blur', (e) => {\n if (e.target.nodeType === Node.ELEMENT_NODE) {\n\n const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);\n if (presentationEle) {\n this.presentationChanged();\n }\n }\n }, true);\n\n // Character count.\n this.root.addEventListener('keyup', (e) => {\n const altEle = e.target.closest(Selectors.IMAGE.elements.alt);\n if (altEle) {\n this.handleKeyupCharacterCount();\n }\n });\n\n this.root.addEventListener('input', (e) => {\n const widthEle = e.target.closest(Selectors.IMAGE.elements.width);\n if (widthEle) {\n // Avoid empty value.\n widthEle.value = widthEle.value === \"\" ? 0 : Number(widthEle.value);\n this.autoAdjustSize();\n }\n\n const heightEle = e.target.closest(Selectors.IMAGE.elements.height);\n if (heightEle) {\n // Avoid empty value.\n heightEle.value = heightEle.value === \"\" ? 0 : Number(heightEle.value);\n this.autoAdjustSize(true);\n }\n });\n }\n\n handleKeyupCharacterCount() {\n const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value;\n const current = this.root.querySelector('#currentcount');\n current.innerHTML = alt.length;\n }\n\n /**\n * Calculates the dimensions to fit a square into a specified box while maintaining aspect ratio.\n *\n * @param {number} squareWidth - The width of the square.\n * @param {number} squareHeight - The height of the square.\n * @param {number} boxWidth - The width of the box.\n * @param {number} boxHeight - The height of the box.\n * @returns {Object} An object with the new width and height of the square to fit in the box.\n */\n fitSquareIntoBox = (squareWidth, squareHeight, boxWidth, boxHeight) => {\n if (squareWidth < boxWidth && squareHeight < boxHeight) {\n // If the square is smaller than the box, keep its dimensions.\n return {\n width: squareWidth,\n height: squareHeight,\n };\n }\n // Calculate the scaling factor based on the minimum scaling required to fit in the box.\n const widthScaleFactor = boxWidth / squareWidth;\n const heightScaleFactor = boxHeight / squareHeight;\n const minScaleFactor = Math.min(widthScaleFactor, heightScaleFactor);\n // Scale the square's dimensions based on the aspect ratio and the minimum scaling factor.\n const newWidth = squareWidth * minScaleFactor;\n const newHeight = squareHeight * minScaleFactor;\n return {\n width: newWidth,\n height: newHeight,\n };\n };\n}\n"],"names":["ImageDetails","MediaBase","constructor","root","editor","currentModal","canShowFilePicker","canShowDropZone","currentUrl","image","WIDTH","HEIGHT","setTitle","imageTypeChecked","presentationChanged","storeImageDimensions","this","setImageDimensions","registerEventListeners","async","templateContext","elementid","id","showfilepicker","showdropzone","bodyTemplate","Selectors","IMAGE","template","body","insertImageBody","footerTemplate","footer","insertImageFooter","selector","Promise","all","then","ImageInsert","init","catch","error","window","console","log","imagePreviewBox","querySelector","elements","previewBox","preview","widthField","width","heightField","height","updateImageDimensions","boxWidth","clientWidth","boxHeight","clientHeight","dimensions","fitSquareIntoBox","value","style","getRoot","on","ModalEvents","shown","squareWidth","squareHeight","widthScaleFactor","heightScaleFactor","minScaleFactor","Math","min","mediaDimensions","DEFAULTS","currentWidth","element","getCurrentWidth","currentHeight","getCurrentHeight","setAttribute","src","display","constrain","checked","disabled","widthRatio","round","parseInt","heightRatio","sizeChecked","setSelectedSize","Number","presentation","alt","handleKeyupCharacterCount","isExternalUrl","RegExp","Config","wwwroot","test","url","setFilenameLabel","decodeURI","filename","split","pop","label","urlLabelEle","fileNameLabel","innerHTML","toggleAriaInvalid","selectors","predicate","forEach","querySelectorAll","hasErrorUrlField","urlError","urlWarning","hasErrorAltField","imageAltError","altWarning","updateWarning","getImageContext","classList","sizeOriginal","push","styles","responsive","customStyle","classlist","join","setImage","pendingPromise","Pending","resolve","isNaN","focus","render","html","insertContent","destroy","deleteImage","deleteCancelPromise","loadInsertImage","actions","submit","addEventListener","e","preventDefault","deleteImageEle","key","target","closest","autoAdjustSize","sizeCustom","nodeType","Node","ELEMENT_NODE","widthEle","heightEle","length"],"mappings":"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"} \ 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 11e4cf975cc..2ab6ca2cf0b 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","./selectors","core/dropzone","editor_tiny/uploader","core/prefetch","core/str","./common","editor_tiny/options","editor_tiny/utils","tiny_media/imagedetails","tiny_media/imagehelpers"],(function(_exports,_selectors,_dropzone,_uploader,_prefetch,_str,_common,_options,_utils,_imagedetails,_imagehelpers){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.ImageInsert=void 0,_selectors=_interopRequireDefault(_selectors),_dropzone=_interopRequireDefault(_dropzone),_uploader=_interopRequireDefault(_uploader),(0,_prefetch.prefetchStrings)("tiny_media",["insertimage","enterurl","enterurlor","imageurlrequired","uploading","loading","addfilesdrop","sizecustom_help"]);_exports.ImageInsert=class{constructor(_root,editor,currentModal,canShowFilePicker,canShowDropZone){_defineProperty(this,"init",(async function(){const langStringKeys=["insertimage","enterurl","enterurlor","imageurlrequired","uploading","loading","addfilesdrop","sizecustom_help"],langStringvalues=await(0,_str.getStrings)([...langStringKeys].map((key=>({key:key,component:_common.component}))));if(this.langStrings=Object.fromEntries(langStringKeys.map(((key,index)=>[key,langStringvalues[index]]))),this.currentModal.setTitle(this.langStrings.insertimage),this.canShowDropZone){const dropZoneEle=document.querySelector(_selectors.default.IMAGE.elements.dropzoneContainer);let acceptedTypes=(0,_options.getFilePicker)(this.editor,"image").accepted_types;Array.isArray(acceptedTypes)&&(acceptedTypes=acceptedTypes.join(","));const dropZone=new _dropzone.default(dropZoneEle,acceptedTypes,(files=>{this.handleUploadedFile(files)}));dropZone.setLabel(this.langStrings.addfilesdrop),dropZone.init()}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",(()=>{let templateContext={};templateContext.sizecustomhelpicon={text:this.langStrings.sizecustom_help},templateContext.maxlengthalt=_imagehelpers.MAX_LENGTH_ALT,Promise.all([(0,_imagehelpers.bodyImageDetails)(templateContext,this.root),(0,_imagehelpers.footerImageDetails)(templateContext,this.root)]).then((()=>{new _imagedetails.ImageDetails(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone,this.currentUrl,image).init()})).then((()=>{this.stopImageLoading()})).catch((error=>{window.console.log(error)}))}))})),_defineProperty(this,"updateLoaderIcon",(function(root,langStrings){let progress=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const loaderIcon=root.querySelector(_selectors.default.IMAGE.elements.loaderIconContainer+" div");loaderIcon.innerHTML=null!==progress?"".concat(langStrings.uploading," ").concat(Math.round(progress),"%"):langStrings.loading})),_defineProperty(this,"handleUploadedFile",(async files=>{try{this.startImageLoading();const fileURL=await(0,_uploader.default)(this.editor,"image",files[0],files[0].name,(progress=>{this.updateLoaderIcon(this.root,this.langStrings,progress)}));this.updateLoaderIcon(this.root,this.langStrings),this.filePickerCallback({url:fileURL})}catch(error){this.root.querySelector(_selectors.default.IMAGE.elements.urlWarning).innerHTML=void 0!==error.error?error.error:error,(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),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)}filePickerCallback(params){params.url&&this.loadPreviewImage(params.url)}registerEventListeners(){this.root.addEventListener("click",(async e=>{e.target.closest(_selectors.default.IMAGE.actions.addUrl)&&this.urlChanged();if(e.target.closest(_selectors.default.IMAGE.actions.imageBrowser)&&this.canShowFilePicker){e.preventDefault();const params=await(0,_utils.displayFilepicker)(this.editor,"image");this.filePickerCallback(params)}})),this.root.addEventListener("input",(e=>{e.target.closest(_selectors.default.IMAGE.elements.url)&&this.toggleUrlButton()}));const fileInput=this.root.querySelector(_selectors.default.IMAGE.elements.fileInput);fileInput&&fileInput.addEventListener("change",(()=>{this.handleUploadedFile(fileInput.files)}))}}})); +define("tiny_media/imageinsert",["exports","./selectors","core/dropzone","editor_tiny/uploader","core/prefetch","core/str","./common","editor_tiny/options","editor_tiny/utils","tiny_media/imagedetails","./helpers","./imagehelpers"],(function(_exports,_selectors,_dropzone,_uploader,_prefetch,_str,_common,_options,_utils,_imagedetails,_helpers,_imagehelpers){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.ImageInsert=void 0,_selectors=_interopRequireDefault(_selectors),_dropzone=_interopRequireDefault(_dropzone),_uploader=_interopRequireDefault(_uploader),(0,_prefetch.prefetchStrings)("tiny_media",["insertimage","enterurl","enterurlor","imageurlrequired","uploading","loading","addfilesdrop","sizecustom_help"]);_exports.ImageInsert=class{constructor(_root,editor,currentModal,canShowFilePicker,canShowDropZone){_defineProperty(this,"init",(async function(){const langStringKeys=["insertimage","enterurl","enterurlor","imageurlrequired","uploading","loading","addfilesdrop","sizecustom_help"],langStringvalues=await(0,_str.getStrings)([...langStringKeys].map((key=>({key:key,component:_common.component}))));if(this.langStrings=Object.fromEntries(langStringKeys.map(((key,index)=>[key,langStringvalues[index]]))),this.currentModal.setTitle(this.langStrings.insertimage),this.canShowDropZone){const dropZoneEle=document.querySelector(_selectors.default.IMAGE.elements.dropzoneContainer);let acceptedTypes=(0,_options.getFilePicker)(this.editor,"image").accepted_types;Array.isArray(acceptedTypes)&&(acceptedTypes=acceptedTypes.join(","));const dropZone=new _dropzone.default(dropZoneEle,acceptedTypes,(files=>{this.handleUploadedFile(files)}));dropZone.setLabel(this.langStrings.addfilesdrop),dropZone.init()}await this.registerEventListeners()})),_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,_helpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),this.currentUrl="",this.stopImageLoading()})),image.addEventListener("load",(()=>{let templateContext={};templateContext.sizecustomhelpicon={text:this.langStrings.sizecustom_help},templateContext.bodyTemplate=_selectors.default.IMAGE.template.body.insertImageDetailsBody,templateContext.footerTemplate=_selectors.default.IMAGE.template.footer.insertImageDetailsFooter,templateContext.selector=_selectors.default.IMAGE.type,templateContext.maxlengthalt=_imagehelpers.MAX_LENGTH_ALT,Promise.all([(0,_helpers.body)(templateContext,this.root),(0,_helpers.footer)(templateContext,this.root)]).then((()=>{new _imagedetails.ImageDetails(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone,this.currentUrl,image).init()})).then((()=>{this.stopImageLoading()})).catch((error=>{window.console.log(error)}))}))})),_defineProperty(this,"updateLoaderIcon",(function(root,langStrings){let progress=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const loaderIcon=root.querySelector(_selectors.default.IMAGE.elements.loaderIconContainer+" div");loaderIcon.innerHTML=null!==progress?"".concat(langStrings.uploading," ").concat(Math.round(progress),"%"):langStrings.loading})),_defineProperty(this,"handleUploadedFile",(async files=>{try{this.startImageLoading();const fileURL=await(0,_uploader.default)(this.editor,"image",files[0],files[0].name,(progress=>{this.updateLoaderIcon(this.root,this.langStrings,progress)}));this.updateLoaderIcon(this.root,this.langStrings),this.filePickerCallback({url:fileURL})}catch(error){this.root.querySelector(_selectors.default.IMAGE.elements.urlWarning).innerHTML=void 0!==error.error?error.error:error,(0,_helpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),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&&(0,_helpers.isValidUrl)(url))}urlChanged(){(0,_helpers.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,_helpers.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,_helpers.hideElements)(elementsToHide,this.root)}stopImageLoading(){(0,_helpers.hideElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root);const elementsToShow=[_selectors.default.IMAGE.elements.insertImage,_selectors.default.IMAGE.elements.modalFooter];(0,_helpers.showElements)(elementsToShow,this.root)}filePickerCallback(params){params.url&&this.loadPreviewImage(params.url)}registerEventListeners(){this.root.addEventListener("click",(async e=>{e.target.closest(_selectors.default.IMAGE.actions.addUrl)&&this.urlChanged();if(e.target.closest(_selectors.default.IMAGE.actions.imageBrowser)&&this.canShowFilePicker){e.preventDefault();const params=await(0,_utils.displayFilepicker)(this.editor,"image");this.filePickerCallback(params)}})),this.root.addEventListener("input",(e=>{e.target.closest(_selectors.default.IMAGE.elements.url)&&this.toggleUrlButton()}));const fileInput=this.root.querySelector(_selectors.default.IMAGE.elements.fileInput);fileInput&&fileInput.addEventListener("change",(()=>{this.handleUploadedFile(fileInput.files)}))}}})); //# 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 60174049073..e1bee8ecedb 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 Selectors from './selectors';\nimport Dropzone from 'core/dropzone';\nimport uploadFile from 'editor_tiny/uploader';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getStrings} from 'core/str';\nimport {component} from \"./common\";\nimport {getFilePicker} from 'editor_tiny/options';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport {ImageDetails} from 'tiny_media/imagedetails';\nimport {\n showElements,\n hideElements,\n bodyImageDetails,\n footerImageDetails,\n MAX_LENGTH_ALT,\n} from 'tiny_media/imagehelpers';\n\nprefetchStrings('tiny_media', [\n 'insertimage',\n 'enterurl',\n 'enterurlor',\n 'imageurlrequired',\n 'uploading',\n 'loading',\n 'addfilesdrop',\n 'sizecustom_help',\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 'addfilesdrop',\n 'sizecustom_help',\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 if (this.canShowDropZone) {\n const dropZoneEle = document.querySelector(Selectors.IMAGE.elements.dropzoneContainer);\n\n // Accepted types can be either a string or an array.\n let acceptedTypes = getFilePicker(this.editor, 'image').accepted_types;\n if (Array.isArray(acceptedTypes)) {\n acceptedTypes = acceptedTypes.join(',');\n }\n\n const dropZone = new Dropzone(\n dropZoneEle,\n acceptedTypes,\n files => {\n this.handleUploadedFile(files);\n }\n );\n dropZone.setLabel(this.langStrings.addfilesdrop);\n dropZone.init();\n }\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 let templateContext = {};\n templateContext.sizecustomhelpicon = {text: this.langStrings.sizecustom_help};\n templateContext.maxlengthalt = MAX_LENGTH_ALT;\n Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)])\n .then(() => {\n const imagedetails = new ImageDetails(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n this.currentUrl,\n image,\n );\n imagedetails.init();\n return;\n }).then(() => {\n this.stopImageLoading();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\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 filePickerCallback(params) {\n if (params.url) {\n this.loadPreviewImage(params.url);\n }\n }\n\n /**\n * Updates the content of the loader icon.\n *\n * @param {HTMLElement} root - The root element containing the loader icon.\n * @param {object} langStrings - An object containing language strings.\n * @param {number|null} progress - The progress percentage (optional).\n * @returns {void}\n */\n updateLoaderIcon = (root, langStrings, progress = null) => {\n const loaderIcon = root.querySelector(Selectors.IMAGE.elements.loaderIconContainer + ' div');\n loaderIcon.innerHTML = progress !== null ? `${langStrings.uploading} ${Math.round(progress)}%` : langStrings.loading;\n };\n\n /**\n * Handles the uploaded file, initiates the upload process, and updates the UI during the upload.\n *\n * @param {FileList} files - The list of files to upload (usually from a file input field).\n * @returns {Promise} A promise that resolves when the file is uploaded and processed.\n */\n handleUploadedFile = async(files) => {\n try {\n this.startImageLoading();\n const fileURL = await uploadFile(this.editor, 'image', files[0], files[0].name, (progress) => {\n this.updateLoaderIcon(this.root, this.langStrings, progress);\n });\n // Set the loader icon content to \"loading\" after the file upload completes.\n this.updateLoaderIcon(this.root, this.langStrings);\n this.filePickerCallback({url: fileURL});\n } catch (error) {\n // Handle the error.\n const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);\n urlWarningLabelEle.innerHTML = error.error !== undefined ? error.error : error;\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n this.stopImageLoading();\n }\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 const imageBrowserAction = e.target.closest(Selectors.IMAGE.actions.imageBrowser);\n if (imageBrowserAction && this.canShowFilePicker) {\n e.preventDefault();\n const params = await displayFilepicker(this.editor, 'image');\n this.filePickerCallback(params);\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 const fileInput = this.root.querySelector(Selectors.IMAGE.elements.fileInput);\n if (fileInput) {\n fileInput.addEventListener('change', () => {\n this.handleUploadedFile(fileInput.files);\n });\n }\n }\n}"],"names":["constructor","root","editor","currentModal","canShowFilePicker","canShowDropZone","async","langStringKeys","langStringvalues","map","key","component","langStrings","Object","fromEntries","index","setTitle","this","insertimage","dropZoneEle","document","querySelector","Selectors","IMAGE","elements","dropzoneContainer","acceptedTypes","accepted_types","Array","isArray","join","dropZone","Dropzone","files","handleUploadedFile","setLabel","addfilesdrop","init","registerEventListeners","urlString","RegExp","test","url","startImageLoading","currentUrl","image","Image","src","addEventListener","urlWarning","innerHTML","imageurlrequired","stopImageLoading","templateContext","sizecustomhelpicon","text","sizecustom_help","maxlengthalt","MAX_LENGTH_ALT","Promise","all","then","ImageDetails","catch","error","window","console","log","progress","loaderIcon","loaderIconContainer","uploading","Math","round","loading","fileURL","name","updateLoaderIcon","filePickerCallback","undefined","toggleUrlButton","value","actions","addUrl","disabled","isValidUrl","urlChanged","input","loadPreviewImage","elementsToHide","insertImage","modalFooter","elementsToShow","params","e","target","closest","imageBrowser","preventDefault","fileInput"],"mappings":"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"} \ 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 Dropzone from 'core/dropzone';\nimport uploadFile from 'editor_tiny/uploader';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getStrings} from 'core/str';\nimport {component} from \"./common\";\nimport {getFilePicker} from 'editor_tiny/options';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport {ImageDetails} from 'tiny_media/imagedetails';\nimport {\n body,\n footer,\n hideElements,\n showElements,\n isValidUrl,\n} from './helpers';\nimport {MAX_LENGTH_ALT} from './imagehelpers';\n\nprefetchStrings('tiny_media', [\n 'insertimage',\n 'enterurl',\n 'enterurlor',\n 'imageurlrequired',\n 'uploading',\n 'loading',\n 'addfilesdrop',\n 'sizecustom_help',\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 'addfilesdrop',\n 'sizecustom_help',\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 if (this.canShowDropZone) {\n const dropZoneEle = document.querySelector(Selectors.IMAGE.elements.dropzoneContainer);\n\n // Accepted types can be either a string or an array.\n let acceptedTypes = getFilePicker(this.editor, 'image').accepted_types;\n if (Array.isArray(acceptedTypes)) {\n acceptedTypes = acceptedTypes.join(',');\n }\n\n const dropZone = new Dropzone(\n dropZoneEle,\n acceptedTypes,\n files => {\n this.handleUploadedFile(files);\n }\n );\n dropZone.setLabel(this.langStrings.addfilesdrop);\n dropZone.init();\n }\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 !== \"\" && isValidUrl(url));\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 let templateContext = {};\n templateContext.sizecustomhelpicon = {text: this.langStrings.sizecustom_help};\n templateContext.bodyTemplate = Selectors.IMAGE.template.body.insertImageDetailsBody;\n templateContext.footerTemplate = Selectors.IMAGE.template.footer.insertImageDetailsFooter;\n templateContext.selector = Selectors.IMAGE.type;\n templateContext.maxlengthalt = MAX_LENGTH_ALT;\n\n Promise.all([body(templateContext, this.root), footer(templateContext, this.root)])\n .then(() => {\n const imagedetails = new ImageDetails(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n this.currentUrl,\n image,\n );\n imagedetails.init();\n return;\n }).then(() => {\n this.stopImageLoading();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\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 filePickerCallback(params) {\n if (params.url) {\n this.loadPreviewImage(params.url);\n }\n }\n\n /**\n * Updates the content of the loader icon.\n *\n * @param {HTMLElement} root - The root element containing the loader icon.\n * @param {object} langStrings - An object containing language strings.\n * @param {number|null} progress - The progress percentage (optional).\n * @returns {void}\n */\n updateLoaderIcon = (root, langStrings, progress = null) => {\n const loaderIcon = root.querySelector(Selectors.IMAGE.elements.loaderIconContainer + ' div');\n loaderIcon.innerHTML = progress !== null ? `${langStrings.uploading} ${Math.round(progress)}%` : langStrings.loading;\n };\n\n /**\n * Handles the uploaded file, initiates the upload process, and updates the UI during the upload.\n *\n * @param {FileList} files - The list of files to upload (usually from a file input field).\n * @returns {Promise} A promise that resolves when the file is uploaded and processed.\n */\n handleUploadedFile = async(files) => {\n try {\n this.startImageLoading();\n const fileURL = await uploadFile(this.editor, 'image', files[0], files[0].name, (progress) => {\n this.updateLoaderIcon(this.root, this.langStrings, progress);\n });\n // Set the loader icon content to \"loading\" after the file upload completes.\n this.updateLoaderIcon(this.root, this.langStrings);\n this.filePickerCallback({url: fileURL});\n } catch (error) {\n // Handle the error.\n const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);\n urlWarningLabelEle.innerHTML = error.error !== undefined ? error.error : error;\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n this.stopImageLoading();\n }\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 const imageBrowserAction = e.target.closest(Selectors.IMAGE.actions.imageBrowser);\n if (imageBrowserAction && this.canShowFilePicker) {\n e.preventDefault();\n const params = await displayFilepicker(this.editor, 'image');\n this.filePickerCallback(params);\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 const fileInput = this.root.querySelector(Selectors.IMAGE.elements.fileInput);\n if (fileInput) {\n fileInput.addEventListener('change', () => {\n this.handleUploadedFile(fileInput.files);\n });\n }\n }\n}\n"],"names":["constructor","root","editor","currentModal","canShowFilePicker","canShowDropZone","async","langStringKeys","langStringvalues","map","key","component","langStrings","Object","fromEntries","index","setTitle","this","insertimage","dropZoneEle","document","querySelector","Selectors","IMAGE","elements","dropzoneContainer","acceptedTypes","accepted_types","Array","isArray","join","dropZone","Dropzone","files","handleUploadedFile","setLabel","addfilesdrop","init","registerEventListeners","url","startImageLoading","currentUrl","image","Image","src","addEventListener","urlWarning","innerHTML","imageurlrequired","stopImageLoading","templateContext","sizecustomhelpicon","text","sizecustom_help","bodyTemplate","template","body","insertImageDetailsBody","footerTemplate","footer","insertImageDetailsFooter","selector","type","maxlengthalt","MAX_LENGTH_ALT","Promise","all","then","ImageDetails","catch","error","window","console","log","progress","loaderIcon","loaderIconContainer","uploading","Math","round","loading","fileURL","name","updateLoaderIcon","filePickerCallback","undefined","toggleUrlButton","value","actions","addUrl","disabled","urlChanged","input","loadPreviewImage","elementsToHide","insertImage","modalFooter","elementsToShow","params","e","target","closest","imageBrowser","preventDefault","fileInput"],"mappings":"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"} \ 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 4d331acadea..46f4e3d5a95 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",addUrl:".tiny_image_addurl",deleteImage:".tiny_image_deleteicon"},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",loaderIconContainer:".tiny_image_loader_container",insertImage:".tiny_image_insert_image",modalFooter:".modal-footer",dropzoneContainer:".tiny_image_dropzone_container",fileInput:"#tiny_image_fileinput",fileNameLabel:".tiny_image_filename",sizeOriginal:".tiny_image_sizeoriginal",sizeCustom:".tiny_image_sizecustom",properties:".tiny_image_properties"},styles:{responsive:"img-fluid"}},EMBED:{actions:{mediaBrowser:".openmediabrowser",addUrl:".tiny_media_add_url",deleteMedia:".tiny_media_delete_icon",showSubtitleCaption:'[data-action="show-subtitle-caption"]',backToMediaDetails:'[data-action="back-to-media-details"]',uploadCustomThumbnail:".upload-custom-thumbnail",deleteThumbnail:".delete_tiny_thumbnail",setPoster:'[data-action="set-poster"]'},elements:{source:".tiny_media_source",track:".tiny_media_track",posterSource:".tiny_media_poster_source",title:".tiny_media_title_entry",url:".tiny_media_source_url_entry",width:".tiny_media_width_entry",height:".tiny_media_height_entry",trackSource:".tiny_media_track_source",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",bodyTemplate:".tiny_media_body_template",footerTemplate:".tiny_media_footer_template",dropzoneContainer:".tiny_media_dropzone_container",fromUrl:".tiny_media_from_url_entry",urlWarning:".tiny_media_url_warning",loaderIcon:".tiny_media_loader",loaderIconContainer:".tiny_media_loader_container",insertMedia:".tiny_media_insert_media",modalFooter:".modal-footer",fileNameLabel:".tiny_media_filename",preview:".tiny_media_preview",previewBox:".tiny_media_preview_box",previewClass:".tiny_media_preview_tag",videoTag:"#video-tag",audioTag:"#audio-tag",sizeOriginal:".tiny_media_sizeoriginal",sizeCustom:".tiny_media_sizecustom",properties:".tiny_media_properties",mediaDetailsBody:"#tiny-media-details-body",mediaDetailsFooter:"#media-details-footer",mediaSubtitleCaptionFooter:"#media-subtitle-caption-footer",mediaSubtitleCaptionBody:"#tiny-media-subtitle-caption-body",mediaMetadataTabPane:".tab-pane-media-metadata"},template:{body:{insertMediaBody:"tiny_media/embed/body/insert_media_body",mediaDetailsBody:"tiny_media/embed/body/media_details_body",mediaThumbnailBody:"tiny_media/embed/body/media_thumbnail_body"},footer:{insertMediaFooter:"tiny_media/embed/footer/insert_media_footer",mediaDetailsFooter:"tiny_media/embed/footer/media_details_footer",mediaThumbnailFooter:"tiny_media/embed/footer/media_thumbnail_footer"}},mediaSites:{youtube:"www.youtube.com"},mediaTypes:["audio","video","link"]},MEDIA_MIME_TYPES:{aac:"audio",avi:"video",midi:"audio",mid:"audio",mp3:"audio",mp4:"video",xmpeg3:"audio",mpeg3:"audio",xmpeg:"video",mpeg:"video",oga:"audio",ogg:"audio",ogv:"video",opus:"audio",ts:"video",wav:"audio",weba:"audio",webm:"video","3gp":"video","3g2":"video"}},_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",deleteImage:".tiny_image_deleteicon"},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",loaderIconContainer:".tiny_image_loader_container",insertImage:".tiny_image_insert_image",modalFooter:".modal-footer",dropzoneContainer:".tiny_image_dropzone_container",fileInput:"#tiny_image_fileinput",fileNameLabel:".tiny_image_filename",sizeOriginal:".tiny_image_sizeoriginal",sizeCustom:".tiny_image_sizecustom",properties:".tiny_image_properties",bodyTemplate:".tiny_image_body_template",footerTemplate:".tiny_image_footer_template"},styles:{responsive:"img-fluid"},template:{body:{insertImageBody:"tiny_media/insert_image_modal_insert",insertImageDetailsBody:"tiny_media/insert_image_modal_details"},footer:{insertImageFooter:"tiny_media/insert_image_modal_insert_footer",insertImageDetailsFooter:"tiny_media/insert_image_modal_details_footer"}}},EMBED:{actions:{mediaBrowser:".openmediabrowser",addUrl:".tiny_media_add_url",deleteMedia:".tiny_media_delete_icon",showSubtitleCaption:'[data-action="show-subtitle-caption"]',backToMediaDetails:'[data-action="back-to-media-details"]',uploadCustomThumbnail:".upload-custom-thumbnail",deleteThumbnail:".delete_tiny_thumbnail",setPoster:'[data-action="set-poster"]'},elements:{source:".tiny_media_source",track:".tiny_media_track",posterSource:".tiny_media_poster_source",title:".tiny_media_title_entry",url:".tiny_media_source_url_entry",width:".tiny_media_width_entry",height:".tiny_media_height_entry",trackSource:".tiny_media_track_source",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",bodyTemplate:".tiny_media_body_template",footerTemplate:".tiny_media_footer_template",dropzoneContainer:".tiny_media_dropzone_container",fromUrl:".tiny_media_from_url_entry",urlWarning:".tiny_media_url_warning",loaderIcon:".tiny_media_loader",loaderIconContainer:".tiny_media_loader_container",insertMedia:".tiny_media_insert_media",modalFooter:".modal-footer",fileNameLabel:".tiny_media_filename",preview:".tiny_media_preview",previewBox:".tiny_media_preview_box",previewClass:".tiny_media_preview_tag",videoTag:"#video-tag",audioTag:"#audio-tag",sizeOriginal:".tiny_media_sizeoriginal",sizeCustom:".tiny_media_sizecustom",properties:".tiny_media_properties",mediaDetailsBody:"#tiny-media-details-body",mediaDetailsFooter:"#media-details-footer",mediaSubtitleCaptionFooter:"#media-subtitle-caption-footer",mediaSubtitleCaptionBody:"#tiny-media-subtitle-caption-body",mediaMetadataTabPane:".tab-pane-media-metadata"},template:{body:{insertMediaBody:"tiny_media/embed/body/insert_media_body",mediaDetailsBody:"tiny_media/embed/body/media_details_body",mediaThumbnailBody:"tiny_media/embed/body/media_thumbnail_body"},footer:{insertMediaFooter:"tiny_media/embed/footer/insert_media_footer",mediaDetailsFooter:"tiny_media/embed/footer/media_details_footer",mediaThumbnailFooter:"tiny_media/embed/footer/media_thumbnail_footer"}},mediaSites:{youtube:"www.youtube.com"},mediaTypes:["audio","video","link"]},MEDIA_MIME_TYPES:{aac:"audio",avi:"video",midi:"audio",mid:"audio",mp3:"audio",mp4:"video",xmpeg3:"audio",mpeg3:"audio",xmpeg:"video",mpeg:"video",oga:"audio",ogg:"audio",ogv:"video",opus:"audio",ts:"video",wav:"audio",weba:"audio",webm:"video","3gp":"video","3g2":"video"}},_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 3885d00112c..ffbf2161124 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 addUrl: '.tiny_image_addurl',\n deleteImage: '.tiny_image_deleteicon',\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 loaderIconContainer: '.tiny_image_loader_container',\n insertImage: '.tiny_image_insert_image',\n modalFooter: '.modal-footer',\n dropzoneContainer: '.tiny_image_dropzone_container',\n fileInput: '#tiny_image_fileinput',\n fileNameLabel: '.tiny_image_filename',\n sizeOriginal: '.tiny_image_sizeoriginal',\n sizeCustom: '.tiny_image_sizecustom',\n properties: '.tiny_image_properties',\n },\n styles: {\n responsive: 'img-fluid',\n },\n },\n EMBED: {\n actions: {\n mediaBrowser: '.openmediabrowser',\n addUrl: '.tiny_media_add_url',\n deleteMedia: '.tiny_media_delete_icon',\n showSubtitleCaption: '[data-action=\"show-subtitle-caption\"]',\n backToMediaDetails: '[data-action=\"back-to-media-details\"]',\n uploadCustomThumbnail: '.upload-custom-thumbnail',\n deleteThumbnail: '.delete_tiny_thumbnail',\n setPoster: '[data-action=\"set-poster\"]',\n },\n elements: {\n source: '.tiny_media_source',\n track: '.tiny_media_track',\n posterSource: '.tiny_media_poster_source',\n title: '.tiny_media_title_entry',\n url: '.tiny_media_source_url_entry',\n width: '.tiny_media_width_entry',\n height: '.tiny_media_height_entry',\n trackSource: '.tiny_media_track_source',\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 bodyTemplate: '.tiny_media_body_template',\n footerTemplate: '.tiny_media_footer_template',\n dropzoneContainer: '.tiny_media_dropzone_container',\n fromUrl: '.tiny_media_from_url_entry',\n urlWarning: '.tiny_media_url_warning',\n loaderIcon: '.tiny_media_loader',\n loaderIconContainer: '.tiny_media_loader_container',\n insertMedia: '.tiny_media_insert_media',\n modalFooter: '.modal-footer',\n fileNameLabel: '.tiny_media_filename',\n preview: '.tiny_media_preview',\n previewBox: '.tiny_media_preview_box',\n previewClass: '.tiny_media_preview_tag',\n videoTag: '#video-tag',\n audioTag: '#audio-tag',\n sizeOriginal: '.tiny_media_sizeoriginal',\n sizeCustom: '.tiny_media_sizecustom',\n properties: '.tiny_media_properties',\n mediaDetailsBody: '#tiny-media-details-body',\n mediaDetailsFooter: '#media-details-footer',\n mediaSubtitleCaptionFooter: '#media-subtitle-caption-footer',\n mediaSubtitleCaptionBody: '#tiny-media-subtitle-caption-body',\n mediaMetadataTabPane: '.tab-pane-media-metadata',\n },\n template: {\n body: {\n insertMediaBody: 'tiny_media/embed/body/insert_media_body',\n mediaDetailsBody: 'tiny_media/embed/body/media_details_body',\n mediaThumbnailBody: 'tiny_media/embed/body/media_thumbnail_body',\n },\n footer: {\n insertMediaFooter: 'tiny_media/embed/footer/insert_media_footer',\n mediaDetailsFooter: 'tiny_media/embed/footer/media_details_footer',\n mediaThumbnailFooter: 'tiny_media/embed/footer/media_thumbnail_footer',\n },\n },\n mediaSites: {\n youtube: 'www.youtube.com',\n },\n mediaTypes: ['audio', 'video', 'link'],\n },\n MEDIA_MIME_TYPES: {\n \"aac\": \"audio\", // Mime subtype = /aac.\n \"avi\": \"video\", // Mime subtype = /x-msvideo.\n \"midi\": \"audio\", // Mime subtype = /midi|x-midi.\n \"mid\": \"audio\", // Mime subtype = /midi|x-midi.\n \"mp3\": \"audio\", // Mime subtype = /mpeg.\n \"mp4\": \"video\", // Mime subtype = /mp4.\n \"xmpeg3\": \"audio\", // Mime subtype = /x-mpeg-3.\n \"mpeg3\": \"audio\", // Mime subtype = /mpeg3.\n \"xmpeg\": \"video\", // Mime subtype = /x-mpeg.\n \"mpeg\": \"video\", // Mime subtype = /mpeg.\n \"oga\": \"audio\", // Mime subtype = /ogg.\n \"ogg\": \"audio\", // Mime subtype = /ogg.\n \"ogv\": \"video\", // Mime subtype = /ogg.\n \"opus\": \"audio\", // Mime subtype = /opus.\n \"ts\": \"video\", // Mime subtype = /mp2t.\n \"wav\": \"audio\", // Mime subtype = /wav.\n \"weba\": \"audio\", // Mime subtype = /webm.\n \"webm\": \"video\", // Mime subtype = /webm.\n \"3gp\": \"video\", // Mime subtype = /3gpp.\n \"3g2\": \"video\", // Mime subtype = /3gpp2.\n },\n};\n"],"names":["IMAGE","actions","submit","imageBrowser","addUrl","deleteImage","elements","form","alignSettings","alt","altWarning","height","width","url","urlWarning","size","presentation","constrain","customStyle","preview","previewBox","loaderIcon","loaderIconContainer","insertImage","modalFooter","dropzoneContainer","fileInput","fileNameLabel","sizeOriginal","sizeCustom","properties","styles","responsive","EMBED","mediaBrowser","deleteMedia","showSubtitleCaption","backToMediaDetails","uploadCustomThumbnail","deleteThumbnail","setPoster","source","track","posterSource","title","trackSource","trackLabel","trackLang","trackDefault","mediaControl","mediaAutoplay","mediaMute","mediaLoop","bodyTemplate","footerTemplate","fromUrl","insertMedia","previewClass","videoTag","audioTag","mediaDetailsBody","mediaDetailsFooter","mediaSubtitleCaptionFooter","mediaSubtitleCaptionBody","mediaMetadataTabPane","template","body","insertMediaBody","mediaThumbnailBody","footer","insertMediaFooter","mediaThumbnailFooter","mediaSites","youtube","mediaTypes","MEDIA_MIME_TYPES"],"mappings":"sKAuBe,CACXA,MAAO,CACHC,QAAS,CACLC,OAAQ,6BACRC,aAAc,oBACdC,OAAQ,qBACRC,YAAa,0BAEjBC,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,oBAAqB,+BACrBC,YAAa,2BACbC,YAAa,gBACbC,kBAAmB,iCACnBC,UAAW,wBACXC,cAAe,uBACfC,aAAc,2BACdC,WAAY,yBACZC,WAAY,0BAEhBC,OAAQ,CACJC,WAAY,cAGpBC,MAAO,CACHhC,QAAS,CACLiC,aAAc,oBACd9B,OAAQ,sBACR+B,YAAa,0BACbC,oBAAqB,wCACrBC,mBAAoB,wCACpBC,sBAAuB,2BACvBC,gBAAiB,yBACjBC,UAAW,8BAEflC,SAAU,CACNmC,OAAQ,qBACRC,MAAO,oBACPC,aAAc,4BACdC,MAAO,0BACP/B,IAAK,+BACLD,MAAO,0BACPD,OAAQ,2BACRkC,YAAa,2BACbC,WAAY,gCACZC,UAAW,+BACXC,aAAc,4BACdC,aAAc,uBACdC,cAAe,uBACfC,UAAW,mBACXC,UAAW,mBACXC,aAAc,4BACdC,eAAgB,8BAChB7B,kBAAmB,iCACnB8B,QAAS,6BACTzC,WAAY,0BACZO,WAAY,qBACZC,oBAAqB,+BACrBkC,YAAa,2BACbhC,YAAa,gBACbG,cAAe,uBACfR,QAAS,sBACTC,WAAY,0BACZqC,aAAc,0BACdC,SAAU,aACVC,SAAU,aACV/B,aAAc,2BACdC,WAAY,yBACZC,WAAY,yBACZ8B,iBAAkB,2BAClBC,mBAAoB,wBACpBC,2BAA4B,iCAC5BC,yBAA0B,oCAC1BC,qBAAsB,4BAE1BC,SAAU,CACNC,KAAM,CACFC,gBAAiB,0CACjBP,iBAAkB,2CAClBQ,mBAAoB,8CAExBC,OAAQ,CACJC,kBAAmB,8CACnBT,mBAAoB,+CACpBU,qBAAsB,mDAG9BC,WAAY,CACRC,QAAS,mBAEbC,WAAY,CAAC,QAAS,QAAS,SAEnCC,iBAAkB,KACP,YACA,aACC,YACD,YACA,YACA,eACG,cACD,cACA,aACD,YACD,YACA,YACA,aACC,WACF,YACC,aACC,aACA,cACD,cACA"} \ 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 deleteImage: '.tiny_image_deleteicon',\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 loaderIconContainer: '.tiny_image_loader_container',\n insertImage: '.tiny_image_insert_image',\n modalFooter: '.modal-footer',\n dropzoneContainer: '.tiny_image_dropzone_container',\n fileInput: '#tiny_image_fileinput',\n fileNameLabel: '.tiny_image_filename',\n sizeOriginal: '.tiny_image_sizeoriginal',\n sizeCustom: '.tiny_image_sizecustom',\n properties: '.tiny_image_properties',\n bodyTemplate: '.tiny_image_body_template',\n footerTemplate: '.tiny_image_footer_template',\n },\n styles: {\n responsive: 'img-fluid',\n },\n template: {\n body: {\n insertImageBody: 'tiny_media/insert_image_modal_insert',\n insertImageDetailsBody: 'tiny_media/insert_image_modal_details',\n },\n footer: {\n insertImageFooter: 'tiny_media/insert_image_modal_insert_footer',\n insertImageDetailsFooter: 'tiny_media/insert_image_modal_details_footer',\n },\n },\n },\n EMBED: {\n actions: {\n mediaBrowser: '.openmediabrowser',\n addUrl: '.tiny_media_add_url',\n deleteMedia: '.tiny_media_delete_icon',\n showSubtitleCaption: '[data-action=\"show-subtitle-caption\"]',\n backToMediaDetails: '[data-action=\"back-to-media-details\"]',\n uploadCustomThumbnail: '.upload-custom-thumbnail',\n deleteThumbnail: '.delete_tiny_thumbnail',\n setPoster: '[data-action=\"set-poster\"]',\n },\n elements: {\n source: '.tiny_media_source',\n track: '.tiny_media_track',\n posterSource: '.tiny_media_poster_source',\n title: '.tiny_media_title_entry',\n url: '.tiny_media_source_url_entry',\n width: '.tiny_media_width_entry',\n height: '.tiny_media_height_entry',\n trackSource: '.tiny_media_track_source',\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 bodyTemplate: '.tiny_media_body_template',\n footerTemplate: '.tiny_media_footer_template',\n dropzoneContainer: '.tiny_media_dropzone_container',\n fromUrl: '.tiny_media_from_url_entry',\n urlWarning: '.tiny_media_url_warning',\n loaderIcon: '.tiny_media_loader',\n loaderIconContainer: '.tiny_media_loader_container',\n insertMedia: '.tiny_media_insert_media',\n modalFooter: '.modal-footer',\n fileNameLabel: '.tiny_media_filename',\n preview: '.tiny_media_preview',\n previewBox: '.tiny_media_preview_box',\n previewClass: '.tiny_media_preview_tag',\n videoTag: '#video-tag',\n audioTag: '#audio-tag',\n sizeOriginal: '.tiny_media_sizeoriginal',\n sizeCustom: '.tiny_media_sizecustom',\n properties: '.tiny_media_properties',\n mediaDetailsBody: '#tiny-media-details-body',\n mediaDetailsFooter: '#media-details-footer',\n mediaSubtitleCaptionFooter: '#media-subtitle-caption-footer',\n mediaSubtitleCaptionBody: '#tiny-media-subtitle-caption-body',\n mediaMetadataTabPane: '.tab-pane-media-metadata',\n },\n template: {\n body: {\n insertMediaBody: 'tiny_media/embed/body/insert_media_body',\n mediaDetailsBody: 'tiny_media/embed/body/media_details_body',\n mediaThumbnailBody: 'tiny_media/embed/body/media_thumbnail_body',\n },\n footer: {\n insertMediaFooter: 'tiny_media/embed/footer/insert_media_footer',\n mediaDetailsFooter: 'tiny_media/embed/footer/media_details_footer',\n mediaThumbnailFooter: 'tiny_media/embed/footer/media_thumbnail_footer',\n },\n },\n mediaSites: {\n youtube: 'www.youtube.com',\n },\n mediaTypes: ['audio', 'video', 'link'],\n },\n MEDIA_MIME_TYPES: {\n \"aac\": \"audio\", // Mime subtype = /aac.\n \"avi\": \"video\", // Mime subtype = /x-msvideo.\n \"midi\": \"audio\", // Mime subtype = /midi|x-midi.\n \"mid\": \"audio\", // Mime subtype = /midi|x-midi.\n \"mp3\": \"audio\", // Mime subtype = /mpeg.\n \"mp4\": \"video\", // Mime subtype = /mp4.\n \"xmpeg3\": \"audio\", // Mime subtype = /x-mpeg-3.\n \"mpeg3\": \"audio\", // Mime subtype = /mpeg3.\n \"xmpeg\": \"video\", // Mime subtype = /x-mpeg.\n \"mpeg\": \"video\", // Mime subtype = /mpeg.\n \"oga\": \"audio\", // Mime subtype = /ogg.\n \"ogg\": \"audio\", // Mime subtype = /ogg.\n \"ogv\": \"video\", // Mime subtype = /ogg.\n \"opus\": \"audio\", // Mime subtype = /opus.\n \"ts\": \"video\", // Mime subtype = /mp2t.\n \"wav\": \"audio\", // Mime subtype = /wav.\n \"weba\": \"audio\", // Mime subtype = /webm.\n \"webm\": \"video\", // Mime subtype = /webm.\n \"3gp\": \"video\", // Mime subtype = /3gpp.\n \"3g2\": \"video\", // Mime subtype = /3gpp2.\n },\n};\n"],"names":["IMAGE","actions","submit","imageBrowser","addUrl","deleteImage","elements","form","alignSettings","alt","altWarning","height","width","url","urlWarning","size","presentation","constrain","customStyle","preview","previewBox","loaderIcon","loaderIconContainer","insertImage","modalFooter","dropzoneContainer","fileInput","fileNameLabel","sizeOriginal","sizeCustom","properties","bodyTemplate","footerTemplate","styles","responsive","template","body","insertImageBody","insertImageDetailsBody","footer","insertImageFooter","insertImageDetailsFooter","EMBED","mediaBrowser","deleteMedia","showSubtitleCaption","backToMediaDetails","uploadCustomThumbnail","deleteThumbnail","setPoster","source","track","posterSource","title","trackSource","trackLabel","trackLang","trackDefault","mediaControl","mediaAutoplay","mediaMute","mediaLoop","fromUrl","insertMedia","previewClass","videoTag","audioTag","mediaDetailsBody","mediaDetailsFooter","mediaSubtitleCaptionFooter","mediaSubtitleCaptionBody","mediaMetadataTabPane","insertMediaBody","mediaThumbnailBody","insertMediaFooter","mediaThumbnailFooter","mediaSites","youtube","mediaTypes","MEDIA_MIME_TYPES"],"mappings":"sKAuBe,CACXA,MAAO,CACHC,QAAS,CACLC,OAAQ,6BACRC,aAAc,oBACdC,OAAQ,qBACRC,YAAa,0BAEjBC,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,oBAAqB,+BACrBC,YAAa,2BACbC,YAAa,gBACbC,kBAAmB,iCACnBC,UAAW,wBACXC,cAAe,uBACfC,aAAc,2BACdC,WAAY,yBACZC,WAAY,yBACZC,aAAc,4BACdC,eAAgB,+BAEpBC,OAAQ,CACJC,WAAY,aAEhBC,SAAU,CACNC,KAAM,CACFC,gBAAiB,uCACjBC,uBAAwB,yCAE5BC,OAAQ,CACJC,kBAAmB,8CACnBC,yBAA0B,kDAItCC,MAAO,CACHzC,QAAS,CACL0C,aAAc,oBACdvC,OAAQ,sBACRwC,YAAa,0BACbC,oBAAqB,wCACrBC,mBAAoB,wCACpBC,sBAAuB,2BACvBC,gBAAiB,yBACjBC,UAAW,8BAEf3C,SAAU,CACN4C,OAAQ,qBACRC,MAAO,oBACPC,aAAc,4BACdC,MAAO,0BACPxC,IAAK,+BACLD,MAAO,0BACPD,OAAQ,2BACR2C,YAAa,2BACbC,WAAY,gCACZC,UAAW,+BACXC,aAAc,4BACdC,aAAc,uBACdC,cAAe,uBACfC,UAAW,mBACXC,UAAW,mBACX9B,aAAc,4BACdC,eAAgB,8BAChBP,kBAAmB,iCACnBqC,QAAS,6BACThD,WAAY,0BACZO,WAAY,qBACZC,oBAAqB,+BACrByC,YAAa,2BACbvC,YAAa,gBACbG,cAAe,uBACfR,QAAS,sBACTC,WAAY,0BACZ4C,aAAc,0BACdC,SAAU,aACVC,SAAU,aACVtC,aAAc,2BACdC,WAAY,yBACZC,WAAY,yBACZqC,iBAAkB,2BAClBC,mBAAoB,wBACpBC,2BAA4B,iCAC5BC,yBAA0B,oCAC1BC,qBAAsB,4BAE1BpC,SAAU,CACNC,KAAM,CACFoC,gBAAiB,0CACjBL,iBAAkB,2CAClBM,mBAAoB,8CAExBlC,OAAQ,CACJmC,kBAAmB,8CACnBN,mBAAoB,+CACpBO,qBAAsB,mDAG9BC,WAAY,CACRC,QAAS,mBAEbC,WAAY,CAAC,QAAS,QAAS,SAEnCC,iBAAkB,KACP,YACA,aACC,YACD,YACA,YACA,eACG,cACD,cACA,aACD,YACD,YACA,YACA,aACC,WACF,YACC,aACC,aACA,cACD,cACA"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/src/image.js b/lib/editor/tiny/plugins/media/amd/src/image.js index ab706b093e3..66fa1295996 100644 --- a/lib/editor/tiny/plugins/media/amd/src/image.js +++ b/lib/editor/tiny/plugins/media/amd/src/image.js @@ -30,15 +30,13 @@ import {ImageDetails} from 'tiny_media/imagedetails'; import {prefetchStrings} from 'core/prefetch'; import {getString} from 'core/str'; import { - bodyImageInsert, - footerImageInsert, - bodyImageDetails, - footerImageDetails, - showElements, + body, + footer, hideElements, + showElements, isPercentageValue, - MAX_LENGTH_ALT, -} from 'tiny_media/imagehelpers'; +} from './helpers'; +import {MAX_LENGTH_ALT} from './imagehelpers'; prefetchStrings('tiny_media', [ 'imageurlrequired', @@ -89,9 +87,12 @@ export default class MediaImage { elementid: this.editor.id, showfilepicker: this.canShowFilePicker, showdropzone: this.canShowDropZone, + bodyTemplate: Selectors.IMAGE.template.body.insertImageBody, + footerTemplate: Selectors.IMAGE.template.footer.insertImageFooter, + selector: Selectors.IMAGE.type, }; - Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)]) + Promise.all([body(templateContext, this.root), footer(templateContext, this.root)]) .then(() => { const imageinsert = new ImageInsert( this.root, @@ -156,8 +157,12 @@ export default class MediaImage { const currentImageData = await this.getCurrentImageData(); let templateContext = await this.getTemplateContext(currentImageData); templateContext.sizecustomhelpicon = {text: await getString('sizecustom_help', 'tiny_media')}; + templateContext.bodyTemplate = Selectors.IMAGE.template.body.insertImageDetailsBody; + templateContext.footerTemplate = Selectors.IMAGE.template.footer.insertImageDetailsFooter; + templateContext.selector = Selectors.IMAGE.type; templateContext.maxlengthalt = MAX_LENGTH_ALT; - Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)]) + + Promise.all([body(templateContext, this.root), footer(templateContext, this.root)]) .then(() => { this.stopImageLoading(); return; diff --git a/lib/editor/tiny/plugins/media/amd/src/imagedetails.js b/lib/editor/tiny/plugins/media/amd/src/imagedetails.js index c20754bc81c..ac31c01f2a9 100644 --- a/lib/editor/tiny/plugins/media/amd/src/imagedetails.js +++ b/lib/editor/tiny/plugins/media/amd/src/imagedetails.js @@ -29,21 +29,24 @@ import Selectors from './selectors'; import Templates from 'core/templates'; import {getString} from 'core/str'; import {ImageInsert} from 'tiny_media/imageinsert'; +import {MediaBase} from './mediabase'; import { - bodyImageInsert, - footerImageInsert, - showElements, + body, + footer, hideElements, + showElements, isPercentageValue, -} from 'tiny_media/imagehelpers'; +} from './helpers'; -export class ImageDetails { +export class ImageDetails extends MediaBase { DEFAULTS = { WIDTH: 160, HEIGHT: 160, }; - rawImageDimensions = null; + selectorType = 'IMAGE'; + + mediaDimensions = null; constructor( root, @@ -54,6 +57,7 @@ export class ImageDetails { currentUrl, image, ) { + super(); this.root = root; this.editor = editor; this.currentModal = currentModal; @@ -80,9 +84,12 @@ export class ImageDetails { elementid: this.editor.id, showfilepicker: this.canShowFilePicker, showdropzone: this.canShowDropZone, + bodyTemplate: Selectors.IMAGE.template.body.insertImageBody, + footerTemplate: Selectors.IMAGE.template.footer.insertImageFooter, + selector: 'IMAGE', }; - Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)]) + Promise.all([body(templateContext, this.root), footer(templateContext, this.root)]) .then(() => { const imageinsert = new ImageInsert( this.root, @@ -101,21 +108,21 @@ export class ImageDetails { storeImageDimensions(image) { // Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG). - this.rawImageDimensions = { + this.mediaDimensions = { width: image.width || this.DEFAULTS.WIDTH, height: image.height || this.DEFAULTS.HEIGHT, }; const getCurrentWidth = (element) => { if (element.value === '') { - element.value = this.rawImageDimensions.width; + element.value = this.mediaDimensions.width; } return element.value; }; const getCurrentHeight = (element) => { if (element.value === '') { - element.value = this.rawImageDimensions.height; + element.value = this.mediaDimensions.height; } return element.value; }; @@ -151,11 +158,11 @@ export class ImageDetails { * @param {number} currentHeight - The current height value. */ const setSelectedSize = (currentWidth, currentHeight) => { - if (this.rawImageDimensions.width === currentWidth && - this.rawImageDimensions.height === currentHeight + if (this.mediaDimensions.width === currentWidth && + this.mediaDimensions.height === currentHeight ) { - this.currentWidth = this.rawImageDimensions.width; - this.currentHeight = this.rawImageDimensions.height; + this.currentWidth = this.mediaDimensions.width; + this.currentHeight = this.mediaDimensions.height; this.sizeChecked('original'); } else { this.currentWidth = currentWidth; @@ -167,115 +174,6 @@ export class ImageDetails { setSelectedSize(Number(currentWidth), Number(currentHeight)); } - /** - * Handles the selection of image size options and updates the form inputs accordingly. - * - * @param {string} option - The selected image size option ("original" or "custom"). - */ - sizeChecked(option) { - const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width); - const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height); - if (option === "original") { - this.sizeOriginalChecked(); - widthInput.value = this.rawImageDimensions.width; - heightInput.value = this.rawImageDimensions.height; - } else if (option === "custom") { - this.sizeCustomChecked(); - widthInput.value = this.currentWidth; - heightInput.value = this.currentHeight; - - // If the current size is equal to the original size, then check the Keep proportion checkbox. - if (this.currentWidth === this.rawImageDimensions.width && this.currentHeight === this.rawImageDimensions.height) { - const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain); - constrainField.checked = true; - } - } - this.autoAdjustSize(); - } - - autoAdjustSize(forceHeight = false) { - // If we do not know the image size, do not do anything. - if (!this.rawImageDimensions) { - return; - } - - const widthField = this.root.querySelector(Selectors.IMAGE.elements.width); - const heightField = this.root.querySelector(Selectors.IMAGE.elements.height); - - const normalizeFieldData = (fieldData) => { - fieldData.isPercentageValue = !!isPercentageValue(fieldData.field.value); - if (fieldData.isPercentageValue) { - fieldData.percentValue = parseInt(fieldData.field.value, 10); - fieldData.pixelSize = this.rawImageDimensions[fieldData.type] / 100 * fieldData.percentValue; - } else { - fieldData.pixelSize = parseInt(fieldData.field.value, 10); - fieldData.percentValue = fieldData.pixelSize / this.rawImageDimensions[fieldData.type] * 100; - } - - return fieldData; - }; - - const getKeyField = () => { - const getValue = () => { - if (forceHeight) { - return { - field: heightField, - type: 'height', - }; - } else { - return { - field: widthField, - type: 'width', - }; - } - }; - - const currentValue = getValue(); - if (currentValue.field.value === '') { - currentValue.field.value = this.rawImageDimensions[currentValue.type]; - } - - return normalizeFieldData(currentValue); - }; - - const getRelativeField = () => { - if (forceHeight) { - return normalizeFieldData({ - field: widthField, - type: 'width', - }); - } else { - return normalizeFieldData({ - field: heightField, - type: 'height', - }); - } - }; - - // Now update with the new values. - const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain); - if (constrainField.checked) { - const keyField = getKeyField(); - const relativeField = getRelativeField(); - // We are keeping the image in proportion. - // Calculate the size for the relative field. - if (keyField.isPercentageValue) { - // In proportion, so the percentages are the same. - relativeField.field.value = keyField.field.value; - relativeField.percentValue = keyField.percentValue; - } else { - relativeField.pixelSize = Math.round( - keyField.pixelSize / this.rawImageDimensions[keyField.type] * this.rawImageDimensions[relativeField.type] - ); - relativeField.field.value = relativeField.pixelSize; - } - } - - // Store the custom width and height to reuse. - this.currentWidth = Number(widthField.value) !== this.rawImageDimensions.width ? widthField.value : this.currentWidth; - this.currentHeight = Number(heightField.value) !== this.rawImageDimensions.height ? heightField.value : this.currentHeight; - } - /** * Sets the dimensions of the image preview element based on user input and constraints. */ @@ -305,24 +203,6 @@ export class ImageDetails { } }; - /** - * Handles the selection of the "Original Size" option and updates the form elements accordingly. - */ - sizeOriginalChecked() { - this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = true; - this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = false; - hideElements(Selectors.IMAGE.elements.properties, this.root); - } - - /** - * Handles the selection of the "Custom Size" option and updates the form elements accordingly. - */ - sizeCustomChecked() { - this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = false; - this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = true; - showElements(Selectors.IMAGE.elements.properties, this.root); - } - /** * Handles changes in the image presentation checkbox and enables/disables the image alt text input accordingly. */ @@ -402,7 +282,7 @@ export class ImageDetails { if (imageAltError) { showElements(Selectors.IMAGE.elements.altWarning, this.root); } else { - hideElements(Selectors.IMAGE.elements.urlWaaltWarningrning, this.root); + hideElements(Selectors.IMAGE.elements.altWarning, this.root); } this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError); diff --git a/lib/editor/tiny/plugins/media/amd/src/imageinsert.js b/lib/editor/tiny/plugins/media/amd/src/imageinsert.js index 272a6944752..a4115c86e1a 100644 --- a/lib/editor/tiny/plugins/media/amd/src/imageinsert.js +++ b/lib/editor/tiny/plugins/media/amd/src/imageinsert.js @@ -31,12 +31,13 @@ import {getFilePicker} from 'editor_tiny/options'; import {displayFilepicker} from 'editor_tiny/utils'; import {ImageDetails} from 'tiny_media/imagedetails'; import { - showElements, + body, + footer, hideElements, - bodyImageDetails, - footerImageDetails, - MAX_LENGTH_ALT, -} from 'tiny_media/imagehelpers'; + showElements, + isValidUrl, +} from './helpers'; +import {MAX_LENGTH_ALT} from './imagehelpers'; prefetchStrings('tiny_media', [ 'insertimage', @@ -111,23 +112,9 @@ export class ImageInsert { 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)); + addUrl.disabled = !(url !== "" && 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. */ @@ -160,8 +147,12 @@ export class ImageInsert { image.addEventListener('load', () => { let templateContext = {}; templateContext.sizecustomhelpicon = {text: this.langStrings.sizecustom_help}; + templateContext.bodyTemplate = Selectors.IMAGE.template.body.insertImageDetailsBody; + templateContext.footerTemplate = Selectors.IMAGE.template.footer.insertImageDetailsFooter; + templateContext.selector = Selectors.IMAGE.type; templateContext.maxlengthalt = MAX_LENGTH_ALT; - Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)]) + + Promise.all([body(templateContext, this.root), footer(templateContext, this.root)]) .then(() => { const imagedetails = new ImageDetails( this.root, @@ -281,4 +272,4 @@ export class ImageInsert { }); } } -} \ 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 ac2a1083bae..eee06ed58a2 100644 --- a/lib/editor/tiny/plugins/media/amd/src/selectors.js +++ b/lib/editor/tiny/plugins/media/amd/src/selectors.js @@ -54,10 +54,22 @@ export default { sizeOriginal: '.tiny_image_sizeoriginal', sizeCustom: '.tiny_image_sizecustom', properties: '.tiny_image_properties', + bodyTemplate: '.tiny_image_body_template', + footerTemplate: '.tiny_image_footer_template', }, styles: { responsive: 'img-fluid', }, + template: { + body: { + insertImageBody: 'tiny_media/insert_image_modal_insert', + insertImageDetailsBody: 'tiny_media/insert_image_modal_details', + }, + footer: { + insertImageFooter: 'tiny_media/insert_image_modal_insert_footer', + insertImageDetailsFooter: 'tiny_media/insert_image_modal_details_footer', + }, + }, }, EMBED: { actions: { diff --git a/lib/editor/tiny/plugins/media/lang/en/deprecated.txt b/lib/editor/tiny/plugins/media/lang/en/deprecated.txt index 39dd631cc23..77c516c6d69 100644 --- a/lib/editor/tiny/plugins/media/lang/en/deprecated.txt +++ b/lib/editor/tiny/plugins/media/lang/en/deprecated.txt @@ -10,6 +10,7 @@ addsource_help,tiny_media advancedsettings,tiny_media audio,tiny_media audiosourcelabel,tiny_media +browserepositoriesimage,tiny_media createmedia,tiny_media displayoptions,tiny_media entername,tiny_media 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 9296b724487..81274914a9d 100644 --- a/lib/editor/tiny/plugins/media/lang/en/tiny_media.php +++ b/lib/editor/tiny/plugins/media/lang/en/tiny_media.php @@ -35,7 +35,6 @@ $string['autoplay'] = 'Autoplay'; $string['back'] = 'Back'; $string['browseembedimagerepositories'] = 'Browse repositories'; $string['browserepositories'] = 'Browse repositories...'; -$string['browserepositoriesimage'] = 'Browse repositories'; $string['captions_help'] = 'Captions may be used to describe everything happening in the track, including non-verbal sounds such as a phone ringing.'; $string['captions'] = 'Captions'; $string['captionssourcelabel'] = 'Caption track URL'; @@ -134,6 +133,7 @@ $string['addsource_help'] = 'You are recommended to provide an alternative media $string['advancedsettings'] = 'Advanced settings'; $string['audio'] = 'Audio'; $string['audiosourcelabel'] = 'Audio source URL'; +$string['browserepositoriesimage'] = 'Browse repositories'; $string['createmedia'] = 'Insert media'; $string['displayoptions'] = 'Display options'; $string['entername'] = 'Name';