1 |
efrain |
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 <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny media plugin image insertion class for Moodle.\n *\n * @module tiny_media/imageinsert\n * @copyright 2024 Meirza <meirza.arson@moodle.com>\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 {displayFilepicker} from 'editor_tiny/utils';\nimport {ImageDetails} from 'tiny_media/imagedetails';\nimport {\n showElements,\n hideElements,\n bodyImageDetails,\n footerImageDetails,\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 const dropZone = new Dropzone(\n dropZoneEle,\n 'image/*',\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 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<void>} 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","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","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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"}
|