Proyectos de Subversion Moodle

Rev

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

Rev Autor Línea Nro. Línea
1 efrain 1
// This file is part of Moodle - http://moodle.org/
2
//
3
// Moodle is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// Moodle is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
 
16
/**
17
 * Tiny Media plugin Image class for Moodle.
18
 *
19
 * @module      tiny_media/image
20
 * @copyright   2022 Huong Nguyen <huongnv13@gmail.com>
21
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
 
24
import Selectors from './selectors';
25
import ImageModal from './imagemodal';
26
import {getImagePermissions} from './options';
27
import {getFilePicker} from 'editor_tiny/options';
28
import {ImageInsert} from 'tiny_media/imageinsert';
29
import {ImageDetails} from 'tiny_media/imagedetails';
30
import {getString} from 'core/str';
31
import {
32
    bodyImageInsert,
33
    footerImageInsert,
34
    bodyImageDetails,
35
    footerImageDetails,
36
    showElements,
37
    hideElements,
38
    isPercentageValue,
39
} from 'tiny_media/imagehelpers';
40
 
41
export default class MediaImage {
42
    canShowFilePicker = false;
43
    editor = null;
44
    currentModal = null;
45
    /**
46
     * @type {HTMLElement|null} The root element.
47
     */
48
    root = null;
49
 
50
    constructor(editor) {
51
        const permissions = getImagePermissions(editor);
52
        const options = getFilePicker(editor, 'image');
53
        // Indicates whether the file picker can be shown.
54
        this.canShowFilePicker = permissions.filepicker
55
            && (typeof options !== 'undefined')
56
            && Object.keys(options.repositories).length > 0;
57
        // Indicates whether the drop zone area can be shown.
11 efrain 58
        this.canShowDropZone = (typeof options !== 'undefined') &&
59
            Object.values(options.repositories).some(repository => repository.type === 'upload');
1 efrain 60
 
61
        this.editor = editor;
62
    }
63
 
64
    async displayDialogue() {
65
        const currentImageData = await this.getCurrentImageData();
66
        this.currentModal = await ImageModal.create();
67
        this.root = this.currentModal.getRoot()[0];
68
        if (currentImageData && currentImageData.src) {
69
            this.loadPreviewImage(currentImageData.src);
70
        } else {
71
            this.loadInsertImage();
72
        }
73
    }
74
 
75
    /**
76
     * Displays an insert image view asynchronously.
77
     *
78
     * @returns {Promise<void>}
79
     */
80
    loadInsertImage = async function() {
81
        const templateContext = {
82
            elementid: this.editor.id,
83
            showfilepicker: this.canShowFilePicker,
84
            showdropzone: this.canShowDropZone,
85
        };
86
 
87
        Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])
88
            .then(() => {
89
                const imageinsert = new ImageInsert(
90
                    this.root,
91
                    this.editor,
92
                    this.currentModal,
93
                    this.canShowFilePicker,
94
                    this.canShowDropZone,
95
                );
96
                imageinsert.init();
97
                return;
98
            })
99
            .catch(error => {
100
                window.console.log(error);
101
            });
102
    };
103
 
104
    async getTemplateContext(data) {
105
        return {
106
            elementid: this.editor.id,
107
            showfilepicker: this.canShowFilePicker,
108
            ...data,
109
        };
110
    }
111
 
112
    async getCurrentImageData() {
113
        const selectedImageProperties = this.getSelectedImageProperties();
114
        if (!selectedImageProperties) {
115
            return {};
116
        }
117
 
118
        const properties = {...selectedImageProperties};
119
 
120
        if (properties.src) {
121
            properties.haspreview = true;
122
        }
123
 
124
        if (!properties.alt) {
125
            properties.presentation = true;
126
        }
127
 
128
        return properties;
129
    }
130
 
131
    /**
132
     * Asynchronously loads and previews an image from the provided URL.
133
     *
134
     * @param {string} url - The URL of the image to load and preview.
135
     * @returns {Promise<void>}
136
     */
137
    loadPreviewImage = async function(url) {
138
        this.startImageLoading();
139
        const image = new Image();
140
        image.src = url;
141
        image.addEventListener('error', () => {
142
            const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);
143
            urlWarningLabelEle.innerHTML = this.langStrings.imageurlrequired;
144
            showElements(Selectors.IMAGE.elements.urlWarning, this.root);
145
            this.stopImageLoading();
146
        });
147
 
148
        image.addEventListener('load', async() => {
149
            const currentImageData = await this.getCurrentImageData();
150
            let templateContext = await this.getTemplateContext(currentImageData);
151
            templateContext.sizecustomhelpicon = {text: await getString('sizecustom_help', 'tiny_media')};
152
 
153
            Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)])
154
                .then(() => {
155
                    this.stopImageLoading();
156
                    return;
157
                })
158
                .then(() => {
159
                    const imagedetails = new ImageDetails(
160
                        this.root,
161
                        this.editor,
162
                        this.currentModal,
163
                        this.canShowFilePicker,
164
                        this.canShowDropZone,
165
                        url,
166
                        image,
167
                    );
168
                    imagedetails.init();
169
                    return;
170
                })
171
                .catch(error => {
172
                    window.console.log(error);
173
                });
174
        });
175
    };
176
 
177
    getSelectedImageProperties() {
178
        const image = this.getSelectedImage();
179
        if (!image) {
180
            this.selectedImage = null;
181
            return null;
182
        }
183
 
184
        const properties = {
185
            src: null,
186
            alt: null,
187
            width: null,
188
            height: null,
189
            presentation: false,
190
            customStyle: '', // Custom CSS styles applied to the image.
191
        };
192
 
193
        const getImageHeight = (image) => {
194
            if (!isPercentageValue(String(image.height))) {
195
                return parseInt(image.height, 10);
196
            }
197
 
198
            return image.height;
199
        };
200
 
201
        const getImageWidth = (image) => {
202
            if (!isPercentageValue(String(image.width))) {
203
                return parseInt(image.width, 10);
204
            }
205
 
206
            return image.width;
207
        };
208
 
209
        // Get the current selection.
210
        this.selectedImage = image;
211
 
212
        properties.customStyle = image.style.cssText;
213
 
214
        const width = getImageWidth(image);
215
        if (width !== 0) {
216
            properties.width = width;
217
        }
218
 
219
        const height = getImageHeight(image);
220
        if (height !== 0) {
221
            properties.height = height;
222
        }
223
 
224
        properties.src = image.getAttribute('src');
225
        properties.alt = image.getAttribute('alt') || '';
226
        properties.presentation = (image.getAttribute('role') === 'presentation');
227
 
228
        return properties;
229
    }
230
 
231
    getSelectedImage() {
232
        const imgElm = this.editor.selection.getNode();
233
        const figureElm = this.editor.dom.getParent(imgElm, 'figure.image');
234
        if (figureElm) {
235
            return this.editor.dom.select('img', figureElm)[0];
236
        }
237
 
238
        if (imgElm && (imgElm.nodeName.toUpperCase() !== 'IMG' || this.isPlaceholderImage(imgElm))) {
239
            return null;
240
        }
241
        return imgElm;
242
    }
243
 
244
    isPlaceholderImage(imgElm) {
245
        if (imgElm.nodeName.toUpperCase() !== 'IMG') {
246
            return false;
247
        }
248
 
249
        return (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder'));
250
    }
251
 
252
    /**
253
     * Displays the upload loader and disables UI elements while loading a file.
254
     */
255
    startImageLoading() {
256
        showElements(Selectors.IMAGE.elements.loaderIcon, this.root);
257
        hideElements(Selectors.IMAGE.elements.insertImage, this.root);
258
    }
259
 
260
    /**
261
     * Displays the upload loader and disables UI elements while loading a file.
262
     */
263
    stopImageLoading() {
264
        hideElements(Selectors.IMAGE.elements.loaderIcon, this.root);
265
        showElements(Selectors.IMAGE.elements.insertImage, this.root);
266
    }
267
}