Proyectos de Subversion Moodle

Rev

Rev 1 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 1 Rev 1441
Línea 27... Línea 27...
27
import Pending from 'core/pending';
27
import Pending from 'core/pending';
28
import Selectors from './selectors';
28
import Selectors from './selectors';
29
import Templates from 'core/templates';
29
import Templates from 'core/templates';
30
import {getString} from 'core/str';
30
import {getString} from 'core/str';
31
import {ImageInsert} from 'tiny_media/imageinsert';
31
import {ImageInsert} from 'tiny_media/imageinsert';
-
 
32
import {MediaBase} from './mediabase';
32
import {
33
import {
33
    bodyImageInsert,
34
    body,
34
    footerImageInsert,
35
    footer,
35
    showElements,
-
 
36
    hideElements,
36
    hideElements,
-
 
37
    showElements,
37
    isPercentageValue,
38
    isPercentageValue,
38
} from 'tiny_media/imagehelpers';
39
} from './helpers';
Línea 39... Línea 40...
39
 
40
 
40
export class ImageDetails {
41
export class ImageDetails extends MediaBase {
41
    DEFAULTS = {
42
    DEFAULTS = {
42
        WIDTH: 160,
43
        WIDTH: 160,
43
        HEIGHT: 160,
44
        HEIGHT: 160,
Línea -... Línea 45...
-
 
45
    };
-
 
46
 
44
    };
47
    selectorType = Selectors.IMAGE.type;
Línea 45... Línea 48...
45
 
48
 
46
    rawImageDimensions = null;
49
    mediaDimensions = null;
47
 
50
 
48
    constructor(
51
    constructor(
49
        root,
52
        root,
50
        editor,
53
        editor,
51
        currentModal,
54
        currentModal,
52
        canShowFilePicker,
55
        canShowFilePicker,
53
        canShowDropZone,
56
        canShowDropZone,
-
 
57
        currentUrl,
54
        currentUrl,
58
        image,
55
        image,
59
    ) {
56
    ) {
60
        super();
57
        this.root = root;
61
        this.root = root;
58
        this.editor = editor;
62
        this.editor = editor;
59
        this.currentModal = currentModal;
63
        this.currentModal = currentModal;
60
        this.canShowFilePicker = canShowFilePicker;
64
        this.canShowFilePicker = canShowFilePicker;
-
 
65
        this.canShowDropZone = canShowDropZone;
61
        this.canShowDropZone = canShowDropZone;
66
        this.currentUrl = currentUrl;
Línea 62... Línea 67...
62
        this.currentUrl = currentUrl;
67
        this.image = image;
63
        this.image = image;
68
        this.toggleMaxlengthFeedbackSuffix = false;
64
    }
69
    }
Línea 78... Línea 83...
78
    loadInsertImage = async function() {
83
    loadInsertImage = async function() {
79
        const templateContext = {
84
        const templateContext = {
80
            elementid: this.editor.id,
85
            elementid: this.editor.id,
81
            showfilepicker: this.canShowFilePicker,
86
            showfilepicker: this.canShowFilePicker,
82
            showdropzone: this.canShowDropZone,
87
            showdropzone: this.canShowDropZone,
-
 
88
            bodyTemplate: Selectors.IMAGE.template.body.insertImageBody,
-
 
89
            footerTemplate: Selectors.IMAGE.template.footer.insertImageFooter,
-
 
90
            selector: Selectors.IMAGE.type,
83
        };
91
        };
Línea 84... Línea 92...
84
 
92
 
85
        Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])
93
        Promise.all([body(templateContext, this.root), footer(templateContext, this.root)])
86
            .then(() => {
94
            .then(() => {
87
                const imageinsert = new ImageInsert(
95
                const imageinsert = new ImageInsert(
88
                    this.root,
96
                    this.root,
89
                    this.editor,
97
                    this.editor,
Línea 99... Línea 107...
99
            });
107
            });
100
    };
108
    };
Línea 101... Línea 109...
101
 
109
 
102
    storeImageDimensions(image) {
110
    storeImageDimensions(image) {
103
        // Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG).
111
        // Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG).
104
        this.rawImageDimensions = {
112
        this.mediaDimensions = {
105
            width: image.width || this.DEFAULTS.WIDTH,
113
            width: image.width || this.DEFAULTS.WIDTH,
106
            height: image.height || this.DEFAULTS.HEIGHT,
114
            height: image.height || this.DEFAULTS.HEIGHT,
Línea 107... Línea 115...
107
        };
115
        };
108
 
116
 
109
        const getCurrentWidth = (element) => {
117
        const getCurrentWidth = (element) => {
110
            if (element.value === '') {
118
            if (element.value === '') {
111
                element.value = this.rawImageDimensions.width;
119
                element.value = this.mediaDimensions.width;
112
            }
120
            }
Línea 113... Línea 121...
113
            return element.value;
121
            return element.value;
114
        };
122
        };
115
 
123
 
116
        const getCurrentHeight = (element) => {
124
        const getCurrentHeight = (element) => {
117
            if (element.value === '') {
125
            if (element.value === '') {
118
                element.value = this.rawImageDimensions.height;
126
                element.value = this.mediaDimensions.height;
Línea 119... Línea 127...
119
            }
127
            }
Línea 149... Línea 157...
149
         *
157
         *
150
         * @param {number} currentWidth - The current width value.
158
         * @param {number} currentWidth - The current width value.
151
         * @param {number} currentHeight - The current height value.
159
         * @param {number} currentHeight - The current height value.
152
         */
160
         */
153
        const setSelectedSize = (currentWidth, currentHeight) => {
161
        const setSelectedSize = (currentWidth, currentHeight) => {
154
            if (this.rawImageDimensions.width === currentWidth &&
162
            if (this.mediaDimensions.width === currentWidth &&
155
                this.rawImageDimensions.height === currentHeight
163
                this.mediaDimensions.height === currentHeight
156
            ) {
164
            ) {
157
                this.currentWidth = this.rawImageDimensions.width;
165
                this.currentWidth = this.mediaDimensions.width;
158
                this.currentHeight = this.rawImageDimensions.height;
166
                this.currentHeight = this.mediaDimensions.height;
159
                this.sizeChecked('original');
167
                this.sizeChecked('original');
160
            } else {
168
            } else {
161
                this.currentWidth = currentWidth;
169
                this.currentWidth = currentWidth;
162
                this.currentHeight = currentHeight;
170
                this.currentHeight = currentHeight;
163
                this.sizeChecked('custom');
171
                this.sizeChecked('custom');
Línea 166... Línea 174...
166
 
174
 
167
        setSelectedSize(Number(currentWidth), Number(currentHeight));
175
        setSelectedSize(Number(currentWidth), Number(currentHeight));
Línea 168... Línea 176...
168
    }
176
    }
169
 
-
 
170
    /**
-
 
171
     * Handles the selection of image size options and updates the form inputs accordingly.
-
 
172
     *
-
 
173
     * @param {string} option - The selected image size option ("original" or "custom").
-
 
174
     */
-
 
175
    sizeChecked(option) {
-
 
176
        const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width);
-
 
177
        const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height);
-
 
178
        if (option === "original") {
-
 
179
            this.sizeOriginalChecked();
-
 
180
            widthInput.value = this.rawImageDimensions.width;
-
 
181
            heightInput.value = this.rawImageDimensions.height;
-
 
182
        } else if (option === "custom") {
-
 
183
            this.sizeCustomChecked();
-
 
184
            widthInput.value = this.currentWidth;
-
 
185
            heightInput.value = this.currentHeight;
-
 
186
 
-
 
187
            // If the current size is equal to the original size, then check the Keep proportion checkbox.
-
 
188
            if (this.currentWidth === this.rawImageDimensions.width && this.currentHeight === this.rawImageDimensions.height) {
-
 
189
                const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain);
-
 
190
                constrainField.checked = true;
-
 
191
            }
-
 
192
        }
-
 
193
        this.autoAdjustSize();
-
 
194
    }
-
 
195
 
-
 
196
    autoAdjustSize(forceHeight = false) {
-
 
197
        // If we do not know the image size, do not do anything.
-
 
198
        if (!this.rawImageDimensions) {
-
 
199
            return;
-
 
200
        }
-
 
201
 
-
 
202
        const widthField = this.root.querySelector(Selectors.IMAGE.elements.width);
-
 
203
        const heightField = this.root.querySelector(Selectors.IMAGE.elements.height);
-
 
204
 
-
 
205
        const normalizeFieldData = (fieldData) => {
-
 
206
            fieldData.isPercentageValue = !!isPercentageValue(fieldData.field.value);
-
 
207
            if (fieldData.isPercentageValue) {
-
 
208
                fieldData.percentValue = parseInt(fieldData.field.value, 10);
-
 
209
                fieldData.pixelSize = this.rawImageDimensions[fieldData.type] / 100 * fieldData.percentValue;
-
 
210
            } else {
-
 
211
                fieldData.pixelSize = parseInt(fieldData.field.value, 10);
-
 
212
                fieldData.percentValue = fieldData.pixelSize / this.rawImageDimensions[fieldData.type] * 100;
-
 
213
            }
-
 
214
 
-
 
215
            return fieldData;
-
 
216
        };
-
 
217
 
-
 
218
        const getKeyField = () => {
-
 
219
            const getValue = () => {
-
 
220
                if (forceHeight) {
-
 
221
                    return {
-
 
222
                        field: heightField,
-
 
223
                        type: 'height',
-
 
224
                    };
-
 
225
                } else {
-
 
226
                    return {
-
 
227
                        field: widthField,
-
 
228
                        type: 'width',
-
 
229
                    };
-
 
230
                }
-
 
231
            };
-
 
232
 
-
 
233
            const currentValue = getValue();
-
 
234
            if (currentValue.field.value === '') {
-
 
235
                currentValue.field.value = this.rawImageDimensions[currentValue.type];
-
 
236
            }
-
 
237
 
-
 
238
            return normalizeFieldData(currentValue);
-
 
239
        };
-
 
240
 
-
 
241
        const getRelativeField = () => {
-
 
242
            if (forceHeight) {
-
 
243
                return normalizeFieldData({
-
 
244
                    field: widthField,
-
 
245
                    type: 'width',
-
 
246
                });
-
 
247
            } else {
-
 
248
                return normalizeFieldData({
-
 
249
                    field: heightField,
-
 
250
                    type: 'height',
-
 
251
                });
-
 
252
            }
-
 
253
        };
-
 
254
 
-
 
255
        // Now update with the new values.
-
 
256
        const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain);
-
 
257
        if (constrainField.checked) {
-
 
258
            const keyField = getKeyField();
-
 
259
            const relativeField = getRelativeField();
-
 
260
            // We are keeping the image in proportion.
-
 
261
            // Calculate the size for the relative field.
-
 
262
            if (keyField.isPercentageValue) {
-
 
263
                // In proportion, so the percentages are the same.
-
 
264
                relativeField.field.value = keyField.field.value;
-
 
265
                relativeField.percentValue = keyField.percentValue;
-
 
266
            } else {
-
 
267
                relativeField.pixelSize = Math.round(
-
 
268
                    keyField.pixelSize / this.rawImageDimensions[keyField.type] * this.rawImageDimensions[relativeField.type]
-
 
269
                );
-
 
270
                relativeField.field.value = relativeField.pixelSize;
-
 
271
            }
-
 
272
        }
-
 
273
 
-
 
274
        // Store the custom width and height to reuse.
-
 
275
        this.currentWidth = Number(widthField.value) !== this.rawImageDimensions.width ? widthField.value : this.currentWidth;
-
 
276
        this.currentHeight = Number(heightField.value) !== this.rawImageDimensions.height ? heightField.value : this.currentHeight;
-
 
277
    }
-
 
278
 
177
 
279
    /**
178
    /**
280
     * Sets the dimensions of the image preview element based on user input and constraints.
179
     * Sets the dimensions of the image preview element based on user input and constraints.
281
     */
180
     */
282
    setImageDimensions = () => {
181
    setImageDimensions = () => {
Línea 304... Línea 203...
304
            updateImageDimensions();
203
            updateImageDimensions();
305
        }
204
        }
306
    };
205
    };
Línea 307... Línea 206...
307
 
206
 
308
    /**
-
 
309
     * Handles the selection of the "Original Size" option and updates the form elements accordingly.
-
 
310
     */
-
 
311
    sizeOriginalChecked() {
-
 
312
        this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = true;
-
 
313
        this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = false;
-
 
314
        hideElements(Selectors.IMAGE.elements.properties, this.root);
-
 
315
    }
-
 
316
 
-
 
317
    /**
-
 
318
     * Handles the selection of the "Custom Size" option and updates the form elements accordingly.
-
 
319
     */
-
 
320
    sizeCustomChecked() {
-
 
321
        this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = false;
-
 
322
        this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = true;
-
 
323
        showElements(Selectors.IMAGE.elements.properties, this.root);
-
 
324
    }
-
 
325
 
-
 
326
    /**
207
    /**
327
     * Handles changes in the image presentation checkbox and enables/disables the image alt text input accordingly.
208
     * Handles changes in the image presentation checkbox and enables/disables the image alt text input accordingly.
328
     */
209
     */
329
    presentationChanged() {
210
    async presentationChanged() {
330
        const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation);
211
        const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation);
331
        const alt = this.root.querySelector(Selectors.IMAGE.elements.alt);
212
        const alt = this.root.querySelector(Selectors.IMAGE.elements.alt);
Línea 332... Línea 213...
332
        alt.disabled = presentation.checked;
213
        alt.disabled = presentation.checked;
333
 
214
 
334
        // Counting the image description characters.
215
        // Counting the image description characters.
Línea 335... Línea 216...
335
        this.handleKeyupCharacterCount();
216
        await this.handleKeyupCharacterCount();
336
    }
217
    }
337
 
218
 
Línea 400... Línea 281...
400
        const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation).checked;
281
        const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation).checked;
401
        const imageAltError = alt === '' && !presentation;
282
        const imageAltError = alt === '' && !presentation;
402
        if (imageAltError) {
283
        if (imageAltError) {
403
            showElements(Selectors.IMAGE.elements.altWarning, this.root);
284
            showElements(Selectors.IMAGE.elements.altWarning, this.root);
404
        } else {
285
        } else {
405
            hideElements(Selectors.IMAGE.elements.urlWaaltWarningrning, this.root);
286
            hideElements(Selectors.IMAGE.elements.altWarning, this.root);
406
        }
287
        }
407
        this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError);
288
        this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError);
Línea 408... Línea 289...
408
 
289
 
409
        return imageAltError;
290
        return imageAltError;
Línea 517... Línea 398...
517
            if (e.key === "Enter") {
398
            if (e.key === "Enter") {
518
                this.deleteImage();
399
                this.deleteImage();
519
            }
400
            }
520
        });
401
        });
Línea 521... Línea 402...
521
 
402
 
522
        this.root.addEventListener('change', (e) => {
403
        this.root.addEventListener('change', async(e) => {
523
            const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);
404
            const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);
524
            if (presentationEle) {
405
            if (presentationEle) {
525
                this.presentationChanged();
406
                await this.presentationChanged();
Línea 526... Línea 407...
526
            }
407
            }
527
 
408
 
528
            const constrainEle = e.target.closest(Selectors.IMAGE.elements.constrain);
409
            const constrainEle = e.target.closest(Selectors.IMAGE.elements.constrain);
Línea 539... Línea 420...
539
            if (sizeCustomEle) {
420
            if (sizeCustomEle) {
540
                this.sizeChecked('custom');
421
                this.sizeChecked('custom');
541
            }
422
            }
542
        });
423
        });
Línea 543... Línea 424...
543
 
424
 
544
        this.root.addEventListener('blur', (e) => {
425
        this.root.addEventListener('blur', async(e) => {
Línea 545... Línea 426...
545
            if (e.target.nodeType === Node.ELEMENT_NODE) {
426
            if (e.target.nodeType === Node.ELEMENT_NODE) {
546
 
427
 
547
                const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);
428
                const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);
548
                if (presentationEle) {
429
                if (presentationEle) {
549
                    this.presentationChanged();
430
                    await this.presentationChanged();
550
                }
431
                }
Línea 551... Línea 432...
551
            }
432
            }
552
        }, true);
433
        }, true);
553
 
434
 
554
        // Character count.
435
        // Character count.
555
        this.root.addEventListener('keyup', (e) => {
436
        this.root.addEventListener('keyup', async(e) => {
556
            const altEle = e.target.closest(Selectors.IMAGE.elements.alt);
437
            const altEle = e.target.closest(Selectors.IMAGE.elements.alt);
557
            if (altEle) {
438
            if (altEle) {
Línea 558... Línea 439...
558
                this.handleKeyupCharacterCount();
439
                await this.handleKeyupCharacterCount();
559
            }
440
            }
Línea 574... Línea 455...
574
                this.autoAdjustSize(true);
455
                this.autoAdjustSize(true);
575
            }
456
            }
576
        });
457
        });
577
    }
458
    }
Línea 578... Línea 459...
578
 
459
 
579
    handleKeyupCharacterCount() {
460
    async handleKeyupCharacterCount() {
-
 
461
        const altField = this.root.querySelector(Selectors.IMAGE.elements.alt);
580
        const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value;
462
        const alt = altField.value;
581
        const current = this.root.querySelector('#currentcount');
463
        const current = this.root.querySelector('#currentcount');
-
 
464
        current.innerHTML = alt.length;
-
 
465
        const maxLength = altField.getAttribute('maxlength');
-
 
466
        const maxLengthFeedback = document.getElementById('maxlength_feedback');
-
 
467
        if (alt.length >= maxLength) {
-
 
468
            maxLengthFeedback.textContent = await getString('maxlengthreached', 'core', maxLength);
-
 
469
 
-
 
470
            // Clever (or hacky?;p) way to ensure that the feedback message is announced to screen readers.
-
 
471
            const suffix = this.toggleMaxlengthFeedbackSuffix ? '' : '.';
-
 
472
            maxLengthFeedback.textContent += suffix;
-
 
473
            this.toggleMaxlengthFeedbackSuffix = !this.toggleMaxlengthFeedbackSuffix;
-
 
474
 
-
 
475
            // Clear the feedback message after 4 seconds. This is similar to the default timeout of toast messages
-
 
476
            // before disappearing from view. It is important to clear the message to prevent screen reader users from navigating
-
 
477
            // into this region and avoiding confusion.
-
 
478
            setTimeout(() => {
-
 
479
                maxLengthFeedback.textContent = '';
-
 
480
            }, 4000);
582
        current.innerHTML = alt.length;
481
        }
Línea 583... Línea 482...
583
    }
482
    }
584
 
483
 
585
    /**
484
    /**