Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
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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"}