Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 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 embed helpers.
18
 *
19
 * This provides easy access to any classes without instantiating a new object.
20
 *
21
 * @module      tiny_media/embed/embedhelpers
22
 * @copyright   2024 Stevani Andolo <stevani@hotmail.com.au>
23
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
import Selectors from '../selectors';
27
import {getStrings} from 'core/str';
28
import {component} from "../common";
29
import {getCurrentLanguage, getMoodleLang} from 'editor_tiny/options';
30
import Ajax from 'core/ajax';
31
import {getFileName} from '../helpers';
32
 
33
/**
34
 * Return template context for insert media.
35
 *
36
 * @param {object} props
37
 * @returns {object}
38
 */
39
export const insertMediaTemplateContext = (props) => {
40
    return {
41
        mediaType: props.mediaType,
42
        showDropzone: props.canShowDropZone,
43
        showFilePicker: props.canShowFilePicker,
44
        fileType: 'audio/video',
45
    };
46
};
47
 
48
/**
49
 * Return template context for insert media.
50
 *
51
 * @param {object} props
52
 * @returns {object}
53
 */
54
export const insertMediaThumbnailTemplateContext = (props) => {
55
    return {
56
        elementid: props.editor.id,
57
        showDropzone: props.canShowDropZone,
58
        showImageFilePicker: props.canShowImageFilePicker,
59
        bodyTemplate: Selectors.EMBED.template.body.insertMediaBody,
60
        footerTemplate: Selectors.EMBED.template.footer.insertMediaFooter,
61
        fileType: 'image',
62
        selector: Selectors.EMBED.type,
63
    };
64
};
65
 
66
/**
67
 * Return selected media type and element.
68
 *
69
 * @param {editor} editor
70
 * @returns {Array}
71
 */
72
export const getSelectedMediaElement = (editor) => {
73
    let mediaType = null;
74
    let selectedMedia = null;
75
    const mediaElm = editor.selection.getNode();
76
 
77
    if (!mediaElm) {
78
        mediaType = null;
79
        selectedMedia = null;
80
    } else if (mediaElm.nodeName.toLowerCase() === 'video' || mediaElm.nodeName.toLowerCase() === 'audio') {
81
        mediaType = mediaElm.nodeName.toLowerCase();
82
        selectedMedia = mediaElm;
83
    } else if (mediaElm.querySelector('video')) {
84
        mediaType = 'video';
85
        selectedMedia = mediaElm.querySelector('video');
86
    } else if (mediaElm.querySelector('audio')) {
87
        mediaType = 'audio';
88
        selectedMedia = mediaElm.querySelector('audio');
89
    } else if (mediaElm.nodeName.toLowerCase() === 'a') {
90
        selectedMedia = mediaElm;
91
    }
92
 
93
    return [mediaType, selectedMedia];
94
};
95
 
96
/**
97
 * Returns result of media filtering.
98
 *
99
 * @param {string} url
100
 * @param {string} contextId
101
 * @returns {string}
102
 */
103
export const fetchPreview = async(url, contextId) => {
104
    const request = {
105
        methodname: 'tiny_media_preview',
106
        args: {
107
            contextid: contextId, // Use the system one.
108
            content: url,
109
        }
110
    };
111
    const responseObj = await Ajax.call([request])[0];
112
    return responseObj.content;
113
};
114
 
115
/**
116
 * Returns media type.
117
 *
118
 * @param {string} url
119
 * @returns {string|null}
120
 */
121
export const checkMediaType = async(url) => {
122
    try {
123
        const response = await fetch(url, {method: 'HEAD'});
124
        const contentType = response.headers.get('Content-type');
125
 
126
        if (!contentType) {
127
            return null;
128
        }
129
 
130
        if (contentType.startsWith('video/')) {
131
            return 'video';
132
        } else if (contentType.startsWith('audio/')) {
133
            return 'audio';
134
        }
135
 
136
        return null;
137
    } catch (e) {
138
        return null;
139
    }
140
};
141
 
142
/**
143
 * Returns media title.
144
 *
145
 * @param {string} url
146
 * @param {object} props
147
 * @returns {string|null} String of media title.
148
 */
149
export const getMediaTitle = async(url, props) => {
150
    const extension = url.split('.').pop().split('?')[0];
151
    if (props.acceptedMediaTypes.includes(`.${extension}`)) {
152
        return getFileName(url);
153
    }
154
 
155
    return null;
156
};
157
 
158
/**
159
 * Return template context for media details.
160
 *
161
 * @param {object} props
162
 * @returns {object}
163
 */
164
export const mediaDetailsTemplateContext = async(props) => {
165
    const context = {
166
        bodyTemplate: Selectors.EMBED.template.body.mediaDetailsBody,
167
        footerTemplate: Selectors.EMBED.template.footer.mediaDetailsFooter,
168
        isLink: (props.mediaType === 'link'),
169
        isVideo: (props.mediaType === 'video'),
170
        showControl: (props.mediaType === 'video' || props.mediaType === 'audio'),
171
        isUpdating: props.isUpdating,
172
        isNewFileOrLinkUpload: (props.newMediaLink || props.newFileUpload),
173
        selector: Selectors.EMBED.type,
174
    };
175
 
176
    return {...context, ...props};
177
};
178
 
179
/**
180
 * Get help strings.
181
 *
182
 * @returns {object}
183
 */
184
export const getHelpStrings = async() => {
185
    const [
186
        subtitles,
187
        captions,
188
        descriptions,
189
        chapters,
190
        metadata,
191
        customsize,
192
        linkcustomsize,
193
    ] = await getStrings([
194
        'subtitles_help',
195
        'captions_help',
196
        'descriptions_help',
197
        'chapters_help',
198
        'metadata_help',
199
        'customsize_help',
200
        'linkcustomsize_help',
201
    ].map((key) => ({
202
        key,
203
        component,
204
    })));
205
 
206
    return {
207
        subtitles,
208
        captions,
209
        descriptions,
210
        chapters,
211
        metadata,
212
        customsize,
213
        linkcustomsize,
214
    };
215
};
216
 
217
/**
218
 * Get current moodle languages.
219
 *
220
 * @param {editor} editor
221
 * @returns {object}
222
 */
223
export const prepareMoodleLang = (editor) => {
224
    const moodleLangs = getMoodleLang(editor);
225
    const currentLanguage = getCurrentLanguage(editor);
226
 
227
    const installed = Object.entries(moodleLangs.installed).map(([code, lang]) => ({
228
        lang,
229
        code,
230
        "default": code === currentLanguage,
231
    }));
232
 
233
    const available = Object.entries(moodleLangs.available).map(([code, lang]) => ({
234
        lang,
235
        code,
236
        "default": code === currentLanguage,
237
    }));
238
 
239
    return {
240
        installed,
241
        available,
242
    };
243
};
244
 
245
/**
246
 * Return moodle lang.
247
 *
248
 * @param {string} subtitleLang
249
 * @param {editor} editor
250
 * @returns {object|null}
251
 */
252
export const getMoodleLangObj = (subtitleLang, editor) => {
253
    const {available} = getMoodleLang(editor);
254
 
255
    if (available[subtitleLang]) {
256
        return {
257
            lang: subtitleLang,
258
            code: available[subtitleLang],
259
        };
260
    }
261
 
262
    return null;
263
};
264
 
265
/**
266
 * Get media data from the inserted media.
267
 *
268
 * @param {object} props
269
 * @returns {object}
270
 */
271
export const getEmbeddedMediaDetails = (props) => {
272
    const tracks = {
273
        subtitles: [],
274
        captions: [],
275
        descriptions: [],
276
        chapters: [],
277
        metadata: []
278
    };
279
 
280
    const mediaMetadata = props.root.querySelectorAll(Selectors.EMBED.elements.mediaMetadataTabPane);
281
    mediaMetadata.forEach(metaData => {
282
        const trackElements = metaData.querySelectorAll(Selectors.EMBED.elements.track);
283
        trackElements.forEach(track => {
284
            tracks[metaData.dataset.trackKind].push({
285
                src: track.querySelector(Selectors.EMBED.elements.url).value,
286
                srclang: track.querySelector(Selectors.EMBED.elements.trackLang).value,
287
                label: track.querySelector(Selectors.EMBED.elements.trackLabel).value,
288
                defaultTrack: track.querySelector(Selectors.EMBED.elements.trackDefault).checked,
289
            });
290
        });
291
    });
292
 
293
    const querySelector = (element) => props.root.querySelector(element);
294
    const mediaDataProps = {};
295
    mediaDataProps.media = {
296
        type: props.mediaType,
297
        sources: props.media,
298
        poster: props.media.poster ?? null,
299
        title: querySelector(Selectors.EMBED.elements.title).value,
300
        width: querySelector(Selectors.EMBED.elements.width).value,
301
        height: querySelector(Selectors.EMBED.elements.height).value,
302
        autoplay: querySelector(Selectors.EMBED.elements.mediaAutoplay).checked,
303
        loop: querySelector(Selectors.EMBED.elements.mediaLoop).checked,
304
        muted: querySelector(Selectors.EMBED.elements.mediaMute).checked,
305
        controls: querySelector(Selectors.EMBED.elements.mediaControl).checked,
306
        tracks,
307
    };
308
    mediaDataProps.link = false;
309
    return mediaDataProps;
310
};
311
 
312
/**
313
 * Check for video/audio attributes.
314
 *
315
 * @param {HTMLElement} elem
316
 * @param {string} attr Attribute name
317
 * @returns {boolean}
318
 */
319
export const hasAudioVideoAttr = (elem, attr) => {
320
    // As explained in MDL-64175, some OS (like Ubuntu), are removing the value for these attributes.
321
    // So in order to check if attr="true", we need to check if the attribute exists and if the value is empty or true.
322
    return (elem.hasAttribute(attr) && (elem.getAttribute(attr) || elem.getAttribute(attr) === ''));
323
};