@oat-sa/tao-core-ui 3.19.1 → 3.19.2
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/mediaplayer/players/html5.js +35 -4
- package/dist/rubyHtml.js +1633 -0
- package/package.json +1 -1
- package/src/mediaplayer/players/html5.js +45 -1
- package/src/rubyHtml.js +153 -0
package/package.json
CHANGED
|
@@ -115,6 +115,48 @@ export default function html5PlayerFactory($container, config = {}) {
|
|
|
115
115
|
const debug = (action, ...args) =>
|
|
116
116
|
(config.debug === true || config.debug === action) && window.console.log(getDebugContext(action), ...args);
|
|
117
117
|
|
|
118
|
+
const isVerticalLayout = () => {
|
|
119
|
+
const $layoutContext = $container.closest('.qti-interaction, .custom-text-box');
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
($('body').hasClass('item-writing-mode-vertical-rl') || $layoutContext.hasClass('writing-mode-vertical-rl')) &&
|
|
123
|
+
!$layoutContext.hasClass('writing-mode-horizontal-tb')
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const applyVideoSizing = () => {
|
|
128
|
+
if (type !== 'video' || !$media || !$media.length) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
$container.css({
|
|
133
|
+
display: 'flex',
|
|
134
|
+
alignItems: 'center',
|
|
135
|
+
justifyContent: 'center',
|
|
136
|
+
overflow: 'hidden'
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
$media.css({
|
|
140
|
+
width: '100%',
|
|
141
|
+
height: '100%',
|
|
142
|
+
maxWidth: '100%',
|
|
143
|
+
maxHeight: '100%',
|
|
144
|
+
margin: '0 auto',
|
|
145
|
+
objectFit: 'contain',
|
|
146
|
+
objectPosition: 'center center',
|
|
147
|
+
boxSizing: 'border-box'
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (isVerticalLayout()) {
|
|
151
|
+
$media.css({
|
|
152
|
+
width: 'auto',
|
|
153
|
+
height: '100%',
|
|
154
|
+
maxWidth: 'none',
|
|
155
|
+
maxHeight: '100%'
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
118
160
|
return eventifier({
|
|
119
161
|
init() {
|
|
120
162
|
const tpl = 'audio' === type ? audioTpl : videoTpl;
|
|
@@ -142,6 +184,8 @@ export default function html5PlayerFactory($container, config = {}) {
|
|
|
142
184
|
$media = $(tpl({ cors, preload, poster, link }));
|
|
143
185
|
$container.append($media);
|
|
144
186
|
|
|
187
|
+
applyVideoSizing();
|
|
188
|
+
|
|
145
189
|
media = $media.get(0);
|
|
146
190
|
result = !!(media && support.checkSupport(media));
|
|
147
191
|
|
|
@@ -378,7 +422,7 @@ export default function html5PlayerFactory($container, config = {}) {
|
|
|
378
422
|
$media.height($media.height());
|
|
379
423
|
$media.on('loadedmetadata.recover', () => {
|
|
380
424
|
$media.off('loadedmetadata.recover');
|
|
381
|
-
|
|
425
|
+
applyVideoSizing();
|
|
382
426
|
});
|
|
383
427
|
}
|
|
384
428
|
|
package/src/rubyHtml.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for TAO i18n ruby annotation markup in UI components.
|
|
3
|
+
*/
|
|
4
|
+
import $ from 'jquery';
|
|
5
|
+
import DOMPurify from 'dompurify';
|
|
6
|
+
|
|
7
|
+
const rubyTagPattern = /<ruby[\s>]|{ruby}/i;
|
|
8
|
+
|
|
9
|
+
const purifyConfig = {
|
|
10
|
+
ADD_TAGS: ['ruby', 'rb', 'rt']
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Whether a string contains ruby annotation markup.
|
|
15
|
+
* @param {String} str
|
|
16
|
+
* @returns {Boolean}
|
|
17
|
+
*/
|
|
18
|
+
export function containsRubyMarkup(str) {
|
|
19
|
+
return typeof str === 'string' && rubyTagPattern.test(str);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Convert TAO i18n ruby placeholders to HTML tags.
|
|
24
|
+
* @param {String} str
|
|
25
|
+
* @returns {String}
|
|
26
|
+
*/
|
|
27
|
+
export function normalizeRubyPlaceholders(str) {
|
|
28
|
+
if (typeof str !== 'string') {
|
|
29
|
+
return '';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return str
|
|
33
|
+
.replace(/\{ruby\}/g, '<ruby>')
|
|
34
|
+
.replace(/\{\/ruby\}/g, '</ruby>')
|
|
35
|
+
.replace(/\{rb\}/g, '<rb>')
|
|
36
|
+
.replace(/\{\/rb\}/g, '</rb>')
|
|
37
|
+
.replace(/\{rt\}/g, '<rt>')
|
|
38
|
+
.replace(/\{\/rt\}/g, '</rt>');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Sanitize ruby HTML with the same tag allowlist used in templates.
|
|
43
|
+
* @param {String} str
|
|
44
|
+
* @returns {String}
|
|
45
|
+
*/
|
|
46
|
+
export function sanitizeRubyHtml(str) {
|
|
47
|
+
if (typeof str !== 'string' || !str) {
|
|
48
|
+
return '';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return DOMPurify.sanitize(normalizeRubyPlaceholders(str), purifyConfig);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Strip ruby markup to plain text (for placeholders and option text).
|
|
56
|
+
* @param {String} str
|
|
57
|
+
* @returns {String}
|
|
58
|
+
*/
|
|
59
|
+
export function toPlainText(str) {
|
|
60
|
+
if (typeof str !== 'string' || !str) {
|
|
61
|
+
return '';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!containsRubyMarkup(str)) {
|
|
65
|
+
return str;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const template = document.createElement('template');
|
|
69
|
+
template.innerHTML = normalizeRubyPlaceholders(str);
|
|
70
|
+
template.content.querySelectorAll('rt').forEach(rt => rt.remove());
|
|
71
|
+
|
|
72
|
+
return template.content.textContent || '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {jQuery} $option
|
|
77
|
+
* @returns {jQuery|String|null}
|
|
78
|
+
*/
|
|
79
|
+
function formatRubyOption(option) {
|
|
80
|
+
if (!option.element) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const rubyHtml = $(option.element).data('ruby-html');
|
|
85
|
+
if (rubyHtml) {
|
|
86
|
+
return $('<span>').html(sanitizeRubyHtml(rubyHtml));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Prepare select options for select2 ruby rendering.
|
|
94
|
+
* @param {jQuery} $select
|
|
95
|
+
*/
|
|
96
|
+
export function prepareSelectOptions($select) {
|
|
97
|
+
$select.find('option').each(function prepareOption() {
|
|
98
|
+
const text = this.text;
|
|
99
|
+
|
|
100
|
+
if (!containsRubyMarkup(text)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const sanitized = sanitizeRubyHtml(text);
|
|
105
|
+
$(this).data('ruby-html', sanitized);
|
|
106
|
+
this.text = toPlainText(sanitized);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Strip ruby markup from select placeholder attributes.
|
|
112
|
+
* @param {jQuery} $select
|
|
113
|
+
*/
|
|
114
|
+
export function stripSelectPlaceholder($select) {
|
|
115
|
+
const placeholder = $select.attr('placeholder') || $select.data('placeholder');
|
|
116
|
+
|
|
117
|
+
if (placeholder && containsRubyMarkup(placeholder)) {
|
|
118
|
+
const plain = toPlainText(placeholder);
|
|
119
|
+
$select.attr('placeholder', plain);
|
|
120
|
+
$select.data('placeholder', plain);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Add select2 formatters for ruby option labels.
|
|
126
|
+
* @param {Object} [config]
|
|
127
|
+
* @returns {Object}
|
|
128
|
+
*/
|
|
129
|
+
export function enhanceSelect2Config(config) {
|
|
130
|
+
config = config || {};
|
|
131
|
+
const baseFormatResult = config.formatResult;
|
|
132
|
+
const baseFormatSelection = config.formatSelection;
|
|
133
|
+
|
|
134
|
+
config.formatResult = function formatResult(option, container, query) {
|
|
135
|
+
const ruby = formatRubyOption(option);
|
|
136
|
+
if (ruby !== null) {
|
|
137
|
+
return ruby;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return baseFormatResult ? baseFormatResult(option, container, query) : option.text;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
config.formatSelection = function formatSelection(option) {
|
|
144
|
+
const ruby = formatRubyOption(option);
|
|
145
|
+
if (ruby !== null) {
|
|
146
|
+
return ruby;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return baseFormatSelection ? baseFormatSelection(option) : option.text;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
return config;
|
|
153
|
+
}
|