@brightspace-ui/core 3.32.0 → 3.33.1
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/components/typography/styles.js +62 -0
- package/components/typography/typography.js +2 -60
- package/helpers/getLocalizeResources.js +1 -408
- package/helpers/mathjax.js +1 -26
- package/mixins/localize/localize-mixin.js +1 -1
- package/package.json +1 -3
- package/tools/mathjax-test-context.js +1 -2
- package/mixins/localize/localize.js +0 -255
@@ -318,3 +318,65 @@ export const blockquoteStyles = css`
|
|
318
318
|
}
|
319
319
|
}
|
320
320
|
`;
|
321
|
+
|
322
|
+
const importUrl = 'https://s.brightspace.com/lib/fonts/0.6.1/assets/';
|
323
|
+
const fonts = {
|
324
|
+
LatoRegular: 'Lato-400',
|
325
|
+
LatoBold: 'Lato-700',
|
326
|
+
BCSansLight: 'BCSans-Light',
|
327
|
+
BCSansRegular: 'BCSans-Regular',
|
328
|
+
BCSansBold: 'BCSans-Bold',
|
329
|
+
BCSansLightItalic: 'BCSans-LightItalic',
|
330
|
+
BCSansItalic: 'BCSans-Italic',
|
331
|
+
BCSansBoldItalic: 'BCSans-BoldItalic'
|
332
|
+
};
|
333
|
+
export const fontFacesCss = `
|
334
|
+
@font-face {
|
335
|
+
font-family: 'Lato';
|
336
|
+
font-style: normal;
|
337
|
+
font-weight: 400;
|
338
|
+
src: local('Lato Regular'), local('Lato-Regular'), url(${new URL(`${fonts.LatoRegular}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.LatoRegular}.woff`, importUrl)}) format('woff'), url(${new URL(`${fonts.LatoRegular}.ttf`, importUrl)}) format('truetype');
|
339
|
+
}
|
340
|
+
@font-face {
|
341
|
+
font-family: 'Lato';
|
342
|
+
font-style: normal;
|
343
|
+
font-weight: 700;
|
344
|
+
src: local('Lato Bold'), local('Lato-Bold'), url(${new URL(`${fonts.LatoBold}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.LatoBold}.woff`, importUrl)}) format('woff'), url(${new URL(`${fonts.LatoBold}.ttf`, importUrl)}) format('truetype');
|
345
|
+
}
|
346
|
+
@font-face {
|
347
|
+
font-family: 'BC Sans';
|
348
|
+
font-style: normal;
|
349
|
+
font-weight: 300;
|
350
|
+
src: url(${new URL(`${fonts.BCSansLight}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.BCSansLight}.woff`, importUrl)}) format('woff');
|
351
|
+
}
|
352
|
+
@font-face {
|
353
|
+
font-family: 'BC Sans';
|
354
|
+
font-style: normal;
|
355
|
+
font-weight: 400;
|
356
|
+
src: url(${new URL(`${fonts.BCSansRegular}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.BCSansRegular}.woff`, importUrl)}) format('woff');
|
357
|
+
}
|
358
|
+
@font-face {
|
359
|
+
font-family: 'BC Sans';
|
360
|
+
font-style: normal;
|
361
|
+
font-weight: 700;
|
362
|
+
src: url(${new URL(`${fonts.BCSansBold}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.BCSansBold}.woff`, importUrl)}) format('woff');
|
363
|
+
}
|
364
|
+
@font-face {
|
365
|
+
font-family: 'BC Sans';
|
366
|
+
font-style: italic;
|
367
|
+
font-weight: 300;
|
368
|
+
src: url(${new URL(`${fonts.BCSansLightItalic}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.BCSansLightItalic}.woff`, importUrl)}) format('woff');
|
369
|
+
}
|
370
|
+
@font-face {
|
371
|
+
font-family: 'BC Sans';
|
372
|
+
font-style: italic;
|
373
|
+
font-weight: 400;
|
374
|
+
src: url(${new URL(`${fonts.BCSansItalic}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.BCSansItalic}.woff`, importUrl)}) format('woff');
|
375
|
+
}
|
376
|
+
@font-face {
|
377
|
+
font-family: 'BC Sans';
|
378
|
+
font-style: italic;
|
379
|
+
font-weight: 700;
|
380
|
+
src: url(${new URL(`${fonts.BCSansBoldItalic}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.BCSansBoldItalic}.woff`, importUrl)}) format('woff');
|
381
|
+
}
|
382
|
+
`;
|
@@ -1,16 +1,5 @@
|
|
1
1
|
import '../colors/colors.js';
|
2
|
-
|
3
|
-
export const importUrl = 'https://s.brightspace.com/lib/fonts/0.6.1/assets/';
|
4
|
-
export const fonts = {
|
5
|
-
LatoRegular: 'Lato-400',
|
6
|
-
LatoBold: 'Lato-700',
|
7
|
-
BCSansLight: 'BCSans-Light',
|
8
|
-
BCSansRegular: 'BCSans-Regular',
|
9
|
-
BCSansBold: 'BCSans-Bold',
|
10
|
-
BCSansLightItalic: 'BCSans-LightItalic',
|
11
|
-
BCSansItalic: 'BCSans-Italic',
|
12
|
-
BCSansBoldItalic: 'BCSans-BoldItalic'
|
13
|
-
};
|
2
|
+
import { fontFacesCss } from './styles.js';
|
14
3
|
|
15
4
|
if (!document.head.querySelector('#d2l-typography-font-face')) {
|
16
5
|
const style = document.createElement('style');
|
@@ -24,54 +13,7 @@ if (!document.head.querySelector('#d2l-typography-font-face')) {
|
|
24
13
|
--d2l-document-direction: rtl;
|
25
14
|
}
|
26
15
|
|
27
|
-
|
28
|
-
font-family: 'Lato';
|
29
|
-
font-style: normal;
|
30
|
-
font-weight: 400;
|
31
|
-
src: local('Lato Regular'), local('Lato-Regular'), url(${new URL(`${fonts.LatoRegular}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.LatoRegular}.woff`, importUrl)}) format('woff'), url(${new URL(`${fonts.LatoRegular}.ttf`, importUrl)}) format('truetype');
|
32
|
-
}
|
33
|
-
@font-face {
|
34
|
-
font-family: 'Lato';
|
35
|
-
font-style: normal;
|
36
|
-
font-weight: 700;
|
37
|
-
src: local('Lato Bold'), local('Lato-Bold'), url(${new URL(`${fonts.LatoBold}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.LatoBold}.woff`, importUrl)}) format('woff'), url(${new URL(`${fonts.LatoBold}.ttf`, importUrl)}) format('truetype');
|
38
|
-
}
|
39
|
-
@font-face {
|
40
|
-
font-family: 'BC Sans';
|
41
|
-
font-style: normal;
|
42
|
-
font-weight: 300;
|
43
|
-
src: url(${new URL(`${fonts.BCSansLight}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.BCSansLight}.woff`, importUrl)}) format('woff');
|
44
|
-
}
|
45
|
-
@font-face {
|
46
|
-
font-family: 'BC Sans';
|
47
|
-
font-style: normal;
|
48
|
-
font-weight: 400;
|
49
|
-
src: url(${new URL(`${fonts.BCSansRegular}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.BCSansRegular}.woff`, importUrl)}) format('woff');
|
50
|
-
}
|
51
|
-
@font-face {
|
52
|
-
font-family: 'BC Sans';
|
53
|
-
font-style: normal;
|
54
|
-
font-weight: 700;
|
55
|
-
src: url(${new URL(`${fonts.BCSansBold}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.BCSansBold}.woff`, importUrl)}) format('woff');
|
56
|
-
}
|
57
|
-
@font-face {
|
58
|
-
font-family: 'BC Sans';
|
59
|
-
font-style: italic;
|
60
|
-
font-weight: 300;
|
61
|
-
src: url(${new URL(`${fonts.BCSansLightItalic}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.BCSansLightItalic}.woff`, importUrl)}) format('woff');
|
62
|
-
}
|
63
|
-
@font-face {
|
64
|
-
font-family: 'BC Sans';
|
65
|
-
font-style: italic;
|
66
|
-
font-weight: 400;
|
67
|
-
src: url(${new URL(`${fonts.BCSansItalic}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.BCSansItalic}.woff`, importUrl)}) format('woff');
|
68
|
-
}
|
69
|
-
@font-face {
|
70
|
-
font-family: 'BC Sans';
|
71
|
-
font-style: italic;
|
72
|
-
font-weight: 700;
|
73
|
-
src: url(${new URL(`${fonts.BCSansBoldItalic}.woff2`, importUrl)}) format('woff2'), url(${new URL(`${fonts.BCSansBoldItalic}.woff`, importUrl)}) format('woff');
|
74
|
-
}
|
16
|
+
${fontFacesCss}
|
75
17
|
|
76
18
|
.d2l-typography {
|
77
19
|
color: var(--d2l-color-ferrite);
|
@@ -1,408 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
const CacheName = 'd2l-oslo';
|
4
|
-
const ContentTypeHeader = 'Content-Type';
|
5
|
-
const ContentTypeJson = 'application/json';
|
6
|
-
const DebounceTime = 150;
|
7
|
-
const ETagHeader = 'ETag';
|
8
|
-
const StateFetching = 2;
|
9
|
-
const StateIdle = 1;
|
10
|
-
|
11
|
-
const BatchFailedReason = new Error('Failed to fetch batch overrides.');
|
12
|
-
const SingleFailedReason = new Error('Failed to fetch overrides.');
|
13
|
-
|
14
|
-
const blobs = new Map();
|
15
|
-
|
16
|
-
let cache = undefined;
|
17
|
-
let cachePromise = undefined;
|
18
|
-
let documentLocaleSettings = undefined;
|
19
|
-
let queue = [];
|
20
|
-
let state = StateIdle;
|
21
|
-
let timer = 0;
|
22
|
-
let debug = false;
|
23
|
-
|
24
|
-
async function publish(request, response) {
|
25
|
-
|
26
|
-
if (response.ok) {
|
27
|
-
const overridesJson = await response.json();
|
28
|
-
request.resolve(overridesJson);
|
29
|
-
} else {
|
30
|
-
request.reject(SingleFailedReason);
|
31
|
-
}
|
32
|
-
}
|
33
|
-
|
34
|
-
async function flushQueue() {
|
35
|
-
|
36
|
-
timer = 0;
|
37
|
-
state = StateFetching;
|
38
|
-
|
39
|
-
if (queue.length <= 0) {
|
40
|
-
state = StateIdle;
|
41
|
-
return;
|
42
|
-
}
|
43
|
-
|
44
|
-
const requests = queue;
|
45
|
-
|
46
|
-
queue = [];
|
47
|
-
|
48
|
-
const resources = requests.map(item => item.resource);
|
49
|
-
const bodyObject = { resources };
|
50
|
-
const bodyText = JSON.stringify(bodyObject);
|
51
|
-
|
52
|
-
const res = await fetch(documentLocaleSettings.oslo.batch, {
|
53
|
-
method: 'POST',
|
54
|
-
body: bodyText,
|
55
|
-
headers: { [ContentTypeHeader]: ContentTypeJson }
|
56
|
-
});
|
57
|
-
|
58
|
-
if (res.ok) {
|
59
|
-
|
60
|
-
const responses = (await res.json()).resources;
|
61
|
-
|
62
|
-
const tasks = [];
|
63
|
-
|
64
|
-
for (let i = 0; i < responses.length; ++i) {
|
65
|
-
|
66
|
-
const response = responses[i];
|
67
|
-
const request = requests[i];
|
68
|
-
|
69
|
-
const responseValue = new Response(response.body, {
|
70
|
-
status: response.status,
|
71
|
-
headers: response.headers
|
72
|
-
});
|
73
|
-
|
74
|
-
// New version might be available since the page loaded, so make a
|
75
|
-
// record of it.
|
76
|
-
|
77
|
-
const nextVersion = responseValue.headers.get(ETagHeader);
|
78
|
-
if (nextVersion) {
|
79
|
-
setVersion(nextVersion);
|
80
|
-
}
|
81
|
-
|
82
|
-
const cacheKey = new Request(formatCacheKey(request.resource));
|
83
|
-
const cacheValue = responseValue.clone();
|
84
|
-
|
85
|
-
if (cache === undefined) {
|
86
|
-
if (cachePromise === undefined) {
|
87
|
-
cachePromise = caches.open(CacheName);
|
88
|
-
}
|
89
|
-
cache = await cachePromise;
|
90
|
-
}
|
91
|
-
|
92
|
-
debug && console.log(`[Oslo] cache prime: ${request.resource}`);
|
93
|
-
tasks.push(cache.put(cacheKey, cacheValue));
|
94
|
-
tasks.push(publish(request, responseValue));
|
95
|
-
}
|
96
|
-
|
97
|
-
await Promise.all(tasks);
|
98
|
-
|
99
|
-
} else {
|
100
|
-
|
101
|
-
for (const request of requests) {
|
102
|
-
|
103
|
-
request.reject(BatchFailedReason);
|
104
|
-
}
|
105
|
-
}
|
106
|
-
|
107
|
-
if (queue.length > 0) {
|
108
|
-
setTimeout(flushQueue, 0);
|
109
|
-
} else {
|
110
|
-
state = StateIdle;
|
111
|
-
}
|
112
|
-
}
|
113
|
-
|
114
|
-
function debounceQueue() {
|
115
|
-
|
116
|
-
if (state !== StateIdle) {
|
117
|
-
return;
|
118
|
-
}
|
119
|
-
|
120
|
-
if (timer > 0) {
|
121
|
-
clearTimeout(timer);
|
122
|
-
}
|
123
|
-
|
124
|
-
timer = setTimeout(flushQueue, DebounceTime);
|
125
|
-
}
|
126
|
-
|
127
|
-
async function fetchCollection(url) {
|
128
|
-
|
129
|
-
if (blobs.has(url)) {
|
130
|
-
return Promise.resolve(blobs.get(url));
|
131
|
-
}
|
132
|
-
|
133
|
-
const res = await fetch(url, { method: 'GET' });
|
134
|
-
|
135
|
-
if (res.ok) {
|
136
|
-
const resJson = await res.json();
|
137
|
-
blobs.set(url, resJson);
|
138
|
-
return Promise.resolve(resJson);
|
139
|
-
} else {
|
140
|
-
return Promise.reject(SingleFailedReason);
|
141
|
-
}
|
142
|
-
}
|
143
|
-
|
144
|
-
function fetchWithQueuing(resource) {
|
145
|
-
|
146
|
-
const promise = new Promise((resolve, reject) => {
|
147
|
-
|
148
|
-
queue.push({ resource, resolve, reject });
|
149
|
-
});
|
150
|
-
|
151
|
-
debounceQueue();
|
152
|
-
|
153
|
-
return promise;
|
154
|
-
}
|
155
|
-
|
156
|
-
function formatCacheKey(resource) {
|
157
|
-
|
158
|
-
return formatOsloRequest(documentLocaleSettings.oslo.collection, resource);
|
159
|
-
}
|
160
|
-
|
161
|
-
async function fetchWithCaching(resource) {
|
162
|
-
|
163
|
-
if (cache === undefined) {
|
164
|
-
if (cachePromise === undefined) {
|
165
|
-
cachePromise = caches.open(CacheName);
|
166
|
-
}
|
167
|
-
cache = await cachePromise;
|
168
|
-
}
|
169
|
-
|
170
|
-
const cacheKey = new Request(formatCacheKey(resource));
|
171
|
-
const cacheValue = await cache.match(cacheKey);
|
172
|
-
if (cacheValue === undefined) {
|
173
|
-
debug && console.log(`[Oslo] cache miss: ${resource}`);
|
174
|
-
return fetchWithQueuing(resource);
|
175
|
-
}
|
176
|
-
|
177
|
-
debug && console.log(`[Oslo] cache hit: ${resource}`);
|
178
|
-
if (!cacheValue.ok) {
|
179
|
-
fetchWithQueuing(resource).then(url => URL.revokeObjectURL(url));
|
180
|
-
throw SingleFailedReason;
|
181
|
-
}
|
182
|
-
|
183
|
-
// Check if the cache response is stale based on either the document init or
|
184
|
-
// any requests we've made to the LMS since init. We'll still serve stale
|
185
|
-
// from cache for this page, but we'll update it in the background for the
|
186
|
-
// next page.
|
187
|
-
|
188
|
-
// We rely on the ETag header to identify if the cache needs to be updated.
|
189
|
-
// The LMS will provide it in the format: [release].[build].[langModifiedVersion]
|
190
|
-
// So for example, an ETag in the 20.20.10 release could be: 20.20.10.24605.55520
|
191
|
-
|
192
|
-
const currentVersion = getVersion();
|
193
|
-
if (currentVersion) {
|
194
|
-
|
195
|
-
const previousVersion = cacheValue.headers.get(ETagHeader);
|
196
|
-
if (previousVersion !== currentVersion) {
|
197
|
-
|
198
|
-
debug && console.log(`[Oslo] cache stale: ${resource}`);
|
199
|
-
fetchWithQueuing(resource).then(url => URL.revokeObjectURL(url));
|
200
|
-
}
|
201
|
-
}
|
202
|
-
|
203
|
-
return await cacheValue.json();
|
204
|
-
}
|
205
|
-
|
206
|
-
function fetchWithPooling(resource) {
|
207
|
-
|
208
|
-
// At most one request per resource.
|
209
|
-
|
210
|
-
let promise = blobs.get(resource);
|
211
|
-
if (promise === undefined) {
|
212
|
-
promise = fetchWithCaching(resource);
|
213
|
-
blobs.set(resource, promise);
|
214
|
-
}
|
215
|
-
return promise;
|
216
|
-
}
|
217
|
-
|
218
|
-
async function shouldUseBatchFetch() {
|
219
|
-
|
220
|
-
if (documentLocaleSettings === undefined) {
|
221
|
-
documentLocaleSettings = getDocumentLocaleSettings();
|
222
|
-
}
|
223
|
-
|
224
|
-
if (!documentLocaleSettings.oslo) {
|
225
|
-
return false;
|
226
|
-
}
|
227
|
-
|
228
|
-
try {
|
229
|
-
|
230
|
-
// try opening CacheStorage, if the session is in a private browser in firefox this throws an exception
|
231
|
-
await caches.open(CacheName);
|
232
|
-
|
233
|
-
// Only batch if we can do client-side caching, otherwise it's worse on each
|
234
|
-
// subsequent page navigation.
|
235
|
-
|
236
|
-
return Boolean(documentLocaleSettings.oslo.batch) && 'CacheStorage' in window;
|
237
|
-
} catch (err) {
|
238
|
-
return false;
|
239
|
-
}
|
240
|
-
|
241
|
-
}
|
242
|
-
|
243
|
-
function shouldUseCollectionFetch() {
|
244
|
-
|
245
|
-
if (documentLocaleSettings === undefined) {
|
246
|
-
documentLocaleSettings = getDocumentLocaleSettings();
|
247
|
-
}
|
248
|
-
|
249
|
-
if (!documentLocaleSettings.oslo) {
|
250
|
-
return false;
|
251
|
-
}
|
252
|
-
|
253
|
-
return Boolean(documentLocaleSettings.oslo.collection);
|
254
|
-
}
|
255
|
-
|
256
|
-
function setVersion(version) {
|
257
|
-
|
258
|
-
if (documentLocaleSettings === undefined) {
|
259
|
-
documentLocaleSettings = getDocumentLocaleSettings();
|
260
|
-
}
|
261
|
-
|
262
|
-
if (!documentLocaleSettings.oslo) {
|
263
|
-
return;
|
264
|
-
}
|
265
|
-
|
266
|
-
documentLocaleSettings.oslo.version = version;
|
267
|
-
}
|
268
|
-
|
269
|
-
function getVersion() {
|
270
|
-
|
271
|
-
if (documentLocaleSettings === undefined) {
|
272
|
-
documentLocaleSettings = getDocumentLocaleSettings();
|
273
|
-
}
|
274
|
-
|
275
|
-
const shouldReturnVersion =
|
276
|
-
documentLocaleSettings.oslo &&
|
277
|
-
documentLocaleSettings.oslo.version;
|
278
|
-
if (!shouldReturnVersion) {
|
279
|
-
return null;
|
280
|
-
}
|
281
|
-
|
282
|
-
return documentLocaleSettings.oslo.version;
|
283
|
-
}
|
284
|
-
|
285
|
-
async function shouldFetchOverrides() {
|
286
|
-
|
287
|
-
const isOsloAvailable =
|
288
|
-
await shouldUseBatchFetch() ||
|
289
|
-
shouldUseCollectionFetch();
|
290
|
-
|
291
|
-
return isOsloAvailable;
|
292
|
-
}
|
293
|
-
|
294
|
-
async function fetchOverride(formatFunc) {
|
295
|
-
|
296
|
-
let resource, res, requestURL;
|
297
|
-
|
298
|
-
if (await shouldUseBatchFetch()) {
|
299
|
-
|
300
|
-
// If batching is available, pool requests together.
|
301
|
-
|
302
|
-
resource = formatFunc();
|
303
|
-
res = fetchWithPooling(resource);
|
304
|
-
|
305
|
-
} else /* shouldUseCollectionFetch() == true */ {
|
306
|
-
|
307
|
-
// Otherwise, fetch it directly and let the LMS manage the cache.
|
308
|
-
|
309
|
-
resource = formatFunc();
|
310
|
-
requestURL = formatOsloRequest(documentLocaleSettings.oslo.collection, resource);
|
311
|
-
|
312
|
-
res = fetchCollection(requestURL);
|
313
|
-
|
314
|
-
}
|
315
|
-
res = res.catch(coalesceToNull);
|
316
|
-
return res;
|
317
|
-
}
|
318
|
-
|
319
|
-
function coalesceToNull() {
|
320
|
-
|
321
|
-
return null;
|
322
|
-
}
|
323
|
-
|
324
|
-
function formatOsloRequest(baseUrl, resource) {
|
325
|
-
return `${baseUrl}/${resource}`;
|
326
|
-
}
|
327
|
-
|
328
|
-
export function __clearWindowCache() {
|
329
|
-
|
330
|
-
// Used to reset state for tests.
|
331
|
-
|
332
|
-
blobs.clear();
|
333
|
-
cache = undefined;
|
334
|
-
cachePromise = undefined;
|
335
|
-
}
|
336
|
-
|
337
|
-
export function __enableDebugging() {
|
338
|
-
|
339
|
-
// Used to enable debug logging during development.
|
340
|
-
|
341
|
-
debug = true;
|
342
|
-
}
|
343
|
-
|
344
|
-
export async function getLocalizeOverrideResources(
|
345
|
-
langCode,
|
346
|
-
translations,
|
347
|
-
formatFunc
|
348
|
-
) {
|
349
|
-
const promises = [];
|
350
|
-
|
351
|
-
promises.push(translations);
|
352
|
-
|
353
|
-
if (await shouldFetchOverrides()) {
|
354
|
-
const overrides = await fetchOverride(formatFunc);
|
355
|
-
promises.push(overrides);
|
356
|
-
}
|
357
|
-
|
358
|
-
const results = await Promise.all(promises);
|
359
|
-
|
360
|
-
return {
|
361
|
-
language: langCode,
|
362
|
-
resources: Object.assign({}, ...results)
|
363
|
-
};
|
364
|
-
}
|
365
|
-
|
366
|
-
export async function getLocalizeResources(
|
367
|
-
possibleLanguages,
|
368
|
-
filterFunc,
|
369
|
-
formatFunc,
|
370
|
-
fetchFunc
|
371
|
-
) {
|
372
|
-
|
373
|
-
const promises = [];
|
374
|
-
let supportedLanguage;
|
375
|
-
|
376
|
-
if (await shouldFetchOverrides()) {
|
377
|
-
|
378
|
-
const overrides = await fetchOverride(formatFunc, fetchFunc);
|
379
|
-
promises.push(overrides);
|
380
|
-
}
|
381
|
-
|
382
|
-
for (const language of possibleLanguages) {
|
383
|
-
|
384
|
-
if (filterFunc(language)) {
|
385
|
-
|
386
|
-
if (supportedLanguage === undefined) {
|
387
|
-
supportedLanguage = language;
|
388
|
-
}
|
389
|
-
|
390
|
-
const translations = fetchFunc(formatFunc(language));
|
391
|
-
promises.push(translations);
|
392
|
-
|
393
|
-
break;
|
394
|
-
}
|
395
|
-
}
|
396
|
-
|
397
|
-
const results = await Promise.all(promises);
|
398
|
-
|
399
|
-
// We're fetching in best -> worst, so we'll assign worst -> best, so the
|
400
|
-
// best overwrite everything else.
|
401
|
-
|
402
|
-
results.reverse();
|
403
|
-
|
404
|
-
return {
|
405
|
-
language: supportedLanguage,
|
406
|
-
resources: Object.assign({}, ...results)
|
407
|
-
};
|
408
|
-
}
|
1
|
+
export { getLocalizeOverrideResources, getLocalizeResources } from '@brightspace-ui/intl/helpers/getLocalizeResources.js';
|
package/helpers/mathjax.js
CHANGED
@@ -45,7 +45,6 @@ class HtmlBlockMathRenderer {
|
|
45
45
|
|
46
46
|
const mathJaxConfig = {
|
47
47
|
deferTypeset: true,
|
48
|
-
enableMML3Support: context.enableMML3Support,
|
49
48
|
renderLatex: context.renderLatex,
|
50
49
|
outputScale: context.outputScale || 1,
|
51
50
|
window: window
|
@@ -62,24 +61,6 @@ class HtmlBlockMathRenderer {
|
|
62
61
|
elm.style.height = '0.5rem';
|
63
62
|
});
|
64
63
|
|
65
|
-
if (context.enableMML3Support) {
|
66
|
-
// There's a bug in the experimental MML3 plugin that causes mi and mo elements containing non-breaking
|
67
|
-
// spaces to break MathJax's math processing (e.g. <mo> </mo>, <mi>Some Identifier</mi>).
|
68
|
-
// Unfortunately, WIRIS tends to add a lot of these in chemistry equations, so they break math processing.
|
69
|
-
//
|
70
|
-
// In order to address this, we can just remove any non-breaking spaces entirely, replacing them with
|
71
|
-
// empty strings. MathJax will ignore any empty elements as a result, and while this may mean intended
|
72
|
-
// whitespace is occasionally removed, it's necessary for MathJax to render anything at all.
|
73
|
-
//
|
74
|
-
// NOTE: MathJax evidently has a fix for this in MathJax 4, so we should consider trying to remove this when
|
75
|
-
// the update comes out of beta and we decide to take it on.
|
76
|
-
//
|
77
|
-
// See https://github.com/mathjax/MathJax/issues/3030 for some related discussion.
|
78
|
-
elem.querySelectorAll('mo, mi').forEach(elm => {
|
79
|
-
elm.innerHTML = elm.innerHTML.replace(/ /g, '');
|
80
|
-
});
|
81
|
-
}
|
82
|
-
|
83
64
|
// If we're using deferred rendering, we need to create a document structure
|
84
65
|
// within the element so MathJax can appropriately process math.
|
85
66
|
if (!options.noDeferredRendering) elem.innerHTML = `<mjx-doc><mjx-head></mjx-head><mjx-body>${elem.innerHTML}</mjx-body></mjx-doc>`;
|
@@ -120,13 +101,7 @@ export function loadMathJax(mathJaxConfig) {
|
|
120
101
|
}
|
121
102
|
},
|
122
103
|
loader: {
|
123
|
-
load:
|
124
|
-
? [
|
125
|
-
'[mml]/mml3',
|
126
|
-
'ui/menu'
|
127
|
-
] : [
|
128
|
-
'ui/menu'
|
129
|
-
]
|
104
|
+
load: ['ui/menu']
|
130
105
|
},
|
131
106
|
startup: {
|
132
107
|
ready: () => {
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { disallowedTagsRegex, getLocalizeClass, validateMarkup } from '
|
1
|
+
import { disallowedTagsRegex, getLocalizeClass, validateMarkup } from '@brightspace-ui/intl/lib/localize.js';
|
2
2
|
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
3
3
|
import { html } from 'lit';
|
4
4
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@brightspace-ui/core",
|
3
|
-
"version": "3.
|
3
|
+
"version": "3.33.1",
|
4
4
|
"description": "A collection of accessible, free, open-source web components for building Brightspace applications",
|
5
5
|
"type": "module",
|
6
6
|
"repository": "https://github.com/BrightspaceUI/core.git",
|
@@ -67,10 +67,8 @@
|
|
67
67
|
"dependencies": {
|
68
68
|
"@brightspace-ui/intl": "^3",
|
69
69
|
"@brightspace-ui/lms-context-provider": "^1",
|
70
|
-
"@formatjs/intl-pluralrules": "^1",
|
71
70
|
"@open-wc/dedupe-mixin": "^1",
|
72
71
|
"ifrau": "^0.41",
|
73
|
-
"intl-messageformat": "^10",
|
74
72
|
"lit": "^3",
|
75
73
|
"prismjs": "^1",
|
76
74
|
"resize-observer-polyfill": "^1"
|
@@ -4,6 +4,5 @@ console.warn('Using mathjax test context, this is intended for demo pages and te
|
|
4
4
|
|
5
5
|
addContext('d2l-mathjax', {
|
6
6
|
outputScale: 1.1,
|
7
|
-
renderLatex: !(window.location.search.indexOf('latex=false') !== -1)
|
8
|
-
enableMML3Support: true
|
7
|
+
renderLatex: !(window.location.search.indexOf('latex=false') !== -1)
|
9
8
|
});
|
@@ -1,255 +0,0 @@
|
|
1
|
-
import '@formatjs/intl-pluralrules/dist-es6/polyfill-locales.js';
|
2
|
-
import { defaultLocale as fallbackLang, getDocumentLocaleSettings, supportedLangpacks } from '@brightspace-ui/intl/lib/common.js';
|
3
|
-
import { getLocalizeOverrideResources } from '../../helpers/getLocalizeResources.js';
|
4
|
-
import IntlMessageFormat from 'intl-messageformat';
|
5
|
-
|
6
|
-
export const allowedTags = Object.freeze(['d2l-link', 'd2l-tooltip-help', 'p', 'br', 'b', 'strong', 'i', 'em', 'button']);
|
7
|
-
|
8
|
-
const getDisallowedTagsRegex = allowedTags => {
|
9
|
-
const validTerminators = '([>\\s/]|$)';
|
10
|
-
const allowedAfterTriangleBracket = `/?(${allowedTags.join('|')})?${validTerminators}`;
|
11
|
-
return new RegExp(`<(?!${allowedAfterTriangleBracket})`);
|
12
|
-
};
|
13
|
-
|
14
|
-
export const disallowedTagsRegex = getDisallowedTagsRegex(allowedTags);
|
15
|
-
const noAllowedTagsRegex = getDisallowedTagsRegex([]);
|
16
|
-
|
17
|
-
export const getLocalizeClass = (superclass = class {}) => class LocalizeClass extends superclass {
|
18
|
-
|
19
|
-
static documentLocaleSettings = getDocumentLocaleSettings();
|
20
|
-
static #localizeMarkup;
|
21
|
-
|
22
|
-
static setLocalizeMarkup(localizeMarkup) {
|
23
|
-
this.#localizeMarkup ??= localizeMarkup;
|
24
|
-
}
|
25
|
-
|
26
|
-
pristine = true;
|
27
|
-
#connected = false;
|
28
|
-
#localeChangeCallback;
|
29
|
-
#resourcesPromise;
|
30
|
-
#resolveResourcesLoaded;
|
31
|
-
|
32
|
-
async #localeChangeHandler() {
|
33
|
-
if (!this._hasResources()) return;
|
34
|
-
|
35
|
-
const resourcesPromise = this.constructor._getAllLocalizeResources(this.config);
|
36
|
-
this.#resourcesPromise = resourcesPromise;
|
37
|
-
const localizeResources = (await resourcesPromise).flat(Infinity);
|
38
|
-
// If the locale changed while resources were being fetched, abort
|
39
|
-
if (this.#resourcesPromise !== resourcesPromise) return;
|
40
|
-
|
41
|
-
const allResources = {};
|
42
|
-
const resolvedLocales = new Set();
|
43
|
-
for (const { language, resources } of localizeResources) {
|
44
|
-
for (const [key, value] of Object.entries(resources)) {
|
45
|
-
allResources[key] = { language, value };
|
46
|
-
resolvedLocales.add(language);
|
47
|
-
}
|
48
|
-
}
|
49
|
-
this.localize.resources = allResources;
|
50
|
-
this.localize.resolvedLocale = [...resolvedLocales][0];
|
51
|
-
if (resolvedLocales.size > 1) {
|
52
|
-
console.warn(`Resolved multiple locales in '${this.constructor.name || this.tagName || ''}': ${[...resolvedLocales].join(', ')}`);
|
53
|
-
}
|
54
|
-
|
55
|
-
if (this.pristine) {
|
56
|
-
this.pristine = false;
|
57
|
-
this.#resolveResourcesLoaded();
|
58
|
-
}
|
59
|
-
|
60
|
-
this.#onResourcesChange();
|
61
|
-
}
|
62
|
-
|
63
|
-
#onResourcesChange() {
|
64
|
-
if (this.#connected) {
|
65
|
-
this.dispatchEvent?.(new CustomEvent('d2l-localize-resources-change'));
|
66
|
-
this.config?.onResourcesChange?.();
|
67
|
-
this.onLocalizeResourcesChange?.();
|
68
|
-
}
|
69
|
-
}
|
70
|
-
|
71
|
-
connect() {
|
72
|
-
this.#localeChangeCallback = () => this.#localeChangeHandler();
|
73
|
-
LocalizeClass.documentLocaleSettings.addChangeListener(this.#localeChangeCallback);
|
74
|
-
this.#connected = true;
|
75
|
-
this.#localeChangeCallback();
|
76
|
-
}
|
77
|
-
|
78
|
-
disconnect() {
|
79
|
-
LocalizeClass.documentLocaleSettings.removeChangeListener(this.#localeChangeCallback);
|
80
|
-
this.#connected = false;
|
81
|
-
}
|
82
|
-
|
83
|
-
localize(key) {
|
84
|
-
|
85
|
-
const { language, value } = this.localize.resources?.[key] ?? {};
|
86
|
-
if (!value) return '';
|
87
|
-
|
88
|
-
let params = {};
|
89
|
-
if (arguments.length > 1 && arguments[1]?.constructor === Object) {
|
90
|
-
// support for key-value replacements as a single arg
|
91
|
-
params = arguments[1];
|
92
|
-
} else {
|
93
|
-
// legacy support for localize-behavior replacements as many args
|
94
|
-
for (let i = 1; i < arguments.length; i += 2) {
|
95
|
-
params[arguments[i]] = arguments[i + 1];
|
96
|
-
}
|
97
|
-
}
|
98
|
-
|
99
|
-
const translatedMessage = new IntlMessageFormat(value, language);
|
100
|
-
let formattedMessage = value;
|
101
|
-
try {
|
102
|
-
validateMarkup(formattedMessage, noAllowedTagsRegex);
|
103
|
-
formattedMessage = translatedMessage.format(params);
|
104
|
-
} catch (e) {
|
105
|
-
if (e.name === 'MarkupError') {
|
106
|
-
e = new Error('localize() does not support rich text. For more information, see: https://github.com/BrightspaceUI/core/blob/main/mixins/localize/'); // eslint-disable-line no-ex-assign
|
107
|
-
formattedMessage = '';
|
108
|
-
}
|
109
|
-
console.error(e);
|
110
|
-
}
|
111
|
-
|
112
|
-
return formattedMessage;
|
113
|
-
}
|
114
|
-
|
115
|
-
localizeHTML(key, params = {}) {
|
116
|
-
|
117
|
-
const { language, value } = this.localize.resources?.[key] ?? {};
|
118
|
-
if (!value) return '';
|
119
|
-
|
120
|
-
const translatedMessage = new IntlMessageFormat(value, language);
|
121
|
-
let formattedMessage = value;
|
122
|
-
try {
|
123
|
-
const unvalidated = translatedMessage.format({
|
124
|
-
b: chunks => LocalizeClass.#localizeMarkup`<b>${chunks}</b>`,
|
125
|
-
br: () => LocalizeClass.#localizeMarkup`<br>`,
|
126
|
-
em: chunks => LocalizeClass.#localizeMarkup`<em>${chunks}</em>`,
|
127
|
-
i: chunks => LocalizeClass.#localizeMarkup`<i>${chunks}</i>`,
|
128
|
-
p: chunks => LocalizeClass.#localizeMarkup`<p>${chunks}</p>`,
|
129
|
-
strong: chunks => LocalizeClass.#localizeMarkup`<strong>${chunks}</strong>`,
|
130
|
-
...params
|
131
|
-
});
|
132
|
-
validateMarkup(unvalidated);
|
133
|
-
formattedMessage = unvalidated;
|
134
|
-
} catch (e) {
|
135
|
-
if (e.name === 'MarkupError') formattedMessage = '';
|
136
|
-
console.error(e);
|
137
|
-
}
|
138
|
-
|
139
|
-
return formattedMessage;
|
140
|
-
}
|
141
|
-
|
142
|
-
__resourcesLoadedPromise = new Promise(r => this.#resolveResourcesLoaded = r);
|
143
|
-
|
144
|
-
static _generatePossibleLanguages(config) {
|
145
|
-
|
146
|
-
if (config?.useBrowserLangs) return navigator.languages.map(e => e.toLowerCase()).concat('en');
|
147
|
-
|
148
|
-
const { language, fallbackLanguage } = this.documentLocaleSettings;
|
149
|
-
const langs = [ language, fallbackLanguage ]
|
150
|
-
.filter(e => e)
|
151
|
-
.map(e => [ e.toLowerCase(), e.split('-')[0] ])
|
152
|
-
.flat();
|
153
|
-
|
154
|
-
return Array.from(new Set([ ...langs, 'en-us', 'en' ]));
|
155
|
-
}
|
156
|
-
|
157
|
-
static _getAllLocalizeResources(config = this.localizeConfig) {
|
158
|
-
const resourcesLoadedPromises = [];
|
159
|
-
const superCtor = Object.getPrototypeOf(this);
|
160
|
-
// get imported terms for each config, head up the chain to get them all
|
161
|
-
if ('_getAllLocalizeResources' in superCtor) {
|
162
|
-
const superConfig = Object.prototype.hasOwnProperty.call(superCtor, 'localizeConfig') && superCtor.localizeConfig.importFunc ? superCtor.localizeConfig : config;
|
163
|
-
resourcesLoadedPromises.push(superCtor._getAllLocalizeResources(superConfig));
|
164
|
-
}
|
165
|
-
if (Object.prototype.hasOwnProperty.call(this, 'getLocalizeResources') || Object.prototype.hasOwnProperty.call(this, 'resources')) {
|
166
|
-
const possibleLanguages = this._generatePossibleLanguages(config);
|
167
|
-
const resourcesPromise = this.getLocalizeResources(possibleLanguages, config);
|
168
|
-
resourcesLoadedPromises.push(resourcesPromise);
|
169
|
-
}
|
170
|
-
return Promise.all(resourcesLoadedPromises);
|
171
|
-
}
|
172
|
-
|
173
|
-
static async _getLocalizeResources(langs, { importFunc, osloCollection, useBrowserLangs }) {
|
174
|
-
|
175
|
-
// in dev, don't request unsupported langpacks
|
176
|
-
if (!importFunc.toString().includes('switch') && !useBrowserLangs) {
|
177
|
-
langs = langs.filter(lang => supportedLangpacks.includes(lang));
|
178
|
-
}
|
179
|
-
|
180
|
-
for (const lang of [...langs, fallbackLang]) {
|
181
|
-
|
182
|
-
const resources = await Promise.resolve(importFunc(lang)).catch(() => {});
|
183
|
-
|
184
|
-
if (resources) {
|
185
|
-
|
186
|
-
if (osloCollection) {
|
187
|
-
return await getLocalizeOverrideResources(
|
188
|
-
lang,
|
189
|
-
resources,
|
190
|
-
() => osloCollection
|
191
|
-
);
|
192
|
-
}
|
193
|
-
|
194
|
-
return {
|
195
|
-
language: lang,
|
196
|
-
resources
|
197
|
-
};
|
198
|
-
}
|
199
|
-
}
|
200
|
-
}
|
201
|
-
|
202
|
-
_hasResources() {
|
203
|
-
return this.constructor.localizeConfig ? Boolean(this.constructor.localizeConfig.importFunc) : this.constructor.getLocalizeResources !== undefined;
|
204
|
-
}
|
205
|
-
|
206
|
-
};
|
207
|
-
|
208
|
-
export const Localize = class extends getLocalizeClass() {
|
209
|
-
|
210
|
-
static getLocalizeResources() {
|
211
|
-
return super._getLocalizeResources(...arguments);
|
212
|
-
}
|
213
|
-
|
214
|
-
constructor(config) {
|
215
|
-
super();
|
216
|
-
super.constructor.setLocalizeMarkup(localizeMarkup);
|
217
|
-
this.config = config;
|
218
|
-
this.connect();
|
219
|
-
}
|
220
|
-
|
221
|
-
get ready() {
|
222
|
-
return this.__resourcesLoadedPromise;
|
223
|
-
}
|
224
|
-
|
225
|
-
connect() {
|
226
|
-
super.connect();
|
227
|
-
return this.ready;
|
228
|
-
}
|
229
|
-
|
230
|
-
};
|
231
|
-
|
232
|
-
class MarkupError extends Error {
|
233
|
-
name = this.constructor.name;
|
234
|
-
}
|
235
|
-
|
236
|
-
export function validateMarkup(content, disallowedTagsRegex) {
|
237
|
-
if (content) {
|
238
|
-
if (content.forEach) {
|
239
|
-
content.forEach(item => validateMarkup(item));
|
240
|
-
return;
|
241
|
-
}
|
242
|
-
if (content._localizeMarkup) return;
|
243
|
-
if (Object.hasOwn(content, '_$litType$')) throw new MarkupError('Rich-text replacements must use localizeMarkup templates. For more information, see: https://github.com/BrightspaceUI/core/blob/main/mixins/localize/');
|
244
|
-
|
245
|
-
if (content.constructor === String && disallowedTagsRegex?.test(content)) throw new MarkupError(`Rich-text replacements may use only the following allowed elements: ${allowedTags}. For more information, see: https://github.com/BrightspaceUI/core/blob/main/mixins/localize/`);
|
246
|
-
}
|
247
|
-
}
|
248
|
-
|
249
|
-
export function localizeMarkup(strings, ...expressions) {
|
250
|
-
strings.forEach(str => validateMarkup(str, disallowedTagsRegex));
|
251
|
-
expressions.forEach(exp => validateMarkup(exp, disallowedTagsRegex));
|
252
|
-
return strings.reduce((acc, i, idx) => {
|
253
|
-
return acc.push(i, expressions[idx] ?? '') && acc;
|
254
|
-
}, []).join('');
|
255
|
-
}
|