Proyectos de Subversion Moodle

Rev

Rev 1 | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |

{"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 {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<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}\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":"q1BAyCgB,aAAc,CAC1B,cACA,WACA,aACA,mBACA,YACA,UACA,eACA,+CAKAA,YACIC,MACAC,OACAC,aACAC,kBACAC,8CASGC,uBAEGC,eAAiB,CACnB,cACA,WACA,aACA,mBACA,YACA,UACA,eACA,mBAEEC,uBAAyB,mBAAW,IAAID,gBAAgBE,KAAKC,OAAUA,IAAAA,IAAKC,UAAAA,+BAG7EC,YAAcC,OAAOC,YAAYP,eAAeE,KAAI,CAACC,IAAKK,QAAU,CAACL,IAAKF,iBAAiBO,gBAC3FZ,aAAaa,SAASC,KAAKL,YAAYM,aACxCD,KAAKZ,gBAAiB,OAChBc,YAAcC,SAASC,cAAcC,mBAAUC,MAAMC,SAASC,uBAGhEC,eAAgB,0BAAcT,KAAKf,OAAQ,SAASyB,eACpDC,MAAMC,QAAQH,iBACdA,cAAgBA,cAAcI,KAAK,YAGjCC,SAAW,IAAIC,kBACjBb,YACAO,eACAO,aACSC,mBAAmBD,UAGhCF,SAASI,SAASlB,KAAKL,YAAYwB,cACnCL,SAASM,aAEPpB,KAAKqB,qEA6BI,SAASC,UACnBC,yBACAC,WAAaF,UACZG,MAAQ,IAAIC,MAClBD,MAAME,IAAML,IACZG,MAAMG,iBAAiB,SAAS,KACD5B,KAAKhB,KAAKoB,cAAcC,mBAAUC,MAAMC,SAASsB,YACzDC,UAAY9B,KAAKL,YAAYoC,2CACnC1B,mBAAUC,MAAMC,SAASsB,WAAY7B,KAAKhB,WAClDwC,WAAa,QACbQ,sBAGTP,MAAMG,iBAAiB,QAAQ,SACvBK,gBAAkB,GACtBA,gBAAgBC,mBAAqB,CAACC,KAAMnC,KAAKL,YAAYyC,iBAC7DH,gBAAgBI,aAAehC,mBAAUC,MAAMgC,SAASC,KAAKC,uBAC7DP,gBAAgBQ,eAAiBpC,mBAAUC,MAAMgC,SAASI,OAAOC,yBACjEV,gBAAgBW,SAAWvC,mBAAUC,MAAMuC,KAC3CZ,gBAAgBa,aAAeC,6BAE/BC,QAAQC,IAAI,EAAC,iBAAKhB,gBAAiBjC,KAAKhB,OAAO,mBAAOiD,gBAAiBjC,KAAKhB,QACvEkE,MAAK,KACmB,IAAIC,2BACrBnD,KAAKhB,KACLgB,KAAKf,OACLe,KAAKd,aACLc,KAAKb,kBACLa,KAAKZ,gBACLY,KAAKwB,WACLC,OAESL,UAEd8B,MAAK,UACClB,sBAGRoB,OAAMC,QACHC,OAAOC,QAAQC,IAAIH,yDA4ChB,SAACrE,KAAMW,iBAAa8D,gEAAW,WACxCC,WAAa1E,KAAKoB,cAAcC,mBAAUC,MAAMC,SAASoD,oBAAsB,QACrFD,WAAW5B,UAAyB,OAAb2B,mBAAuB9D,YAAYiE,sBAAaC,KAAKC,MAAML,eAAe9D,YAAYoE,sDAS5F1E,MAAAA,iBAERkC,0BACCyC,cAAgB,qBAAWhE,KAAKf,OAAQ,QAAS+B,MAAM,GAAIA,MAAM,GAAGiD,MAAOR,gBACxES,iBAAiBlE,KAAKhB,KAAMgB,KAAKL,YAAa8D,kBAGlDS,iBAAiBlE,KAAKhB,KAAMgB,KAAKL,kBACjCwE,mBAAmB,CAAC7C,IAAK0C,UAChC,MAAOX,OAEsBrD,KAAKhB,KAAKoB,cAAcC,mBAAUC,MAAMC,SAASsB,YACzDC,eAA4BsC,IAAhBf,MAAMA,MAAsBA,MAAMA,MAAQA,gCAC5DhD,mBAAUC,MAAMC,SAASsB,WAAY7B,KAAKhB,WAClDgD,4BApLJhD,KAAOA,WACPC,OAASA,YACTC,aAAeA,kBACfC,kBAAoBA,uBACpBC,gBAAkBA,gBA6C3BiF,wBAEU/C,IADWtB,KAAKhB,KAAKoB,cAAcC,mBAAUC,MAAMC,SAASe,KAC7CgD,MACNtE,KAAKhB,KAAKoB,cAAcC,mBAAUC,MAAMiE,QAAQC,QACxDC,WAAqB,KAARnD,MAAc,uBAAWA,MAMjDoD,uCACiBrE,mBAAUC,MAAMC,SAASsB,WAAY7B,KAAKhB,YACjD2F,MAAQ3E,KAAKhB,KAAKoB,cAAcC,mBAAUC,MAAMC,SAASe,KAC3DqD,MAAML,OAASK,MAAML,QAAUtE,KAAKwB,iBAC/BoD,iBAAiBD,MAAML,OAwDpC/C,8CACiBlB,mBAAUC,MAAMC,SAASmD,WAAY1D,KAAKhB,YACjD6F,eAAiB,CACnBxE,mBAAUC,MAAMC,SAASuE,YACzBzE,mBAAUC,MAAMC,SAASsB,WACzBxB,mBAAUC,MAAMC,SAASwE,uCAEhBF,eAAgB7E,KAAKhB,MAMtCgD,6CACiB3B,mBAAUC,MAAMC,SAASmD,WAAY1D,KAAKhB,YACjDgG,eAAiB,CACnB3E,mBAAUC,MAAMC,SAASuE,YACzBzE,mBAAUC,MAAMC,SAASwE,uCAEhBC,eAAgBhF,KAAKhB,MAGtCmF,mBAAmBc,QACXA,OAAO3D,UACFsD,iBAAiBK,OAAO3D,KAyCrCD,8BACSrC,KAAK4C,iBAAiB,SAASvC,MAAAA,IACd6F,EAAEC,OAAOC,QAAQ/E,mBAAUC,MAAMiE,QAAQC,cAElDE,gBAGkBQ,EAAEC,OAAOC,QAAQ/E,mBAAUC,MAAMiE,QAAQc,eAC1CrF,KAAKb,kBAAmB,CAC9C+F,EAAEI,uBACIL,aAAe,4BAAkBjF,KAAKf,OAAQ,cAC/CkF,mBAAmBc,iBAI3BjG,KAAK4C,iBAAiB,SAAUsD,IAClBA,EAAEC,OAAOC,QAAQ/E,mBAAUC,MAAMC,SAASe,WAEhD+C,2BAIPkB,UAAYvF,KAAKhB,KAAKoB,cAAcC,mBAAUC,MAAMC,SAASgF,WAC/DA,WACAA,UAAU3D,iBAAiB,UAAU,UAC5BX,mBAAmBsE,UAAUvE"}