@oat-sa/tao-core-ui 3.19.1 → 3.20.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/datatable.js CHANGED
@@ -228,7 +228,7 @@ define(['jquery', 'lodash', 'i18n', 'core/pluginifier', 'handlebars', 'lib/handl
228
228
  buffer += ">";
229
229
  if (helper = helpers.label) { stack1 = helper.call(depth0, {hash:{},data:data}); }
230
230
  else { helper = (depth0 && depth0.label); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
231
- buffer += escapeExpression(stack1);
231
+ if(stack1 || stack1 === 0) { buffer += stack1; }
232
232
  stack1 = helpers['if'].call(depth0, (depth0 && depth0.alias), {hash:{},inverse:self.noop,fn:self.program(51, program51, data),data:data});
233
233
  if(stack1 || stack1 === 0) { buffer += stack1; }
234
234
  stack1 = helpers['if'].call(depth0, (depth0 && depth0.comment), {hash:{},inverse:self.noop,fn:self.program(53, program53, data),data:data});
@@ -563,9 +563,10 @@ define(['jquery', 'lodash', 'i18n', 'core/pluginifier', 'handlebars', 'lib/handl
563
563
  buffer += ">\n ";
564
564
  stack1 = helpers['if'].call(depth0, (depth2 && depth2.icon), {hash:{},inverse:self.noop,fn:self.programWithDepth(75, program75, data, depth2),data:data});
565
565
  if(stack1 || stack1 === 0) { buffer += stack1; }
566
- buffer += "\n "
567
- + escapeExpression(((stack1 = (depth2 && depth2.label)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
568
- + "\n </button>\n ";
566
+ buffer += "\n ";
567
+ stack1 = ((stack1 = (depth2 && depth2.label)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1);
568
+ if(stack1 || stack1 === 0) { buffer += stack1; }
569
+ buffer += "\n </button>\n ";
569
570
  return buffer;
570
571
  }
571
572
 
package/dist/dialog.js CHANGED
@@ -118,8 +118,8 @@ define(['jquery', 'lodash', 'i18n', 'handlebars', 'lib/handlebars/helpers', 'ui/
118
118
  buffer += "\n <span class=\"label\">";
119
119
  if (helper = helpers.label) { stack1 = helper.call(depth0, {hash:{},data:data}); }
120
120
  else { helper = (depth0 && depth0.label); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
121
- buffer += escapeExpression(stack1)
122
- + "</span>\n </button>\n";
121
+ if(stack1 || stack1 === 0) { buffer += stack1; }
122
+ buffer += "</span>\n </button>\n";
123
123
  return buffer;
124
124
  }
125
125
  function program2(depth0,data) {
package/dist/feedback.js CHANGED
@@ -96,6 +96,32 @@ define(['jquery', 'lodash', 'core/format', 'ui/component', 'util/wrapLongWords',
96
96
  //change the display (absolute or in the flow)
97
97
  popup: true
98
98
  };
99
+ var feedbackFactory;
100
+ const RUBY_HTML = /<\s*(?:ruby|rt|rp|rb)\b/i;
101
+ const RUBY_BLOCK = /<ruby\b[^>]*>[\s\S]*?<\/ruby>/gi;
102
+
103
+ /**
104
+ * HTML-encodes a string while preserving ruby annotation blocks.
105
+ *
106
+ * @param {String} str
107
+ * @returns {String}
108
+ */
109
+ function encodeHtmlPreservingRuby(str) {
110
+ var parts = [];
111
+ var rubies = [];
112
+ var lastIndex = 0;
113
+ var match;
114
+ RUBY_BLOCK.lastIndex = 0;
115
+ while ((match = RUBY_BLOCK.exec(str)) !== null) {
116
+ parts.push(str.slice(lastIndex, match.index));
117
+ rubies.push(match[0]);
118
+ lastIndex = match.index + match[0].length;
119
+ }
120
+ parts.push(str.slice(lastIndex));
121
+ return parts.map(function (part, index) {
122
+ return encode.html(part) + (rubies[index] || '');
123
+ }).join('');
124
+ }
99
125
 
100
126
  /**
101
127
  * Creates a feedback object.
@@ -110,7 +136,7 @@ define(['jquery', 'lodash', 'core/format', 'ui/component', 'util/wrapLongWords',
110
136
  * @returns {feedback} the feedback object
111
137
  * @throws {TypeError} without a container
112
138
  */
113
- var feedbackFactory = function feedbackFactory($container, config) {
139
+ feedbackFactory = function ($container, config) {
114
140
  var feedback;
115
141
  const codeEnter = 13;
116
142
  const codeSpace = 32;
@@ -146,14 +172,15 @@ define(['jquery', 'lodash', 'core/format', 'ui/component', 'util/wrapLongWords',
146
172
  }
147
173
  this.config = _.defaults(options || {}, this.config);
148
174
  this.config.level = level;
175
+ const hasRubyHtml = typeof displayedMessage === 'string' && RUBY_HTML.test(displayedMessage);
149
176
 
150
177
  // encode plain text string to html
151
178
  if (this.config.encodeHtml) {
152
- displayedMessage = encode.html(displayedMessage);
179
+ displayedMessage = hasRubyHtml ? encodeHtmlPreservingRuby(displayedMessage) : encode.html(displayedMessage);
153
180
  }
154
181
 
155
182
  // wrap long words
156
- if (this.config.wrapLongWordsAfter) {
183
+ if (this.config.wrapLongWordsAfter && !hasRubyHtml) {
157
184
  displayedMessage = wrapLongWords(displayedMessage, this.config.wrapLongWordsAfter);
158
185
  }
159
186
 
@@ -285,7 +312,8 @@ define(['jquery', 'lodash', 'core/format', 'ui/component', 'util/wrapLongWords',
285
312
  _.pull(currents, this);
286
313
  }).init(config);
287
314
  };
315
+ var feedbackFactory$1 = feedbackFactory;
288
316
 
289
- return feedbackFactory;
317
+ return feedbackFactory$1;
290
318
 
291
319
  });
@@ -86,9 +86,9 @@ define(['jquery', 'lodash', 'i18n', 'ui/component', 'ui/feedback', 'util/url', '
86
86
  buffer += " aria-required=\"true\">\n ";
87
87
  stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.fieldMessages)),stack1 == null || stack1 === false ? stack1 : stack1.password), {hash:{},inverse:self.noop,fn:self.program(7, program7, data),data:data});
88
88
  if(stack1 || stack1 === 0) { buffer += stack1; }
89
- buffer += "\n </div>\n <div class=\"form-toolbar\"><input type=\"submit\" id=\"connect\" name=\"connect\" disabled=\"disabled\" class=\"disabled\" value=\""
89
+ buffer += "\n </div>\n <div class=\"form-toolbar\">\n <button type=\"submit\" id=\"connect\" name=\"connect\" disabled=\"disabled\" class=\"disabled\">"
90
90
  + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Log in", options) : helperMissing.call(depth0, "__", "Log in", options)))
91
- + "\"></div>\n </form>\n</div>\n";
91
+ + "</button>\n </div>\n </form>\n</div>\n";
92
92
  return buffer;
93
93
  });
94
94
  function loginTpl(data, options, asString) {
@@ -216,6 +216,39 @@ define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support',
216
216
  }
217
217
  return (config.debug === true || config.debug === action) && window.console.log(getDebugContext(action), ...args);
218
218
  };
219
+ const isVerticalLayout = () => {
220
+ const $layoutContext = $container.closest('.qti-interaction, .custom-text-box');
221
+ return ($$1('body').hasClass('item-writing-mode-vertical-rl') || $layoutContext.hasClass('writing-mode-vertical-rl')) && !$layoutContext.hasClass('writing-mode-horizontal-tb');
222
+ };
223
+ const applyVideoSizing = () => {
224
+ if (type !== 'video' || !$media || !$media.length) {
225
+ return;
226
+ }
227
+ $container.css({
228
+ display: 'flex',
229
+ alignItems: 'center',
230
+ justifyContent: 'center',
231
+ overflow: 'hidden'
232
+ });
233
+ $media.css({
234
+ width: '100%',
235
+ height: '100%',
236
+ maxWidth: '100%',
237
+ maxHeight: '100%',
238
+ margin: '0 auto',
239
+ objectFit: 'contain',
240
+ objectPosition: 'center center',
241
+ boxSizing: 'border-box'
242
+ });
243
+ if (isVerticalLayout()) {
244
+ $media.css({
245
+ width: 'auto',
246
+ height: '100%',
247
+ maxWidth: 'none',
248
+ maxHeight: '100%'
249
+ });
250
+ }
251
+ };
219
252
  return eventifier({
220
253
  init() {
221
254
  const tpl = 'audio' === type ? audioTpl : videoTpl;
@@ -244,6 +277,7 @@ define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support',
244
277
  link
245
278
  }));
246
279
  $container.append($media);
280
+ applyVideoSizing();
247
281
  media = $media.get(0);
248
282
  result = !!(media && support.checkSupport(media));
249
283
 
@@ -433,10 +467,7 @@ define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support',
433
467
  $media.height($media.height());
434
468
  $media.on('loadedmetadata.recover', () => {
435
469
  $media.off('loadedmetadata.recover');
436
- $media.css({
437
- width: '',
438
- height: ''
439
- });
470
+ applyVideoSizing();
440
471
  });
441
472
  }
442
473
  media.load();
@@ -124,8 +124,8 @@ define(['jquery', 'lodash', 'i18n', 'ui/component', 'ui/hider', 'ui/class/select
124
124
  + " ";
125
125
  if (helper = helpers.type) { stack1 = helper.call(depth0, {hash:{},data:data}); }
126
126
  else { helper = (depth0 && depth0.type); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
127
- buffer += escapeExpression(stack1)
128
- + " : </span><span class=\"selected-num\">0</span>\n </div>\n </footer>\n ";
127
+ if(stack1 || stack1 === 0) { buffer += stack1; }
128
+ buffer += " : </span><span class=\"selected-num\">0</span>\n </div>\n </footer>\n ";
129
129
  return buffer;
130
130
  }
131
131
 
@@ -32,8 +32,8 @@ define(['jquery', 'lodash', 'core/pluginifier', 'ui/resourcemgr/fileBrowser', 'u
32
32
  buffer += "\">\n\n <h2>";
33
33
  if (helper = helpers.title) { stack1 = helper.call(depth0, {hash:{},data:data}); }
34
34
  else { helper = (depth0 && depth0.title); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
35
- buffer += escapeExpression(stack1)
36
- + "</h2>\n\n <div class=\"file-wrapper\">\n\n <!-- left section: items selection -->\n <section class=\"file-browser\">\n <h1>"
35
+ if(stack1 || stack1 === 0) { buffer += stack1; }
36
+ buffer += "</h2>\n\n <div class=\"file-wrapper\">\n\n <!-- left section: items selection -->\n <section class=\"file-browser\">\n <h1>"
37
37
  + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Browse resources", options) : helperMissing.call(depth0, "__", "Browse resources", options)))
38
38
  + "</h1>\n <div class=\"file-browser-wrapper\"></div>\n </section>\n\n <!-- test editor -->\n <section class=\"file-selector\">\n\n <h1>\n <div class=\"title lft\"></div>\n <div class=\"upload-switcher rgt\">\n <a href=\"#\" class=\"btn-info small upload hidden\"><span class=\"icon-add\"></span>"
39
39
  + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Add file(s)", options) : helperMissing.call(depth0, "__", "Add file(s)", options)))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oat-sa/tao-core-ui",
3
- "version": "3.19.1",
3
+ "version": "3.20.0",
4
4
  "displayName": "TAO Core UI",
5
5
  "description": "UI libraries of TAO",
6
6
  "scripts": {
@@ -52,7 +52,7 @@
52
52
  "@oat-sa/prettier-config": "^0.1.1",
53
53
  "@oat-sa/rollup-plugin-wildcard-external": "^1.0.0",
54
54
  "@oat-sa/tao-calculator": "^0.6.2",
55
- "@oat-sa/tao-core-libs": "^1.0.0",
55
+ "@oat-sa/tao-core-libs": "^1.2.1",
56
56
  "@oat-sa/tao-core-sdk": "^3.0.0",
57
57
  "@oat-sa/tao-core-shared-libs": "^1.6.1",
58
58
  "@oat-sa/tao-qunit-testrunner": "^2.0.0",
@@ -64,7 +64,7 @@
64
64
  data-sort-by="{{#if sortId}}{{sortId}}{{else}}{{id}}{{/if}}"
65
65
  {{#if sorttype}}data-sort-type="{{sorttype}}"{{/if}}
66
66
  tabindex="0"
67
- {{/if}}>{{label}}{{#if alias}} <span class="alias">({{alias}})</span>{{/if}}{{#if comment}} <span class="comment">/ {{comment}}</span>{{/if}}</div>
67
+ {{/if}}>{{{label}}}{{#if alias}} <span class="alias">({{alias}})</span>{{/if}}{{#if comment}} <span class="comment">/ {{comment}}</span>{{/if}}</div>
68
68
  {{#if filterable}}
69
69
  <aside data-column="{{id}}" class="filter column
70
70
  {{#if customFilter}} customInput" >
@@ -132,7 +132,7 @@
132
132
  {{#if ../../title}} title="{{../../../title}}"{{/if}}
133
133
  {{#if ../../disabled}} disabled="disabled"{{/if}}>
134
134
  {{#if ../../icon}}<span class="icon-{{../../../icon}}"></span>{{/if}}
135
- {{../../label}}
135
+ {{{../../label}}}
136
136
  </button>
137
137
  {{/unless}}
138
138
  {{/with}}
@@ -1,6 +1,6 @@
1
1
  {{#each buttons}}
2
2
  <button class="btn-{{type}} small {{id}}" data-control="{{id}}" type="button">
3
3
  {{#if icon}}<span class="icon-{{icon}}"></span> {{/if}}
4
- <span class="label">{{label}}</span>
4
+ <span class="label">{{{label}}}</span>
5
5
  </button>
6
6
  {{/each}}
package/src/feedback.js CHANGED
@@ -70,6 +70,36 @@ var defaultOptions = {
70
70
  popup: true
71
71
  };
72
72
 
73
+ var feedbackFactory;
74
+
75
+ const RUBY_HTML = /<\s*(?:ruby|rt|rp|rb)\b/i;
76
+ const RUBY_BLOCK = /<ruby\b[^>]*>[\s\S]*?<\/ruby>/gi;
77
+
78
+ /**
79
+ * HTML-encodes a string while preserving ruby annotation blocks.
80
+ *
81
+ * @param {String} str
82
+ * @returns {String}
83
+ */
84
+ function encodeHtmlPreservingRuby(str) {
85
+ var parts = [];
86
+ var rubies = [];
87
+ var lastIndex = 0;
88
+ var match;
89
+
90
+ RUBY_BLOCK.lastIndex = 0;
91
+ while ((match = RUBY_BLOCK.exec(str)) !== null) {
92
+ parts.push(str.slice(lastIndex, match.index));
93
+ rubies.push(match[0]);
94
+ lastIndex = match.index + match[0].length;
95
+ }
96
+ parts.push(str.slice(lastIndex));
97
+
98
+ return parts.map(function(part, index) {
99
+ return encode.html(part) + (rubies[index] || '');
100
+ }).join('');
101
+ }
102
+
73
103
  /**
74
104
  * Creates a feedback object.
75
105
  *
@@ -83,7 +113,7 @@ var defaultOptions = {
83
113
  * @returns {feedback} the feedback object
84
114
  * @throws {TypeError} without a container
85
115
  */
86
- var feedbackFactory = function feedbackFactory($container, config) {
116
+ feedbackFactory = function ($container, config) {
87
117
  var feedback;
88
118
  const codeEnter = 13;
89
119
  const codeSpace = 32;
@@ -124,13 +154,18 @@ var feedbackFactory = function feedbackFactory($container, config) {
124
154
  this.config = _.defaults(options || {}, this.config);
125
155
  this.config.level = level;
126
156
 
157
+ const hasRubyHtml =
158
+ typeof displayedMessage === 'string' && RUBY_HTML.test(displayedMessage);
159
+
127
160
  // encode plain text string to html
128
161
  if (this.config.encodeHtml) {
129
- displayedMessage = encode.html(displayedMessage);
162
+ displayedMessage = hasRubyHtml
163
+ ? encodeHtmlPreservingRuby(displayedMessage)
164
+ : encode.html(displayedMessage);
130
165
  }
131
166
 
132
167
  // wrap long words
133
- if (this.config.wrapLongWordsAfter) {
168
+ if (this.config.wrapLongWordsAfter && !hasRubyHtml) {
134
169
  displayedMessage = wrapLongWords(displayedMessage, this.config.wrapLongWordsAfter);
135
170
  }
136
171
 
@@ -24,6 +24,8 @@
24
24
  </div>
25
25
  {{/if}}
26
26
  </div>
27
- <div class="form-toolbar"><input type="submit" id="connect" name="connect" disabled="disabled" class="disabled" value="{{__ "Log in"}}"></div>
27
+ <div class="form-toolbar">
28
+ <button type="submit" id="connect" name="connect" disabled="disabled" class="disabled">{{__ "Log in"}}</button>
29
+ </div>
28
30
  </form>
29
31
  </div>
@@ -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
- $media.css({ width: '', height: '' });
425
+ applyVideoSizing();
382
426
  });
383
427
  }
384
428
 
@@ -39,7 +39,7 @@
39
39
  {{#if multiple}}
40
40
  <footer>
41
41
  <div class="get-selection">
42
- <span>{{__ 'Selected'}} {{type}} : </span><span class="selected-num">0</span>
42
+ <span>{{__ 'Selected'}} {{{ type }}} : </span><span class="selected-num">0</span>
43
43
  </div>
44
44
  </footer>
45
45
  {{/if}}
@@ -1,6 +1,6 @@
1
1
  <div class="resourcemgr modal {{#if className}}{{className}}{{/if}}">
2
2
 
3
- <h2>{{title}}</h2>
3
+ <h2>{{{ title }}}</h2>
4
4
 
5
5
  <div class="file-wrapper">
6
6