@oat-sa/tao-core-ui 3.22.0 → 3.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/figure/FigureStateActive.js +64 -32
- package/dist/image/ImgStateActive/initHelper.js +64 -32
- package/dist/image/ImgStateActive.js +64 -32
- package/dist/mediaplayer/players/html5.js +44 -18
- package/dist/mediaplayer/utils/crossOrigin.js +64 -0
- package/dist/mediaplayer.js +7 -1
- package/dist/previewer.js +1 -1
- package/dist/resourcemgr/assetSearch.js +793 -0
- package/dist/resourcemgr/assetSearchContract.js +477 -0
- package/dist/resourcemgr/css/resourcemgr.css +1137 -97
- package/dist/resourcemgr/css/resourcemgr.css.map +1 -1
- package/dist/resourcemgr/fileBrowser.js +456 -79
- package/dist/resourcemgr/filePreview.js +40 -4
- package/dist/resourcemgr/fileSelector.js +362 -92
- package/dist/resourcemgr.js +309 -57
- package/dist/searchModal/advancedSearch.js +233 -31
- package/package.json +15 -6
- package/src/image/ImgStateActive/initHelper.js +69 -31
- package/src/mediaplayer/players/html5.js +17 -7
- package/src/mediaplayer/readme.md +17 -0
- package/src/mediaplayer/tpl/audio.tpl +1 -1
- package/src/mediaplayer/tpl/video.tpl +1 -1
- package/src/mediaplayer/utils/crossOrigin.js +63 -0
- package/src/mediaplayer.js +10 -0
- package/src/previewer.js +1 -1
- package/src/resourcemgr/assetSearch.js +847 -0
- package/src/resourcemgr/assetSearchContract.js +489 -0
- package/src/resourcemgr/css/resourcemgr.css +1137 -97
- package/src/resourcemgr/css/resourcemgr.css.map +1 -1
- package/src/resourcemgr/fileBrowser.js +470 -91
- package/src/resourcemgr/filePreview.js +46 -4
- package/src/resourcemgr/fileSelector.js +338 -46
- package/src/resourcemgr/scss/resourcemgr.scss +1351 -88
- package/src/resourcemgr/tpl/advanced-search.tpl +10 -0
- package/src/resourcemgr/tpl/fileSelect.tpl +25 -28
- package/src/resourcemgr/tpl/folder.tpl +4 -1
- package/src/resourcemgr/tpl/layout.tpl +113 -54
- package/src/resourcemgr/tpl/rootFolder.tpl +3 -1
- package/src/resourcemgr.js +289 -47
- package/src/searchModal/advancedSearch.js +242 -31
|
@@ -212,9 +212,9 @@ define(['jquery', 'nouislider', 'ui/resourcemgr', 'ui/tooltip', 'lodash', 'i18n'
|
|
|
212
212
|
*
|
|
213
213
|
* You should have received a copy of the GNU General Public License
|
|
214
214
|
* along with this program; if not, write to the Free Software
|
|
215
|
-
* Foundation, Inc.,
|
|
215
|
+
* Foundation, Inc., 31 Milk St # 960789 Boston, MA 02196 USA.
|
|
216
216
|
*
|
|
217
|
-
* Copyright (c) 2021 (original work) Open Assessment Technologies SA ;
|
|
217
|
+
* Copyright (c) 2021-2026 (original work) Open Assessment Technologies SA ;
|
|
218
218
|
*
|
|
219
219
|
*/
|
|
220
220
|
const initAdvanced = function (widget) {
|
|
@@ -233,21 +233,45 @@ define(['jquery', 'nouislider', 'ui/resourcemgr', 'ui/tooltip', 'lodash', 'i18n'
|
|
|
233
233
|
const $form = widget.$form;
|
|
234
234
|
const options = widget.options;
|
|
235
235
|
const {
|
|
236
|
-
img
|
|
236
|
+
img,
|
|
237
|
+
$img
|
|
237
238
|
} = getImage(widget);
|
|
238
239
|
const $uploadTrigger = $form.find('[data-role="upload-trigger"]');
|
|
239
240
|
const $src = $form.find('input[name=src]');
|
|
240
241
|
const $alt = $form.find('input[name=alt]');
|
|
242
|
+
const commitSrc = function (srcValue) {
|
|
243
|
+
$src.val(srcValue);
|
|
244
|
+
// Keep QTI model + preview in sync immediately: the form src callback is
|
|
245
|
+
// throttled (1s), so a late trailing call must not leave a rejected pick.
|
|
246
|
+
if (img) {
|
|
247
|
+
img.attr('src', srcValue);
|
|
248
|
+
}
|
|
249
|
+
if ($img && $img.length) {
|
|
250
|
+
$img.attr('src', srcValue ? widget.getAssetManager().resolve(srcValue) : '');
|
|
251
|
+
$img.trigger('contentChange.qti-widget').change();
|
|
252
|
+
}
|
|
253
|
+
_.defer(function () {
|
|
254
|
+
if (img) {
|
|
255
|
+
img.attr('off-media-editor', 1);
|
|
256
|
+
}
|
|
257
|
+
$src.trigger('change');
|
|
258
|
+
});
|
|
259
|
+
};
|
|
241
260
|
const _openResourceMgr = function () {
|
|
242
261
|
$uploadTrigger.resourcemgr({
|
|
243
262
|
title: __('Please select an image file from the resource manager. You can add files from your computer with the button "Add file(s)".'),
|
|
244
263
|
appendContainer: options.mediaManager.appendContainer,
|
|
245
264
|
mediaSourcesUrl: options.mediaManager.mediaSourcesUrl,
|
|
246
265
|
browseUrl: options.mediaManager.browseUrl,
|
|
266
|
+
searchUrl: options.mediaManager.searchUrl,
|
|
247
267
|
uploadUrl: options.mediaManager.uploadUrl,
|
|
248
268
|
deleteUrl: options.mediaManager.deleteUrl,
|
|
249
269
|
downloadUrl: options.mediaManager.downloadUrl,
|
|
250
270
|
fileExistsUrl: options.mediaManager.fileExistsUrl,
|
|
271
|
+
rootClassUri: options.mediaManager.rootClassUri,
|
|
272
|
+
classMappingUrl: options.mediaManager.classMappingUrl,
|
|
273
|
+
statusUrl: options.mediaManager.statusUrl,
|
|
274
|
+
maxListSize: options.mediaManager.maxListSize,
|
|
251
275
|
params: {
|
|
252
276
|
uri: options.uri,
|
|
253
277
|
lang: options.lang,
|
|
@@ -267,41 +291,49 @@ define(['jquery', 'nouislider', 'ui/resourcemgr', 'ui/tooltip', 'lodash', 'i18n'
|
|
|
267
291
|
pathParam: 'path',
|
|
268
292
|
path: options.mediaManager.path,
|
|
269
293
|
root: options.mediaManager.root,
|
|
294
|
+
currentAsset: $src.val() || null,
|
|
270
295
|
select: function (e, files) {
|
|
271
296
|
let file, alt;
|
|
272
297
|
let confirmBox, cancel, save;
|
|
273
|
-
if (files && files.length) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
298
|
+
if (!(files && files.length)) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
file = files[0].file;
|
|
302
|
+
alt = files[0].alt;
|
|
303
|
+
|
|
304
|
+
// Apply the new file immediately; alt confirm only decides alt text.
|
|
305
|
+
commitSrc(file);
|
|
306
|
+
if ($$1.trim($alt.val()) === '') {
|
|
307
|
+
if (alt === '') {
|
|
308
|
+
alt = extractLabel(file);
|
|
309
|
+
}
|
|
310
|
+
if (img) {
|
|
281
311
|
img.attr('alt', alt);
|
|
282
|
-
$alt.val(alt).trigger('change');
|
|
283
|
-
} else {
|
|
284
|
-
confirmBox = $$1('.change-alt-modal-feedback', $form);
|
|
285
|
-
cancel = confirmBox.find('.cancel');
|
|
286
|
-
save = confirmBox.find('.save');
|
|
287
|
-
$$1('.alt-text', confirmBox).html(`"${$alt.val()}"<br>${__('with')}<br>"${alt}" ?`);
|
|
288
|
-
confirmBox.modal({
|
|
289
|
-
width: 500
|
|
290
|
-
});
|
|
291
|
-
save.off('click').on('click', function () {
|
|
292
|
-
img.attr('alt', alt);
|
|
293
|
-
$alt.val(alt).trigger('change');
|
|
294
|
-
confirmBox.modal('close');
|
|
295
|
-
});
|
|
296
|
-
cancel.off('click').on('click', function () {
|
|
297
|
-
confirmBox.modal('close');
|
|
298
|
-
});
|
|
299
312
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
$src.trigger('change');
|
|
303
|
-
});
|
|
313
|
+
$alt.val(alt).trigger('change');
|
|
314
|
+
return;
|
|
304
315
|
}
|
|
316
|
+
|
|
317
|
+
// Existing alt: confirm replace. Cancel / overlay / Esc keeps the new image;
|
|
318
|
+
// only alt text stays unchanged unless the user confirms.
|
|
319
|
+
confirmBox = $$1('.change-alt-modal-feedback', $form);
|
|
320
|
+
cancel = confirmBox.find('.cancel');
|
|
321
|
+
save = confirmBox.find('.save');
|
|
322
|
+
const $altText = $$1('.alt-text', confirmBox).empty();
|
|
323
|
+
$altText.append(document.createTextNode(`"${$alt.val()}"`)).append($$1('<br>')).append(document.createTextNode(__('with'))).append($$1('<br>')).append(document.createTextNode(`"${alt}" ?`));
|
|
324
|
+
confirmBox.modal({
|
|
325
|
+
width: 500
|
|
326
|
+
});
|
|
327
|
+
save.off('click').on('click', function () {
|
|
328
|
+
if (img) {
|
|
329
|
+
img.attr('alt', alt);
|
|
330
|
+
}
|
|
331
|
+
$alt.val(alt).trigger('change');
|
|
332
|
+
confirmBox.modal('close');
|
|
333
|
+
});
|
|
334
|
+
cancel.off('click').on('click', function () {
|
|
335
|
+
confirmBox.modal('close');
|
|
336
|
+
});
|
|
305
337
|
},
|
|
306
338
|
open: function () {
|
|
307
339
|
// hide tooltip if displayed
|
|
@@ -210,9 +210,9 @@ define(['exports', 'jquery', 'lodash', 'i18n', 'core/mimetype', 'ui/mediaEditor/
|
|
|
210
210
|
*
|
|
211
211
|
* You should have received a copy of the GNU General Public License
|
|
212
212
|
* along with this program; if not, write to the Free Software
|
|
213
|
-
* Foundation, Inc.,
|
|
213
|
+
* Foundation, Inc., 31 Milk St # 960789 Boston, MA 02196 USA.
|
|
214
214
|
*
|
|
215
|
-
* Copyright (c) 2021 (original work) Open Assessment Technologies SA ;
|
|
215
|
+
* Copyright (c) 2021-2026 (original work) Open Assessment Technologies SA ;
|
|
216
216
|
*
|
|
217
217
|
*/
|
|
218
218
|
const initAdvanced = function (widget) {
|
|
@@ -231,21 +231,45 @@ define(['exports', 'jquery', 'lodash', 'i18n', 'core/mimetype', 'ui/mediaEditor/
|
|
|
231
231
|
const $form = widget.$form;
|
|
232
232
|
const options = widget.options;
|
|
233
233
|
const {
|
|
234
|
-
img
|
|
234
|
+
img,
|
|
235
|
+
$img
|
|
235
236
|
} = getImage(widget);
|
|
236
237
|
const $uploadTrigger = $form.find('[data-role="upload-trigger"]');
|
|
237
238
|
const $src = $form.find('input[name=src]');
|
|
238
239
|
const $alt = $form.find('input[name=alt]');
|
|
240
|
+
const commitSrc = function (srcValue) {
|
|
241
|
+
$src.val(srcValue);
|
|
242
|
+
// Keep QTI model + preview in sync immediately: the form src callback is
|
|
243
|
+
// throttled (1s), so a late trailing call must not leave a rejected pick.
|
|
244
|
+
if (img) {
|
|
245
|
+
img.attr('src', srcValue);
|
|
246
|
+
}
|
|
247
|
+
if ($img && $img.length) {
|
|
248
|
+
$img.attr('src', srcValue ? widget.getAssetManager().resolve(srcValue) : '');
|
|
249
|
+
$img.trigger('contentChange.qti-widget').change();
|
|
250
|
+
}
|
|
251
|
+
_.defer(function () {
|
|
252
|
+
if (img) {
|
|
253
|
+
img.attr('off-media-editor', 1);
|
|
254
|
+
}
|
|
255
|
+
$src.trigger('change');
|
|
256
|
+
});
|
|
257
|
+
};
|
|
239
258
|
const _openResourceMgr = function () {
|
|
240
259
|
$uploadTrigger.resourcemgr({
|
|
241
260
|
title: __('Please select an image file from the resource manager. You can add files from your computer with the button "Add file(s)".'),
|
|
242
261
|
appendContainer: options.mediaManager.appendContainer,
|
|
243
262
|
mediaSourcesUrl: options.mediaManager.mediaSourcesUrl,
|
|
244
263
|
browseUrl: options.mediaManager.browseUrl,
|
|
264
|
+
searchUrl: options.mediaManager.searchUrl,
|
|
245
265
|
uploadUrl: options.mediaManager.uploadUrl,
|
|
246
266
|
deleteUrl: options.mediaManager.deleteUrl,
|
|
247
267
|
downloadUrl: options.mediaManager.downloadUrl,
|
|
248
268
|
fileExistsUrl: options.mediaManager.fileExistsUrl,
|
|
269
|
+
rootClassUri: options.mediaManager.rootClassUri,
|
|
270
|
+
classMappingUrl: options.mediaManager.classMappingUrl,
|
|
271
|
+
statusUrl: options.mediaManager.statusUrl,
|
|
272
|
+
maxListSize: options.mediaManager.maxListSize,
|
|
249
273
|
params: {
|
|
250
274
|
uri: options.uri,
|
|
251
275
|
lang: options.lang,
|
|
@@ -265,41 +289,49 @@ define(['exports', 'jquery', 'lodash', 'i18n', 'core/mimetype', 'ui/mediaEditor/
|
|
|
265
289
|
pathParam: 'path',
|
|
266
290
|
path: options.mediaManager.path,
|
|
267
291
|
root: options.mediaManager.root,
|
|
292
|
+
currentAsset: $src.val() || null,
|
|
268
293
|
select: function (e, files) {
|
|
269
294
|
let file, alt;
|
|
270
295
|
let confirmBox, cancel, save;
|
|
271
|
-
if (files && files.length) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
296
|
+
if (!(files && files.length)) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
file = files[0].file;
|
|
300
|
+
alt = files[0].alt;
|
|
301
|
+
|
|
302
|
+
// Apply the new file immediately; alt confirm only decides alt text.
|
|
303
|
+
commitSrc(file);
|
|
304
|
+
if ($.trim($alt.val()) === '') {
|
|
305
|
+
if (alt === '') {
|
|
306
|
+
alt = extractLabel(file);
|
|
307
|
+
}
|
|
308
|
+
if (img) {
|
|
279
309
|
img.attr('alt', alt);
|
|
280
|
-
$alt.val(alt).trigger('change');
|
|
281
|
-
} else {
|
|
282
|
-
confirmBox = $('.change-alt-modal-feedback', $form);
|
|
283
|
-
cancel = confirmBox.find('.cancel');
|
|
284
|
-
save = confirmBox.find('.save');
|
|
285
|
-
$('.alt-text', confirmBox).html(`"${$alt.val()}"<br>${__('with')}<br>"${alt}" ?`);
|
|
286
|
-
confirmBox.modal({
|
|
287
|
-
width: 500
|
|
288
|
-
});
|
|
289
|
-
save.off('click').on('click', function () {
|
|
290
|
-
img.attr('alt', alt);
|
|
291
|
-
$alt.val(alt).trigger('change');
|
|
292
|
-
confirmBox.modal('close');
|
|
293
|
-
});
|
|
294
|
-
cancel.off('click').on('click', function () {
|
|
295
|
-
confirmBox.modal('close');
|
|
296
|
-
});
|
|
297
310
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
$src.trigger('change');
|
|
301
|
-
});
|
|
311
|
+
$alt.val(alt).trigger('change');
|
|
312
|
+
return;
|
|
302
313
|
}
|
|
314
|
+
|
|
315
|
+
// Existing alt: confirm replace. Cancel / overlay / Esc keeps the new image;
|
|
316
|
+
// only alt text stays unchanged unless the user confirms.
|
|
317
|
+
confirmBox = $('.change-alt-modal-feedback', $form);
|
|
318
|
+
cancel = confirmBox.find('.cancel');
|
|
319
|
+
save = confirmBox.find('.save');
|
|
320
|
+
const $altText = $('.alt-text', confirmBox).empty();
|
|
321
|
+
$altText.append(document.createTextNode(`"${$alt.val()}"`)).append($('<br>')).append(document.createTextNode(__('with'))).append($('<br>')).append(document.createTextNode(`"${alt}" ?`));
|
|
322
|
+
confirmBox.modal({
|
|
323
|
+
width: 500
|
|
324
|
+
});
|
|
325
|
+
save.off('click').on('click', function () {
|
|
326
|
+
if (img) {
|
|
327
|
+
img.attr('alt', alt);
|
|
328
|
+
}
|
|
329
|
+
$alt.val(alt).trigger('change');
|
|
330
|
+
confirmBox.modal('close');
|
|
331
|
+
});
|
|
332
|
+
cancel.off('click').on('click', function () {
|
|
333
|
+
confirmBox.modal('close');
|
|
334
|
+
});
|
|
303
335
|
},
|
|
304
336
|
open: function () {
|
|
305
337
|
// hide tooltip if displayed
|
|
@@ -210,9 +210,9 @@ define(['jquery', 'nouislider', 'ui/resourcemgr', 'ui/tooltip', 'lodash', 'i18n'
|
|
|
210
210
|
*
|
|
211
211
|
* You should have received a copy of the GNU General Public License
|
|
212
212
|
* along with this program; if not, write to the Free Software
|
|
213
|
-
* Foundation, Inc.,
|
|
213
|
+
* Foundation, Inc., 31 Milk St # 960789 Boston, MA 02196 USA.
|
|
214
214
|
*
|
|
215
|
-
* Copyright (c) 2021 (original work) Open Assessment Technologies SA ;
|
|
215
|
+
* Copyright (c) 2021-2026 (original work) Open Assessment Technologies SA ;
|
|
216
216
|
*
|
|
217
217
|
*/
|
|
218
218
|
const initAdvanced = function (widget) {
|
|
@@ -231,21 +231,45 @@ define(['jquery', 'nouislider', 'ui/resourcemgr', 'ui/tooltip', 'lodash', 'i18n'
|
|
|
231
231
|
const $form = widget.$form;
|
|
232
232
|
const options = widget.options;
|
|
233
233
|
const {
|
|
234
|
-
img
|
|
234
|
+
img,
|
|
235
|
+
$img
|
|
235
236
|
} = getImage(widget);
|
|
236
237
|
const $uploadTrigger = $form.find('[data-role="upload-trigger"]');
|
|
237
238
|
const $src = $form.find('input[name=src]');
|
|
238
239
|
const $alt = $form.find('input[name=alt]');
|
|
240
|
+
const commitSrc = function (srcValue) {
|
|
241
|
+
$src.val(srcValue);
|
|
242
|
+
// Keep QTI model + preview in sync immediately: the form src callback is
|
|
243
|
+
// throttled (1s), so a late trailing call must not leave a rejected pick.
|
|
244
|
+
if (img) {
|
|
245
|
+
img.attr('src', srcValue);
|
|
246
|
+
}
|
|
247
|
+
if ($img && $img.length) {
|
|
248
|
+
$img.attr('src', srcValue ? widget.getAssetManager().resolve(srcValue) : '');
|
|
249
|
+
$img.trigger('contentChange.qti-widget').change();
|
|
250
|
+
}
|
|
251
|
+
_.defer(function () {
|
|
252
|
+
if (img) {
|
|
253
|
+
img.attr('off-media-editor', 1);
|
|
254
|
+
}
|
|
255
|
+
$src.trigger('change');
|
|
256
|
+
});
|
|
257
|
+
};
|
|
239
258
|
const _openResourceMgr = function () {
|
|
240
259
|
$uploadTrigger.resourcemgr({
|
|
241
260
|
title: __('Please select an image file from the resource manager. You can add files from your computer with the button "Add file(s)".'),
|
|
242
261
|
appendContainer: options.mediaManager.appendContainer,
|
|
243
262
|
mediaSourcesUrl: options.mediaManager.mediaSourcesUrl,
|
|
244
263
|
browseUrl: options.mediaManager.browseUrl,
|
|
264
|
+
searchUrl: options.mediaManager.searchUrl,
|
|
245
265
|
uploadUrl: options.mediaManager.uploadUrl,
|
|
246
266
|
deleteUrl: options.mediaManager.deleteUrl,
|
|
247
267
|
downloadUrl: options.mediaManager.downloadUrl,
|
|
248
268
|
fileExistsUrl: options.mediaManager.fileExistsUrl,
|
|
269
|
+
rootClassUri: options.mediaManager.rootClassUri,
|
|
270
|
+
classMappingUrl: options.mediaManager.classMappingUrl,
|
|
271
|
+
statusUrl: options.mediaManager.statusUrl,
|
|
272
|
+
maxListSize: options.mediaManager.maxListSize,
|
|
249
273
|
params: {
|
|
250
274
|
uri: options.uri,
|
|
251
275
|
lang: options.lang,
|
|
@@ -265,41 +289,49 @@ define(['jquery', 'nouislider', 'ui/resourcemgr', 'ui/tooltip', 'lodash', 'i18n'
|
|
|
265
289
|
pathParam: 'path',
|
|
266
290
|
path: options.mediaManager.path,
|
|
267
291
|
root: options.mediaManager.root,
|
|
292
|
+
currentAsset: $src.val() || null,
|
|
268
293
|
select: function (e, files) {
|
|
269
294
|
let file, alt;
|
|
270
295
|
let confirmBox, cancel, save;
|
|
271
|
-
if (files && files.length) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
296
|
+
if (!(files && files.length)) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
file = files[0].file;
|
|
300
|
+
alt = files[0].alt;
|
|
301
|
+
|
|
302
|
+
// Apply the new file immediately; alt confirm only decides alt text.
|
|
303
|
+
commitSrc(file);
|
|
304
|
+
if ($.trim($alt.val()) === '') {
|
|
305
|
+
if (alt === '') {
|
|
306
|
+
alt = extractLabel(file);
|
|
307
|
+
}
|
|
308
|
+
if (img) {
|
|
279
309
|
img.attr('alt', alt);
|
|
280
|
-
$alt.val(alt).trigger('change');
|
|
281
|
-
} else {
|
|
282
|
-
confirmBox = $('.change-alt-modal-feedback', $form);
|
|
283
|
-
cancel = confirmBox.find('.cancel');
|
|
284
|
-
save = confirmBox.find('.save');
|
|
285
|
-
$('.alt-text', confirmBox).html(`"${$alt.val()}"<br>${__('with')}<br>"${alt}" ?`);
|
|
286
|
-
confirmBox.modal({
|
|
287
|
-
width: 500
|
|
288
|
-
});
|
|
289
|
-
save.off('click').on('click', function () {
|
|
290
|
-
img.attr('alt', alt);
|
|
291
|
-
$alt.val(alt).trigger('change');
|
|
292
|
-
confirmBox.modal('close');
|
|
293
|
-
});
|
|
294
|
-
cancel.off('click').on('click', function () {
|
|
295
|
-
confirmBox.modal('close');
|
|
296
|
-
});
|
|
297
310
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
$src.trigger('change');
|
|
301
|
-
});
|
|
311
|
+
$alt.val(alt).trigger('change');
|
|
312
|
+
return;
|
|
302
313
|
}
|
|
314
|
+
|
|
315
|
+
// Existing alt: confirm replace. Cancel / overlay / Esc keeps the new image;
|
|
316
|
+
// only alt text stays unchanged unless the user confirms.
|
|
317
|
+
confirmBox = $('.change-alt-modal-feedback', $form);
|
|
318
|
+
cancel = confirmBox.find('.cancel');
|
|
319
|
+
save = confirmBox.find('.save');
|
|
320
|
+
const $altText = $('.alt-text', confirmBox).empty();
|
|
321
|
+
$altText.append(document.createTextNode(`"${$alt.val()}"`)).append($('<br>')).append(document.createTextNode(__('with'))).append($('<br>')).append(document.createTextNode(`"${alt}" ?`));
|
|
322
|
+
confirmBox.modal({
|
|
323
|
+
width: 500
|
|
324
|
+
});
|
|
325
|
+
save.off('click').on('click', function () {
|
|
326
|
+
if (img) {
|
|
327
|
+
img.attr('alt', alt);
|
|
328
|
+
}
|
|
329
|
+
$alt.val(alt).trigger('change');
|
|
330
|
+
confirmBox.modal('close');
|
|
331
|
+
});
|
|
332
|
+
cancel.off('click').on('click', function () {
|
|
333
|
+
confirmBox.modal('close');
|
|
334
|
+
});
|
|
303
335
|
},
|
|
304
336
|
open: function () {
|
|
305
337
|
// hide tooltip if displayed
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support', 'handlebars', 'lib/handlebars/helpers', 'ui/mediaplayer/utils/reminder', 'ui/mediaplayer/utils/timeObserver'], function ($$1, UrlParser, eventifier, support, Handlebars, Helpers0, reminderManagerFactory, timeObserverFactory) { 'use strict';
|
|
1
|
+
define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support', 'handlebars', 'lib/handlebars/helpers', 'ui/mediaplayer/utils/reminder', 'ui/mediaplayer/utils/timeObserver', 'ui/mediaplayer/utils/crossOrigin'], function ($$1, UrlParser, eventifier, support, Handlebars, Helpers0, reminderManagerFactory, timeObserverFactory, crossOriginUtil) { 'use strict';
|
|
2
2
|
|
|
3
3
|
$$1 = $$1 && Object.prototype.hasOwnProperty.call($$1, 'default') ? $$1['default'] : $$1;
|
|
4
4
|
UrlParser = UrlParser && Object.prototype.hasOwnProperty.call(UrlParser, 'default') ? UrlParser['default'] : UrlParser;
|
|
@@ -8,6 +8,7 @@ define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support',
|
|
|
8
8
|
Helpers0 = Helpers0 && Object.prototype.hasOwnProperty.call(Helpers0, 'default') ? Helpers0['default'] : Helpers0;
|
|
9
9
|
reminderManagerFactory = reminderManagerFactory && Object.prototype.hasOwnProperty.call(reminderManagerFactory, 'default') ? reminderManagerFactory['default'] : reminderManagerFactory;
|
|
10
10
|
timeObserverFactory = timeObserverFactory && Object.prototype.hasOwnProperty.call(timeObserverFactory, 'default') ? timeObserverFactory['default'] : timeObserverFactory;
|
|
11
|
+
crossOriginUtil = crossOriginUtil && Object.prototype.hasOwnProperty.call(crossOriginUtil, 'default') ? crossOriginUtil['default'] : crossOriginUtil;
|
|
11
12
|
|
|
12
13
|
if (!Helpers0.__initialized) {
|
|
13
14
|
Helpers0(Handlebars);
|
|
@@ -16,16 +17,29 @@ define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support',
|
|
|
16
17
|
var Template = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
|
|
17
18
|
this.compilerInfo = [4,'>= 1.0.0'];
|
|
18
19
|
helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
|
|
19
|
-
var buffer = "", stack1, helper, options, functionType="function", escapeExpression=this.escapeExpression, helperMissing=helpers.helperMissing
|
|
20
|
+
var buffer = "", stack1, helper, options, self=this, functionType="function", escapeExpression=this.escapeExpression, helperMissing=helpers.helperMissing;
|
|
20
21
|
|
|
21
22
|
function program1(depth0,data) {
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
return "crossorigin";
|
|
25
|
+
return " crossorigin=\"use-credentials\"";
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
function program3(depth0,data) {
|
|
28
29
|
|
|
30
|
+
var stack1;
|
|
31
|
+
stack1 = helpers['if'].call(depth0, (depth0 && depth0.crossOrigin), {hash:{},inverse:self.noop,fn:self.program(4, program4, data),data:data});
|
|
32
|
+
if(stack1 || stack1 === 0) { return stack1; }
|
|
33
|
+
else { return ''; }
|
|
34
|
+
}
|
|
35
|
+
function program4(depth0,data) {
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
return " crossorigin";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function program6(depth0,data) {
|
|
42
|
+
|
|
29
43
|
var buffer = "", stack1, helper, options;
|
|
30
44
|
buffer += "\n <a href=\"";
|
|
31
45
|
if (helper = helpers.link) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
@@ -41,13 +55,13 @@ define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support',
|
|
|
41
55
|
if (helper = helpers.preload) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
42
56
|
else { helper = (depth0 && depth0.preload); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
|
|
43
57
|
buffer += escapeExpression(stack1)
|
|
44
|
-
+ "\" controls
|
|
45
|
-
stack1 = helpers['if'].call(depth0, (depth0 && depth0.
|
|
58
|
+
+ "\" controls";
|
|
59
|
+
stack1 = helpers['if'].call(depth0, (depth0 && depth0.crossOriginUseCredentials), {hash:{},inverse:self.program(3, program3, data),fn:self.program(1, program1, data),data:data});
|
|
46
60
|
if(stack1 || stack1 === 0) { buffer += stack1; }
|
|
47
61
|
buffer += ">\n "
|
|
48
62
|
+ escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Your browser doesn’t support the audio player.", options) : helperMissing.call(depth0, "__", "Your browser doesn’t support the audio player.", options)))
|
|
49
63
|
+ "\n";
|
|
50
|
-
stack1 = helpers['if'].call(depth0, (depth0 && depth0.link), {hash:{},inverse:self.noop,fn:self.program(
|
|
64
|
+
stack1 = helpers['if'].call(depth0, (depth0 && depth0.link), {hash:{},inverse:self.noop,fn:self.program(6, program6, data),data:data});
|
|
51
65
|
if(stack1 || stack1 === 0) { buffer += stack1; }
|
|
52
66
|
buffer += "\n</audio>\n";
|
|
53
67
|
return buffer;
|
|
@@ -64,16 +78,29 @@ define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support',
|
|
|
64
78
|
var Template$1 = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
|
|
65
79
|
this.compilerInfo = [4,'>= 1.0.0'];
|
|
66
80
|
helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
|
|
67
|
-
var buffer = "", stack1, helper, options, functionType="function", escapeExpression=this.escapeExpression, helperMissing=helpers.helperMissing
|
|
81
|
+
var buffer = "", stack1, helper, options, self=this, functionType="function", escapeExpression=this.escapeExpression, helperMissing=helpers.helperMissing;
|
|
68
82
|
|
|
69
83
|
function program1(depth0,data) {
|
|
70
84
|
|
|
71
85
|
|
|
72
|
-
return "crossorigin";
|
|
86
|
+
return " crossorigin=\"use-credentials\"";
|
|
73
87
|
}
|
|
74
88
|
|
|
75
89
|
function program3(depth0,data) {
|
|
76
90
|
|
|
91
|
+
var stack1;
|
|
92
|
+
stack1 = helpers['if'].call(depth0, (depth0 && depth0.crossOrigin), {hash:{},inverse:self.noop,fn:self.program(4, program4, data),data:data});
|
|
93
|
+
if(stack1 || stack1 === 0) { return stack1; }
|
|
94
|
+
else { return ''; }
|
|
95
|
+
}
|
|
96
|
+
function program4(depth0,data) {
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
return " crossorigin";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function program6(depth0,data) {
|
|
103
|
+
|
|
77
104
|
var buffer = "", stack1, helper, options;
|
|
78
105
|
buffer += "\n <a href=\"";
|
|
79
106
|
if (helper = helpers.link) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
@@ -93,13 +120,13 @@ define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support',
|
|
|
93
120
|
if (helper = helpers.poster) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
94
121
|
else { helper = (depth0 && depth0.poster); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
|
|
95
122
|
buffer += escapeExpression(stack1)
|
|
96
|
-
+ "\" controls
|
|
97
|
-
stack1 = helpers['if'].call(depth0, (depth0 && depth0.
|
|
123
|
+
+ "\" controls";
|
|
124
|
+
stack1 = helpers['if'].call(depth0, (depth0 && depth0.crossOriginUseCredentials), {hash:{},inverse:self.program(3, program3, data),fn:self.program(1, program1, data),data:data});
|
|
98
125
|
if(stack1 || stack1 === 0) { buffer += stack1; }
|
|
99
126
|
buffer += ">\n "
|
|
100
127
|
+ escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Your browser doesn’t support the video player.", options) : helperMissing.call(depth0, "__", "Your browser doesn’t support the video player.", options)))
|
|
101
128
|
+ "\n";
|
|
102
|
-
stack1 = helpers['if'].call(depth0, (depth0 && depth0.link), {hash:{},inverse:self.noop,fn:self.program(
|
|
129
|
+
stack1 = helpers['if'].call(depth0, (depth0 && depth0.link), {hash:{},inverse:self.noop,fn:self.program(6, program6, data),data:data});
|
|
103
130
|
if(stack1 || stack1 === 0) { buffer += stack1; }
|
|
104
131
|
buffer += "\n</video>\n";
|
|
105
132
|
return buffer;
|
|
@@ -148,9 +175,9 @@ define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support',
|
|
|
148
175
|
*
|
|
149
176
|
* You should have received a copy of the GNU General Public License
|
|
150
177
|
* along with this program; if not, write to the Free Software
|
|
151
|
-
* Foundation, Inc.,
|
|
178
|
+
* Foundation, Inc., 31 Milk St # 960789 Boston, MA 02196 USA.
|
|
152
179
|
*
|
|
153
|
-
* Copyright (c) 2015-
|
|
180
|
+
* Copyright (c) 2015-2026 (original work) Open Assessment Technologies SA ;
|
|
154
181
|
*/
|
|
155
182
|
|
|
156
183
|
/**
|
|
@@ -191,6 +218,7 @@ define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support',
|
|
|
191
218
|
* @param {string} [config.type] - The type of player (video or audio) (default: video)
|
|
192
219
|
* @param {boolean} [config.preview] - Enables the media preview (load media metadata)
|
|
193
220
|
* @param {boolean} [config.debug] - Enables the debug mode
|
|
221
|
+
* @param {'anonymous'|'use-credentials'} [config.crossOriginMode] - CORS credentials mode for cross-domain sources
|
|
194
222
|
* @param {number} [config.config.stalledDetectionDelay] - The delay before considering a media is stalled
|
|
195
223
|
* @returns {object} player
|
|
196
224
|
*/
|
|
@@ -253,16 +281,12 @@ define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support',
|
|
|
253
281
|
init() {
|
|
254
282
|
const tpl = 'audio' === type ? audioTpl : videoTpl;
|
|
255
283
|
const page = new UrlParser(window.location);
|
|
256
|
-
let cors = false;
|
|
257
284
|
let preload = config.preview ? 'metadata' : 'none';
|
|
258
285
|
let poster = '';
|
|
259
286
|
let link = '';
|
|
260
287
|
let result = false;
|
|
261
288
|
state = {};
|
|
262
289
|
sources.forEach(source => {
|
|
263
|
-
if (!page.sameDomain(source.src)) {
|
|
264
|
-
cors = true;
|
|
265
|
-
}
|
|
266
290
|
if (source.poster) {
|
|
267
291
|
poster = source.poster;
|
|
268
292
|
}
|
|
@@ -270,8 +294,10 @@ define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support',
|
|
|
270
294
|
link = source.link;
|
|
271
295
|
}
|
|
272
296
|
});
|
|
297
|
+
const crossOriginAttr = crossOriginUtil.resolveCrossOriginAttribute(sources, config.crossOriginMode, page);
|
|
273
298
|
$media = $$1(tpl({
|
|
274
|
-
|
|
299
|
+
crossOrigin: crossOriginAttr === 'anonymous',
|
|
300
|
+
crossOriginUseCredentials: crossOriginAttr === 'use-credentials',
|
|
275
301
|
preload,
|
|
276
302
|
poster,
|
|
277
303
|
link
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
define(function () { 'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This program is free software; you can redistribute it and/or
|
|
5
|
+
* modify it under the terms of the GNU General Public License
|
|
6
|
+
* as published by the Free Software Foundation; under version 2
|
|
7
|
+
* of the License (non-upgradable).
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License
|
|
15
|
+
* along with this program; if not, write to the Free Software
|
|
16
|
+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
17
|
+
*
|
|
18
|
+
* Copyright (c) 2026 (original work) Open Assessment Technologies SA ;
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** @typedef {'anonymous' | 'use-credentials'} CrossOriginMode */
|
|
22
|
+
|
|
23
|
+
const VALID_CROSS_ORIGIN_MODES = new Set(['anonymous', 'use-credentials']);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Normalises cross-origin mode from instance or RequireJS module config.
|
|
27
|
+
* @param {CrossOriginMode | string | undefined | null} instanceMode
|
|
28
|
+
* @param {CrossOriginMode | string | undefined | null} [moduleMode]
|
|
29
|
+
* @returns {CrossOriginMode}
|
|
30
|
+
*/
|
|
31
|
+
function resolveCrossOriginMode(instanceMode, moduleMode) {
|
|
32
|
+
let mode = instanceMode;
|
|
33
|
+
if (typeof mode === 'undefined' || mode === null) {
|
|
34
|
+
mode = moduleMode;
|
|
35
|
+
}
|
|
36
|
+
if (typeof mode === 'undefined' || mode === null) {
|
|
37
|
+
mode = 'anonymous';
|
|
38
|
+
}
|
|
39
|
+
return VALID_CROSS_ORIGIN_MODES.has(mode) ? /** @type {CrossOriginMode} */mode : 'anonymous';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Resolves the crossorigin attribute for HTML5 media when sources are cross-domain.
|
|
44
|
+
* @param {Array<{ src?: string }>} sources
|
|
45
|
+
* @param {CrossOriginMode} crossOriginMode
|
|
46
|
+
* @param {{ sameDomain: (url: string) => boolean }} page
|
|
47
|
+
* @returns {CrossOriginMode | null} null when attribute should be omitted
|
|
48
|
+
*/
|
|
49
|
+
function resolveCrossOriginAttribute(sources, crossOriginMode, page) {
|
|
50
|
+
const isCrossDomain = sources.some(source => source && source.src && !page.sameDomain(source.src));
|
|
51
|
+
if (!isCrossDomain) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return resolveCrossOriginMode(crossOriginMode);
|
|
55
|
+
}
|
|
56
|
+
var crossOrigin = {
|
|
57
|
+
VALID_CROSS_ORIGIN_MODES,
|
|
58
|
+
resolveCrossOriginMode,
|
|
59
|
+
resolveCrossOriginAttribute
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return crossOrigin;
|
|
63
|
+
|
|
64
|
+
});
|