@tolgee/web 5.3.0 → 5.3.1-rc.bb22ab95.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/index.js +4 -0
- package/package.json +7 -13
- package/dist/tolgee-web.universal.cjs.js +0 -3053
- package/dist/tolgee-web.universal.cjs.js.map +0 -1
- package/dist/tolgee-web.universal.cjs.min.js +0 -2
- package/dist/tolgee-web.universal.cjs.min.js.map +0 -1
- package/dist/tolgee-web.universal.esm.js +0 -3032
- package/dist/tolgee-web.universal.esm.js.map +0 -1
- package/dist/tolgee-web.universal.esm.min.js +0 -2
- package/dist/tolgee-web.universal.esm.min.js.map +0 -1
- package/dist/tolgee-web.universal.esm.min.mjs +0 -2
- package/dist/tolgee-web.universal.esm.min.mjs.map +0 -1
- package/dist/tolgee-web.universal.esm.mjs +0 -3032
- package/dist/tolgee-web.universal.esm.mjs.map +0 -1
- package/dist/tolgee-web.universal.umd.js +0 -3059
- package/dist/tolgee-web.universal.umd.js.map +0 -1
- package/dist/tolgee-web.universal.umd.min.js +0 -2
- package/dist/tolgee-web.universal.umd.min.js.map +0 -1
|
@@ -1,3032 +0,0 @@
|
|
|
1
|
-
// needs to be same as in @tolgee/core package
|
|
2
|
-
const DEVTOOLS_ID = '__tolgee_dev_tools';
|
|
3
|
-
const PREFERRED_LANGUAGES_LOCAL_STORAGE_KEY = '__tolgee_preferredLanguages';
|
|
4
|
-
/**
|
|
5
|
-
* Use this if you want to indicate to tolgee that element contains key
|
|
6
|
-
*/
|
|
7
|
-
const TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE = 'data-tolgee-key-only';
|
|
8
|
-
/**
|
|
9
|
-
* Use this attribute if you want tolgee Observer to not touch part of the DOM
|
|
10
|
-
*/
|
|
11
|
-
const TOLGEE_RESTRICT_ATTRIBUTE = 'data-tolgee-restricted';
|
|
12
|
-
/**
|
|
13
|
-
* This attribute is present on elements that have been registred by tolgee and are clickable for in-context
|
|
14
|
-
*/
|
|
15
|
-
const TOLGEE_ATTRIBUTE_NAME = '_tolgee';
|
|
16
|
-
const TOLGEE_HIGHLIGHTER_CLASS = '_tolgee-highlighter';
|
|
17
|
-
|
|
18
|
-
function isSSR() {
|
|
19
|
-
var _a, _b;
|
|
20
|
-
return typeof ((_b = (_a = globalThis.window) === null || _a === void 0 ? void 0 : _a.document) === null || _b === void 0 ? void 0 : _b.createElement) === 'undefined';
|
|
21
|
-
}
|
|
22
|
-
function throwIfSSR(origin) {
|
|
23
|
-
if (isSSR()) {
|
|
24
|
-
throw new Error(`${origin}: Can't run on the server`);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const DomHelper = (options) => {
|
|
29
|
-
function getParentElement(node) {
|
|
30
|
-
if (node.parentElement) {
|
|
31
|
-
return node.parentElement;
|
|
32
|
-
}
|
|
33
|
-
if (node.ownerElement) {
|
|
34
|
-
return node.ownerElement || undefined;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function getSuitableParent(node) {
|
|
38
|
-
const domParent = getParentElement(node);
|
|
39
|
-
if (domParent === undefined) {
|
|
40
|
-
// eslint-disable-next-line no-console
|
|
41
|
-
console.error(node);
|
|
42
|
-
throw new Error('No suitable parent found for node above.');
|
|
43
|
-
}
|
|
44
|
-
if (!options.passToParent) {
|
|
45
|
-
return domParent;
|
|
46
|
-
}
|
|
47
|
-
if (Array.isArray(options.passToParent)) {
|
|
48
|
-
const tagNameEquals = (elementTagName) => domParent.tagName.toLowerCase() === elementTagName.toLowerCase();
|
|
49
|
-
if (options.passToParent.findIndex(tagNameEquals) === -1) {
|
|
50
|
-
return domParent;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
if (typeof options.passToParent === 'function') {
|
|
54
|
-
if (!options.passToParent(domParent)) {
|
|
55
|
-
return domParent;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return getSuitableParent(domParent);
|
|
59
|
-
}
|
|
60
|
-
return Object.freeze({
|
|
61
|
-
getSuitableParent,
|
|
62
|
-
});
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
function initElementMeta() {
|
|
66
|
-
return {
|
|
67
|
-
nodes: new Map(),
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
function initNodeMeta(oldTextContent, keys) {
|
|
71
|
-
return {
|
|
72
|
-
oldTextContent,
|
|
73
|
-
keys,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function isPromise(value) {
|
|
78
|
-
return Boolean(value && typeof value.then === 'function');
|
|
79
|
-
}
|
|
80
|
-
const valueOrPromise = (value, callback) => {
|
|
81
|
-
if (isPromise(value)) {
|
|
82
|
-
return Promise.resolve(value).then(callback);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
return callback(value);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
const missingOptionError = (option) => `Tolgee: You need to specify '${option}' option`;
|
|
89
|
-
function isObject(item) {
|
|
90
|
-
return typeof item === 'object' && !Array.isArray(item) && item !== null;
|
|
91
|
-
}
|
|
92
|
-
function getFallback(value) {
|
|
93
|
-
if (typeof value === 'string') {
|
|
94
|
-
return [value];
|
|
95
|
-
}
|
|
96
|
-
if (Array.isArray(value)) {
|
|
97
|
-
return value;
|
|
98
|
-
}
|
|
99
|
-
return undefined;
|
|
100
|
-
}
|
|
101
|
-
function getFallbackArray(value) {
|
|
102
|
-
return getFallback(value) || [];
|
|
103
|
-
}
|
|
104
|
-
function getFallbackFromStruct(language, fallbackLanguage) {
|
|
105
|
-
if (isObject(fallbackLanguage)) {
|
|
106
|
-
return getFallbackArray(fallbackLanguage === null || fallbackLanguage === void 0 ? void 0 : fallbackLanguage[language]);
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
return getFallbackArray(fallbackLanguage);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
function unique(arr) {
|
|
113
|
-
return Array.from(new Set(arr));
|
|
114
|
-
}
|
|
115
|
-
function sanitizeUrl(url) {
|
|
116
|
-
return url ? url.replace(/\/+$/, '') : url;
|
|
117
|
-
}
|
|
118
|
-
function getErrorMessage(error) {
|
|
119
|
-
if (typeof error === 'string') {
|
|
120
|
-
return error;
|
|
121
|
-
}
|
|
122
|
-
else if (typeof (error === null || error === void 0 ? void 0 : error.message) === 'string') {
|
|
123
|
-
return error.message;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const EventEmitter = (isActive) => {
|
|
128
|
-
let handlers = [];
|
|
129
|
-
const listen = (handler) => {
|
|
130
|
-
const handlerWrapper = (e) => {
|
|
131
|
-
handler(e);
|
|
132
|
-
};
|
|
133
|
-
handlers.push(handlerWrapper);
|
|
134
|
-
return {
|
|
135
|
-
unsubscribe: () => {
|
|
136
|
-
handlers = handlers.filter((i) => handlerWrapper !== i);
|
|
137
|
-
},
|
|
138
|
-
};
|
|
139
|
-
};
|
|
140
|
-
const emit = (data) => {
|
|
141
|
-
if (isActive()) {
|
|
142
|
-
handlers.forEach((handler) => handler({ value: data }));
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
return Object.freeze({ listen, emit });
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
const EventEmitterSelective = (isActive, getFallbackNs, getDefaultNs) => {
|
|
149
|
-
const listeners = new Set();
|
|
150
|
-
const partialListeners = new Set();
|
|
151
|
-
const listen = (handler) => {
|
|
152
|
-
listeners.add(handler);
|
|
153
|
-
const result = {
|
|
154
|
-
unsubscribe: () => {
|
|
155
|
-
listeners.delete(handler);
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
return result;
|
|
159
|
-
};
|
|
160
|
-
const listenSome = (handler) => {
|
|
161
|
-
const handlerWrapper = {
|
|
162
|
-
fn: (e) => {
|
|
163
|
-
handler(e);
|
|
164
|
-
},
|
|
165
|
-
namespaces: new Set(),
|
|
166
|
-
};
|
|
167
|
-
partialListeners.add(handlerWrapper);
|
|
168
|
-
const result = {
|
|
169
|
-
unsubscribe: () => {
|
|
170
|
-
partialListeners.delete(handlerWrapper);
|
|
171
|
-
},
|
|
172
|
-
subscribeNs: (ns) => {
|
|
173
|
-
getFallbackArray(ns).forEach((val) => handlerWrapper.namespaces.add(val));
|
|
174
|
-
if (ns === undefined) {
|
|
175
|
-
// subscribing to default ns
|
|
176
|
-
handlerWrapper.namespaces.add(getDefaultNs());
|
|
177
|
-
}
|
|
178
|
-
return result;
|
|
179
|
-
},
|
|
180
|
-
};
|
|
181
|
-
return result;
|
|
182
|
-
};
|
|
183
|
-
const callHandlers = (ns) => {
|
|
184
|
-
// everything is implicitly subscribed to fallbacks
|
|
185
|
-
// as it can always fall through to it
|
|
186
|
-
const fallbackNamespaces = new Set(getFallbackNs());
|
|
187
|
-
partialListeners.forEach((handler) => {
|
|
188
|
-
const nsMatches = ns === undefined ||
|
|
189
|
-
(ns === null || ns === void 0 ? void 0 : ns.findIndex((ns) => fallbackNamespaces.has(ns) || handler.namespaces.has(ns))) !== -1;
|
|
190
|
-
if (nsMatches) {
|
|
191
|
-
handler.fn({ value: undefined });
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
};
|
|
195
|
-
let queue = [];
|
|
196
|
-
// merge events in queue into one event
|
|
197
|
-
const solveQueue = () => {
|
|
198
|
-
if (queue.length === 0) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
const queueCopy = queue;
|
|
202
|
-
queue = [];
|
|
203
|
-
listeners.forEach((handler) => {
|
|
204
|
-
handler({ value: undefined });
|
|
205
|
-
});
|
|
206
|
-
let namespaces = new Set();
|
|
207
|
-
queueCopy.forEach((ns) => {
|
|
208
|
-
if (ns === undefined) {
|
|
209
|
-
// when no ns specified, it affects all namespaces
|
|
210
|
-
namespaces = undefined;
|
|
211
|
-
}
|
|
212
|
-
else if (namespaces !== undefined) {
|
|
213
|
-
ns.forEach((ns) => namespaces.add(ns));
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
const namespacesArray = namespaces
|
|
217
|
-
? Array.from(namespaces.keys())
|
|
218
|
-
: undefined;
|
|
219
|
-
callHandlers(namespacesArray);
|
|
220
|
-
};
|
|
221
|
-
const emit = (ns, delayed) => {
|
|
222
|
-
if (isActive()) {
|
|
223
|
-
queue.push(ns);
|
|
224
|
-
if (!delayed) {
|
|
225
|
-
solveQueue();
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
setTimeout(solveQueue, 0);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
return Object.freeze({ listenSome, listen, emit });
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
const Events = (getFallbackNs, getDefaultNs) => {
|
|
236
|
-
let emitterActive = true;
|
|
237
|
-
function isActive() {
|
|
238
|
-
return emitterActive;
|
|
239
|
-
}
|
|
240
|
-
const onPendingLanguageChange = EventEmitter(isActive);
|
|
241
|
-
const onLanguageChange = EventEmitter(isActive);
|
|
242
|
-
const onLoadingChange = EventEmitter(isActive);
|
|
243
|
-
const onFetchingChange = EventEmitter(isActive);
|
|
244
|
-
const onInitialLoaded = EventEmitter(isActive);
|
|
245
|
-
const onRunningChange = EventEmitter(isActive);
|
|
246
|
-
const onCacheChange = EventEmitter(isActive);
|
|
247
|
-
const onUpdate = EventEmitterSelective(isActive, getFallbackNs, getDefaultNs);
|
|
248
|
-
onInitialLoaded.listen(() => onUpdate.emit());
|
|
249
|
-
onLanguageChange.listen(() => onUpdate.emit());
|
|
250
|
-
onCacheChange.listen(({ value }) => {
|
|
251
|
-
onUpdate.emit([value.namespace], true);
|
|
252
|
-
});
|
|
253
|
-
const on = (event, handler) => {
|
|
254
|
-
switch (event) {
|
|
255
|
-
case 'pendingLanguage':
|
|
256
|
-
return onPendingLanguageChange.listen(handler);
|
|
257
|
-
case 'language':
|
|
258
|
-
return onLanguageChange.listen(handler);
|
|
259
|
-
case 'loading':
|
|
260
|
-
return onLoadingChange.listen(handler);
|
|
261
|
-
case 'fetching':
|
|
262
|
-
return onFetchingChange.listen(handler);
|
|
263
|
-
case 'initialLoad':
|
|
264
|
-
return onInitialLoaded.listen(handler);
|
|
265
|
-
case 'running':
|
|
266
|
-
return onRunningChange.listen(handler);
|
|
267
|
-
case 'cache':
|
|
268
|
-
return onCacheChange.listen(handler);
|
|
269
|
-
case 'update':
|
|
270
|
-
return onUpdate.listen(handler);
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
function setEmmiterActive(active) {
|
|
274
|
-
emitterActive = active;
|
|
275
|
-
}
|
|
276
|
-
return Object.freeze({
|
|
277
|
-
onPendingLanguageChange,
|
|
278
|
-
onLanguageChange,
|
|
279
|
-
onLoadingChange,
|
|
280
|
-
onFetchingChange,
|
|
281
|
-
onInitialLoaded,
|
|
282
|
-
onRunningChange,
|
|
283
|
-
onCacheChange,
|
|
284
|
-
onUpdate,
|
|
285
|
-
setEmmiterActive,
|
|
286
|
-
on,
|
|
287
|
-
});
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
const flattenTranslations = (data) => {
|
|
291
|
-
const result = new Map();
|
|
292
|
-
Object.entries(data).forEach(([key, value]) => {
|
|
293
|
-
// ignore empty values
|
|
294
|
-
if (value === undefined || value === null) {
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
if (typeof value === 'object') {
|
|
298
|
-
flattenTranslations(value).forEach((flatValue, flatKey) => {
|
|
299
|
-
result.set(key + '.' + flatKey, flatValue);
|
|
300
|
-
});
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
result.set(key, value);
|
|
304
|
-
});
|
|
305
|
-
return result;
|
|
306
|
-
};
|
|
307
|
-
const decodeCacheKey = (key) => {
|
|
308
|
-
const [firstPart, ...rest] = key.split(':');
|
|
309
|
-
// if namespaces contains ":" it won't get lost
|
|
310
|
-
const secondPart = rest.join(':');
|
|
311
|
-
return { language: firstPart, namespace: secondPart || '' };
|
|
312
|
-
};
|
|
313
|
-
const encodeCacheKey = ({ language, namespace, }) => {
|
|
314
|
-
if (namespace) {
|
|
315
|
-
return `${language}:${namespace}`;
|
|
316
|
-
}
|
|
317
|
-
else {
|
|
318
|
-
return language;
|
|
319
|
-
}
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
const Cache = (onCacheChange, backendGetRecord, backendGetDevRecord, withDefaultNs, isInitialLoading, fetchingObserver, loadingObserver) => {
|
|
323
|
-
const asyncRequests = new Map();
|
|
324
|
-
const cache = new Map();
|
|
325
|
-
let staticData = {};
|
|
326
|
-
let version = 0;
|
|
327
|
-
function addStaticData(data) {
|
|
328
|
-
if (data) {
|
|
329
|
-
staticData = Object.assign(Object.assign({}, staticData), data);
|
|
330
|
-
Object.entries(data).forEach(([key, value]) => {
|
|
331
|
-
if (typeof value !== 'function') {
|
|
332
|
-
const descriptor = decodeCacheKey(key);
|
|
333
|
-
const existing = cache.get(key);
|
|
334
|
-
if (!existing || existing.version === 0) {
|
|
335
|
-
addRecordInternal(descriptor, value, 0);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
function invalidate() {
|
|
342
|
-
asyncRequests.clear();
|
|
343
|
-
version += 1;
|
|
344
|
-
}
|
|
345
|
-
function addRecordInternal(descriptor, data, recordVersion) {
|
|
346
|
-
const cacheKey = encodeCacheKey(descriptor);
|
|
347
|
-
cache.set(cacheKey, {
|
|
348
|
-
data: flattenTranslations(data),
|
|
349
|
-
version: recordVersion,
|
|
350
|
-
});
|
|
351
|
-
onCacheChange.emit(descriptor);
|
|
352
|
-
}
|
|
353
|
-
function addRecord(descriptor, data) {
|
|
354
|
-
addRecordInternal(descriptor, data, version);
|
|
355
|
-
}
|
|
356
|
-
function exists(descriptor, strict = false) {
|
|
357
|
-
const record = cache.get(encodeCacheKey(descriptor));
|
|
358
|
-
if (record && strict) {
|
|
359
|
-
return record.version === version;
|
|
360
|
-
}
|
|
361
|
-
return Boolean(record);
|
|
362
|
-
}
|
|
363
|
-
function getRecord(descriptor) {
|
|
364
|
-
var _a;
|
|
365
|
-
return (_a = cache.get(encodeCacheKey(withDefaultNs(descriptor)))) === null || _a === void 0 ? void 0 : _a.data;
|
|
366
|
-
}
|
|
367
|
-
function getTranslation(descriptor, key) {
|
|
368
|
-
var _a;
|
|
369
|
-
return (_a = cache.get(encodeCacheKey(descriptor))) === null || _a === void 0 ? void 0 : _a.data.get(key);
|
|
370
|
-
}
|
|
371
|
-
function getTranslationNs(namespaces, languages, key) {
|
|
372
|
-
var _a;
|
|
373
|
-
for (const namespace of namespaces) {
|
|
374
|
-
for (const language of languages) {
|
|
375
|
-
const value = (_a = cache
|
|
376
|
-
.get(encodeCacheKey({ language, namespace }))) === null || _a === void 0 ? void 0 : _a.data.get(key);
|
|
377
|
-
if (value !== undefined && value !== null) {
|
|
378
|
-
return [namespace];
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
return unique(namespaces);
|
|
383
|
-
}
|
|
384
|
-
function getTranslationFallback(namespaces, languages, key) {
|
|
385
|
-
var _a;
|
|
386
|
-
for (const namespace of namespaces) {
|
|
387
|
-
for (const language of languages) {
|
|
388
|
-
const value = (_a = cache
|
|
389
|
-
.get(encodeCacheKey({ language, namespace }))) === null || _a === void 0 ? void 0 : _a.data.get(key);
|
|
390
|
-
if (value !== undefined && value !== null) {
|
|
391
|
-
return value;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
return undefined;
|
|
396
|
-
}
|
|
397
|
-
function changeTranslation(descriptor, key, value) {
|
|
398
|
-
var _a;
|
|
399
|
-
const record = (_a = cache.get(encodeCacheKey(descriptor))) === null || _a === void 0 ? void 0 : _a.data;
|
|
400
|
-
record === null || record === void 0 ? void 0 : record.set(key, value);
|
|
401
|
-
onCacheChange.emit(Object.assign(Object.assign({}, descriptor), { key }));
|
|
402
|
-
}
|
|
403
|
-
function isFetching(ns) {
|
|
404
|
-
if (isInitialLoading()) {
|
|
405
|
-
return true;
|
|
406
|
-
}
|
|
407
|
-
if (ns === undefined) {
|
|
408
|
-
return asyncRequests.size > 0;
|
|
409
|
-
}
|
|
410
|
-
const namespaces = getFallbackArray(ns);
|
|
411
|
-
return Boolean(Array.from(asyncRequests.keys()).find((key) => namespaces.includes(decodeCacheKey(key).namespace)));
|
|
412
|
-
}
|
|
413
|
-
function isLoading(language, ns) {
|
|
414
|
-
const namespaces = getFallbackArray(ns);
|
|
415
|
-
return Boolean(isInitialLoading() ||
|
|
416
|
-
Array.from(asyncRequests.keys()).find((key) => {
|
|
417
|
-
const descriptor = decodeCacheKey(key);
|
|
418
|
-
return ((!namespaces.length || namespaces.includes(descriptor.namespace)) &&
|
|
419
|
-
!exists({
|
|
420
|
-
namespace: descriptor.namespace,
|
|
421
|
-
language: language,
|
|
422
|
-
}));
|
|
423
|
-
}));
|
|
424
|
-
}
|
|
425
|
-
/**
|
|
426
|
-
* Fetches production data
|
|
427
|
-
*/
|
|
428
|
-
function fetchProd(keyObject) {
|
|
429
|
-
let dataPromise = undefined;
|
|
430
|
-
if (!dataPromise) {
|
|
431
|
-
const staticDataValue = staticData[encodeCacheKey(keyObject)];
|
|
432
|
-
if (typeof staticDataValue === 'function') {
|
|
433
|
-
dataPromise = staticDataValue();
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
if (!dataPromise) {
|
|
437
|
-
dataPromise = backendGetRecord(keyObject);
|
|
438
|
-
}
|
|
439
|
-
return dataPromise;
|
|
440
|
-
}
|
|
441
|
-
function fetchData(keyObject, isDev) {
|
|
442
|
-
var _a;
|
|
443
|
-
let dataPromise = undefined;
|
|
444
|
-
if (isDev) {
|
|
445
|
-
dataPromise = (_a = backendGetDevRecord(keyObject)) === null || _a === void 0 ? void 0 : _a.catch(() => {
|
|
446
|
-
// eslint-disable-next-line no-console
|
|
447
|
-
console.warn(`Tolgee: Failed to fetch data from dev backend`);
|
|
448
|
-
// fallback to prod fetch if dev fails
|
|
449
|
-
return fetchProd(keyObject);
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
if (!dataPromise) {
|
|
453
|
-
dataPromise = fetchProd(keyObject);
|
|
454
|
-
}
|
|
455
|
-
return dataPromise;
|
|
456
|
-
}
|
|
457
|
-
async function loadRecords(descriptors, isDev) {
|
|
458
|
-
const withPromises = descriptors.map((descriptor) => {
|
|
459
|
-
const keyObject = withDefaultNs(descriptor);
|
|
460
|
-
const cacheKey = encodeCacheKey(keyObject);
|
|
461
|
-
const existingPromise = asyncRequests.get(cacheKey);
|
|
462
|
-
if (existingPromise) {
|
|
463
|
-
return {
|
|
464
|
-
new: false,
|
|
465
|
-
promise: existingPromise,
|
|
466
|
-
keyObject,
|
|
467
|
-
cacheKey,
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
const dataPromise = fetchData(keyObject, isDev) || Promise.resolve(undefined);
|
|
471
|
-
asyncRequests.set(cacheKey, dataPromise);
|
|
472
|
-
return {
|
|
473
|
-
new: true,
|
|
474
|
-
promise: dataPromise,
|
|
475
|
-
keyObject,
|
|
476
|
-
cacheKey,
|
|
477
|
-
};
|
|
478
|
-
});
|
|
479
|
-
fetchingObserver.notify();
|
|
480
|
-
loadingObserver.notify();
|
|
481
|
-
const results = await Promise.all(withPromises.map((val) => val.promise));
|
|
482
|
-
withPromises.forEach((value, i) => {
|
|
483
|
-
const promiseChanged = asyncRequests.get(value.cacheKey) !== value.promise;
|
|
484
|
-
// if promise has changed in between, it means cache been invalidated or
|
|
485
|
-
// new data are being fetched
|
|
486
|
-
if (value.new && !promiseChanged) {
|
|
487
|
-
asyncRequests.delete(value.cacheKey);
|
|
488
|
-
const data = results[i];
|
|
489
|
-
if (data) {
|
|
490
|
-
addRecord(value.keyObject, data);
|
|
491
|
-
}
|
|
492
|
-
else if (!getRecord(value.keyObject)) {
|
|
493
|
-
// if no data exist, put empty object
|
|
494
|
-
addRecord(value.keyObject, {});
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
});
|
|
498
|
-
fetchingObserver.notify();
|
|
499
|
-
loadingObserver.notify();
|
|
500
|
-
return withPromises.map((val) => getRecord(val.keyObject));
|
|
501
|
-
}
|
|
502
|
-
function getAllRecords() {
|
|
503
|
-
const entries = Array.from(cache.entries());
|
|
504
|
-
return entries.map(([key, entry]) => {
|
|
505
|
-
return Object.assign(Object.assign({}, decodeCacheKey(key)), { data: entry.data });
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
return Object.freeze({
|
|
509
|
-
addStaticData,
|
|
510
|
-
invalidate,
|
|
511
|
-
addRecord,
|
|
512
|
-
exists,
|
|
513
|
-
getRecord,
|
|
514
|
-
getTranslation,
|
|
515
|
-
getTranslationNs,
|
|
516
|
-
getTranslationFallback,
|
|
517
|
-
changeTranslation,
|
|
518
|
-
isFetching,
|
|
519
|
-
isLoading,
|
|
520
|
-
loadRecords,
|
|
521
|
-
getAllRecords,
|
|
522
|
-
});
|
|
523
|
-
};
|
|
524
|
-
|
|
525
|
-
/******************************************************************************
|
|
526
|
-
Copyright (c) Microsoft Corporation.
|
|
527
|
-
|
|
528
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
529
|
-
purpose with or without fee is hereby granted.
|
|
530
|
-
|
|
531
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
532
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
533
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
534
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
535
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
536
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
537
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
538
|
-
***************************************************************************** */
|
|
539
|
-
|
|
540
|
-
function __rest$1(s, e) {
|
|
541
|
-
var t = {};
|
|
542
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
543
|
-
t[p] = s[p];
|
|
544
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
545
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
546
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
547
|
-
t[p[i]] = s[p[i]];
|
|
548
|
-
}
|
|
549
|
-
return t;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
const defaultObserverOptions = {
|
|
553
|
-
tagAttributes: {
|
|
554
|
-
textarea: ['placeholder'],
|
|
555
|
-
input: ['value', 'placeholder'],
|
|
556
|
-
img: ['alt'],
|
|
557
|
-
'*': ['aria-label', 'title'],
|
|
558
|
-
},
|
|
559
|
-
restrictedElements: ['script', 'style'],
|
|
560
|
-
highlightKeys: ['Alt'],
|
|
561
|
-
highlightColor: 'rgb(255, 0, 0)',
|
|
562
|
-
highlightWidth: 5,
|
|
563
|
-
inputPrefix: '%-%tolgee:',
|
|
564
|
-
inputSuffix: '%-%',
|
|
565
|
-
passToParent: ['option', 'optgroup'],
|
|
566
|
-
};
|
|
567
|
-
|
|
568
|
-
const DEFAULT_FORMAT_ERROR = 'invalid';
|
|
569
|
-
const DEFAULT_API_URL = 'https://app.tolgee.io';
|
|
570
|
-
const defaultValues = {
|
|
571
|
-
defaultNs: '',
|
|
572
|
-
observerOptions: defaultObserverOptions,
|
|
573
|
-
observerType: 'invisible',
|
|
574
|
-
onFormatError: DEFAULT_FORMAT_ERROR,
|
|
575
|
-
apiUrl: DEFAULT_API_URL,
|
|
576
|
-
};
|
|
577
|
-
const combineOptions = (...states) => {
|
|
578
|
-
let result = {};
|
|
579
|
-
states.forEach((state) => {
|
|
580
|
-
result = Object.assign(Object.assign(Object.assign({}, result), state), { observerOptions: Object.assign(Object.assign({}, result.observerOptions), state === null || state === void 0 ? void 0 : state.observerOptions) });
|
|
581
|
-
});
|
|
582
|
-
return result;
|
|
583
|
-
};
|
|
584
|
-
const initState = (options, previousState) => {
|
|
585
|
-
const initialOptions = combineOptions(defaultValues, previousState === null || previousState === void 0 ? void 0 : previousState.initialOptions, options);
|
|
586
|
-
// remove extra '/' from url end
|
|
587
|
-
initialOptions.apiUrl = sanitizeUrl(initialOptions.apiUrl);
|
|
588
|
-
return {
|
|
589
|
-
initialOptions,
|
|
590
|
-
activeNamespaces: (previousState === null || previousState === void 0 ? void 0 : previousState.activeNamespaces) || new Map(),
|
|
591
|
-
language: previousState === null || previousState === void 0 ? void 0 : previousState.language,
|
|
592
|
-
pendingLanguage: previousState === null || previousState === void 0 ? void 0 : previousState.language,
|
|
593
|
-
isInitialLoading: false,
|
|
594
|
-
isRunning: false,
|
|
595
|
-
};
|
|
596
|
-
};
|
|
597
|
-
|
|
598
|
-
const Plugins = (getLanguage, getInitialOptions, getAvailableLanguages, getTranslationNs, getTranslation, changeTranslation) => {
|
|
599
|
-
const plugins = {
|
|
600
|
-
ui: undefined,
|
|
601
|
-
observer: undefined,
|
|
602
|
-
};
|
|
603
|
-
const instances = {
|
|
604
|
-
formatters: [],
|
|
605
|
-
finalFormatter: undefined,
|
|
606
|
-
observer: undefined,
|
|
607
|
-
devBackend: undefined,
|
|
608
|
-
backends: [],
|
|
609
|
-
ui: undefined,
|
|
610
|
-
languageDetector: undefined,
|
|
611
|
-
languageStorage: undefined,
|
|
612
|
-
};
|
|
613
|
-
const onClick = async ({ keysAndDefaults, event }) => {
|
|
614
|
-
var _a;
|
|
615
|
-
const withNs = keysAndDefaults.map(({ key, ns, defaultValue }) => {
|
|
616
|
-
return {
|
|
617
|
-
key,
|
|
618
|
-
defaultValue,
|
|
619
|
-
ns: getTranslationNs({ key, ns }),
|
|
620
|
-
translation: getTranslation({
|
|
621
|
-
key,
|
|
622
|
-
ns,
|
|
623
|
-
}),
|
|
624
|
-
};
|
|
625
|
-
});
|
|
626
|
-
(_a = instances.ui) === null || _a === void 0 ? void 0 : _a.handleElementClick(withNs, event);
|
|
627
|
-
};
|
|
628
|
-
const stop = () => {
|
|
629
|
-
var _a;
|
|
630
|
-
instances.ui = undefined;
|
|
631
|
-
(_a = instances.observer) === null || _a === void 0 ? void 0 : _a.stop();
|
|
632
|
-
};
|
|
633
|
-
const highlight = (key, ns) => {
|
|
634
|
-
var _a, _b;
|
|
635
|
-
return ((_b = (_a = instances.observer) === null || _a === void 0 ? void 0 : _a.highlight) === null || _b === void 0 ? void 0 : _b.call(_a, key, ns)) || { unhighlight() { } };
|
|
636
|
-
};
|
|
637
|
-
const translate = (props) => {
|
|
638
|
-
const translation = getTranslation({
|
|
639
|
-
key: props.key,
|
|
640
|
-
ns: props.ns,
|
|
641
|
-
});
|
|
642
|
-
return formatTranslation(Object.assign(Object.assign({}, props), { translation, formatEnabled: true }));
|
|
643
|
-
};
|
|
644
|
-
const setObserver = (observer) => {
|
|
645
|
-
plugins.observer = observer;
|
|
646
|
-
};
|
|
647
|
-
const hasObserver = () => {
|
|
648
|
-
return Boolean(plugins.observer);
|
|
649
|
-
};
|
|
650
|
-
const addFormatter = (formatter) => {
|
|
651
|
-
if (formatter) {
|
|
652
|
-
instances.formatters.push(formatter);
|
|
653
|
-
}
|
|
654
|
-
};
|
|
655
|
-
const setFinalFormatter = (formatter) => {
|
|
656
|
-
instances.finalFormatter = formatter;
|
|
657
|
-
};
|
|
658
|
-
const setUi = (ui) => {
|
|
659
|
-
plugins.ui = ui;
|
|
660
|
-
};
|
|
661
|
-
const hasUi = () => {
|
|
662
|
-
return Boolean(plugins.ui);
|
|
663
|
-
};
|
|
664
|
-
const setLanguageStorage = (storage) => {
|
|
665
|
-
instances.languageStorage = storage;
|
|
666
|
-
};
|
|
667
|
-
const getLanguageStorage = () => {
|
|
668
|
-
return instances.languageStorage;
|
|
669
|
-
};
|
|
670
|
-
const setStoredLanguage = (language) => {
|
|
671
|
-
var _a;
|
|
672
|
-
(_a = instances.languageStorage) === null || _a === void 0 ? void 0 : _a.setLanguage(language);
|
|
673
|
-
};
|
|
674
|
-
const setLanguageDetector = (detector) => {
|
|
675
|
-
instances.languageDetector = detector;
|
|
676
|
-
};
|
|
677
|
-
const getLanguageDetector = () => {
|
|
678
|
-
return instances.languageDetector;
|
|
679
|
-
};
|
|
680
|
-
const detectLanguage = () => {
|
|
681
|
-
if (!instances.languageDetector) {
|
|
682
|
-
return undefined;
|
|
683
|
-
}
|
|
684
|
-
const availableLanguages = getAvailableLanguages();
|
|
685
|
-
return instances.languageDetector.getLanguage({
|
|
686
|
-
availableLanguages,
|
|
687
|
-
});
|
|
688
|
-
};
|
|
689
|
-
const getInitialLanguage = () => {
|
|
690
|
-
var _a;
|
|
691
|
-
const availableLanguages = getAvailableLanguages();
|
|
692
|
-
const languageOrPromise = (_a = instances.languageStorage) === null || _a === void 0 ? void 0 : _a.getLanguage();
|
|
693
|
-
return valueOrPromise(languageOrPromise, (language) => {
|
|
694
|
-
if ((!availableLanguages || availableLanguages.includes(language)) &&
|
|
695
|
-
language) {
|
|
696
|
-
return language;
|
|
697
|
-
}
|
|
698
|
-
return detectLanguage();
|
|
699
|
-
});
|
|
700
|
-
};
|
|
701
|
-
const addBackend = (backend) => {
|
|
702
|
-
if (backend) {
|
|
703
|
-
instances.backends.push(backend);
|
|
704
|
-
}
|
|
705
|
-
};
|
|
706
|
-
const setDevBackend = (backend) => {
|
|
707
|
-
instances.devBackend = backend;
|
|
708
|
-
};
|
|
709
|
-
const run = () => {
|
|
710
|
-
var _a, _b, _c;
|
|
711
|
-
if (!instances.ui) {
|
|
712
|
-
const { apiKey, apiUrl, projectId } = getInitialOptions();
|
|
713
|
-
instances.ui = (_a = plugins.ui) === null || _a === void 0 ? void 0 : _a.call(plugins, {
|
|
714
|
-
apiKey: apiKey,
|
|
715
|
-
apiUrl: apiUrl,
|
|
716
|
-
projectId,
|
|
717
|
-
highlight,
|
|
718
|
-
changeTranslation,
|
|
719
|
-
});
|
|
720
|
-
}
|
|
721
|
-
if (!instances.observer) {
|
|
722
|
-
instances.observer = (_b = plugins.observer) === null || _b === void 0 ? void 0 : _b.call(plugins, {
|
|
723
|
-
translate,
|
|
724
|
-
onClick,
|
|
725
|
-
options: getInitialOptions().observerOptions,
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
|
-
(_c = instances.observer) === null || _c === void 0 ? void 0 : _c.run({ mouseHighlight: true });
|
|
729
|
-
};
|
|
730
|
-
const getDevBackend = () => {
|
|
731
|
-
return instances.devBackend;
|
|
732
|
-
};
|
|
733
|
-
const getBackendDevRecord = ({ language, namespace }) => {
|
|
734
|
-
var _a;
|
|
735
|
-
const { apiKey, apiUrl, projectId } = getInitialOptions();
|
|
736
|
-
return (_a = instances.devBackend) === null || _a === void 0 ? void 0 : _a.getRecord({
|
|
737
|
-
apiKey,
|
|
738
|
-
apiUrl,
|
|
739
|
-
projectId,
|
|
740
|
-
language,
|
|
741
|
-
namespace,
|
|
742
|
-
});
|
|
743
|
-
};
|
|
744
|
-
const getBackendRecord = ({ language, namespace }) => {
|
|
745
|
-
for (const backend of instances.backends) {
|
|
746
|
-
const data = backend.getRecord({ language, namespace });
|
|
747
|
-
if (isPromise(data)) {
|
|
748
|
-
return data === null || data === void 0 ? void 0 : data.catch((e) => {
|
|
749
|
-
// eslint-disable-next-line no-console
|
|
750
|
-
console.error(e);
|
|
751
|
-
return {};
|
|
752
|
-
});
|
|
753
|
-
}
|
|
754
|
-
if (data !== undefined) {
|
|
755
|
-
return data;
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
return undefined;
|
|
759
|
-
};
|
|
760
|
-
const unwrap = (text) => {
|
|
761
|
-
var _a;
|
|
762
|
-
if (instances.observer) {
|
|
763
|
-
return (_a = instances.observer) === null || _a === void 0 ? void 0 : _a.unwrap(text);
|
|
764
|
-
}
|
|
765
|
-
return { text, keys: [] };
|
|
766
|
-
};
|
|
767
|
-
const retranslate = () => {
|
|
768
|
-
var _a;
|
|
769
|
-
(_a = instances.observer) === null || _a === void 0 ? void 0 : _a.retranslate();
|
|
770
|
-
};
|
|
771
|
-
function addPlugin(tolgeeInstance, plugin) {
|
|
772
|
-
const pluginTools = Object.freeze({
|
|
773
|
-
setFinalFormatter,
|
|
774
|
-
addFormatter,
|
|
775
|
-
setObserver,
|
|
776
|
-
hasObserver,
|
|
777
|
-
setUi,
|
|
778
|
-
hasUi,
|
|
779
|
-
setDevBackend,
|
|
780
|
-
addBackend,
|
|
781
|
-
setLanguageDetector,
|
|
782
|
-
setLanguageStorage,
|
|
783
|
-
});
|
|
784
|
-
plugin(tolgeeInstance, pluginTools);
|
|
785
|
-
}
|
|
786
|
-
function formatTranslation(_a) {
|
|
787
|
-
var _b;
|
|
788
|
-
var { formatEnabled } = _a, props = __rest$1(_a, ["formatEnabled"]);
|
|
789
|
-
const { key, translation, defaultValue, noWrap, params, orEmpty, ns } = props;
|
|
790
|
-
const formattableTranslation = translation || defaultValue;
|
|
791
|
-
let result = formattableTranslation || (orEmpty ? '' : key);
|
|
792
|
-
const language = getLanguage();
|
|
793
|
-
const isFormatEnabled = formatEnabled || !((_b = instances.observer) === null || _b === void 0 ? void 0 : _b.outputNotFormattable);
|
|
794
|
-
const wrap = (result) => {
|
|
795
|
-
if (instances.observer && !noWrap) {
|
|
796
|
-
return instances.observer.wrap({
|
|
797
|
-
key,
|
|
798
|
-
translation: result,
|
|
799
|
-
defaultValue,
|
|
800
|
-
params,
|
|
801
|
-
ns,
|
|
802
|
-
});
|
|
803
|
-
}
|
|
804
|
-
return result;
|
|
805
|
-
};
|
|
806
|
-
result = wrap(result);
|
|
807
|
-
try {
|
|
808
|
-
if (formattableTranslation && language && isFormatEnabled) {
|
|
809
|
-
for (const formatter of instances.formatters) {
|
|
810
|
-
result = formatter.format({
|
|
811
|
-
translation: result,
|
|
812
|
-
language,
|
|
813
|
-
params,
|
|
814
|
-
});
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
if (instances.finalFormatter &&
|
|
818
|
-
formattableTranslation &&
|
|
819
|
-
language &&
|
|
820
|
-
isFormatEnabled) {
|
|
821
|
-
result = instances.finalFormatter.format({
|
|
822
|
-
translation: result,
|
|
823
|
-
language,
|
|
824
|
-
params,
|
|
825
|
-
});
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
catch (e) {
|
|
829
|
-
// eslint-disable-next-line no-console
|
|
830
|
-
console.error(e);
|
|
831
|
-
const errorMessage = getErrorMessage(e) || DEFAULT_FORMAT_ERROR;
|
|
832
|
-
const onFormatError = getInitialOptions().onFormatError;
|
|
833
|
-
const formatErrorType = typeof onFormatError;
|
|
834
|
-
if (formatErrorType === 'string') {
|
|
835
|
-
result = onFormatError;
|
|
836
|
-
}
|
|
837
|
-
else if (formatErrorType === 'function') {
|
|
838
|
-
result = onFormatError(errorMessage, props);
|
|
839
|
-
}
|
|
840
|
-
else {
|
|
841
|
-
result = DEFAULT_FORMAT_ERROR;
|
|
842
|
-
}
|
|
843
|
-
// wrap error message, so it's detectable
|
|
844
|
-
result = wrap(result);
|
|
845
|
-
}
|
|
846
|
-
return result;
|
|
847
|
-
}
|
|
848
|
-
function hasDevBackend() {
|
|
849
|
-
return Boolean(getDevBackend());
|
|
850
|
-
}
|
|
851
|
-
const wrap = (params) => {
|
|
852
|
-
var _a;
|
|
853
|
-
if (instances.observer) {
|
|
854
|
-
return (_a = instances.observer) === null || _a === void 0 ? void 0 : _a.wrap(params);
|
|
855
|
-
}
|
|
856
|
-
return params.translation;
|
|
857
|
-
};
|
|
858
|
-
return Object.freeze({
|
|
859
|
-
addPlugin,
|
|
860
|
-
formatTranslation,
|
|
861
|
-
getDevBackend,
|
|
862
|
-
getBackendRecord,
|
|
863
|
-
getBackendDevRecord,
|
|
864
|
-
getLanguageDetector,
|
|
865
|
-
getLanguageStorage,
|
|
866
|
-
getInitialLanguage,
|
|
867
|
-
setStoredLanguage,
|
|
868
|
-
run,
|
|
869
|
-
stop,
|
|
870
|
-
retranslate,
|
|
871
|
-
highlight,
|
|
872
|
-
unwrap,
|
|
873
|
-
wrap,
|
|
874
|
-
hasDevBackend,
|
|
875
|
-
});
|
|
876
|
-
};
|
|
877
|
-
|
|
878
|
-
const ValueObserver = (initialValue, valueGetter, handler) => {
|
|
879
|
-
let previousValue = initialValue;
|
|
880
|
-
function init(value) {
|
|
881
|
-
previousValue = value;
|
|
882
|
-
}
|
|
883
|
-
function notify() {
|
|
884
|
-
const value = valueGetter();
|
|
885
|
-
if (previousValue !== value) {
|
|
886
|
-
handler(value);
|
|
887
|
-
}
|
|
888
|
-
previousValue = value;
|
|
889
|
-
}
|
|
890
|
-
return Object.freeze({
|
|
891
|
-
init,
|
|
892
|
-
notify,
|
|
893
|
-
});
|
|
894
|
-
};
|
|
895
|
-
|
|
896
|
-
const State = (onLanguageChange, onPendingLanguageChange, onRunningChange) => {
|
|
897
|
-
let state = initState();
|
|
898
|
-
let devCredentials = undefined;
|
|
899
|
-
function init(options) {
|
|
900
|
-
state = initState(options, state);
|
|
901
|
-
}
|
|
902
|
-
function isRunning() {
|
|
903
|
-
return state.isRunning;
|
|
904
|
-
}
|
|
905
|
-
function setRunning(value) {
|
|
906
|
-
if (state.isRunning !== value) {
|
|
907
|
-
state.isRunning = value;
|
|
908
|
-
onRunningChange.emit(value);
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
function isInitialLoading() {
|
|
912
|
-
return state.isInitialLoading;
|
|
913
|
-
}
|
|
914
|
-
function setInitialLoading(value) {
|
|
915
|
-
state.isInitialLoading = value;
|
|
916
|
-
}
|
|
917
|
-
function getLanguage() {
|
|
918
|
-
return state.language || state.initialOptions.language;
|
|
919
|
-
}
|
|
920
|
-
function setLanguage(language) {
|
|
921
|
-
if (state.language !== language) {
|
|
922
|
-
state.language = language;
|
|
923
|
-
onLanguageChange.emit(language);
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
function getPendingLanguage() {
|
|
927
|
-
return state.pendingLanguage || getLanguage();
|
|
928
|
-
}
|
|
929
|
-
function setPendingLanguage(language) {
|
|
930
|
-
if (state.pendingLanguage !== language) {
|
|
931
|
-
state.pendingLanguage = language;
|
|
932
|
-
onPendingLanguageChange.emit(language);
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
function getInitialOptions() {
|
|
936
|
-
return Object.assign(Object.assign({}, state.initialOptions), devCredentials);
|
|
937
|
-
}
|
|
938
|
-
function addActiveNs(ns) {
|
|
939
|
-
const namespaces = getFallbackArray(ns);
|
|
940
|
-
namespaces.forEach((namespace) => {
|
|
941
|
-
const value = state.activeNamespaces.get(namespace);
|
|
942
|
-
if (value !== undefined) {
|
|
943
|
-
state.activeNamespaces.set(namespace, value + 1);
|
|
944
|
-
}
|
|
945
|
-
else {
|
|
946
|
-
state.activeNamespaces.set(namespace, 1);
|
|
947
|
-
}
|
|
948
|
-
});
|
|
949
|
-
}
|
|
950
|
-
function removeActiveNs(ns) {
|
|
951
|
-
const namespaces = getFallbackArray(ns);
|
|
952
|
-
namespaces.forEach((namespace) => {
|
|
953
|
-
const value = state.activeNamespaces.get(namespace);
|
|
954
|
-
if (value !== undefined && value > 1) {
|
|
955
|
-
state.activeNamespaces.set(namespace, value - 1);
|
|
956
|
-
}
|
|
957
|
-
else {
|
|
958
|
-
state.activeNamespaces.delete(namespace);
|
|
959
|
-
}
|
|
960
|
-
});
|
|
961
|
-
}
|
|
962
|
-
function getRequiredNamespaces() {
|
|
963
|
-
return unique([
|
|
964
|
-
...(state.initialOptions.ns || [state.initialOptions.defaultNs]),
|
|
965
|
-
...getFallbackArray(state.initialOptions.fallbackNs),
|
|
966
|
-
...state.activeNamespaces.keys(),
|
|
967
|
-
]);
|
|
968
|
-
}
|
|
969
|
-
function getFallbackLangs(lang) {
|
|
970
|
-
const language = lang || getLanguage();
|
|
971
|
-
if (!language) {
|
|
972
|
-
return [];
|
|
973
|
-
}
|
|
974
|
-
return unique([
|
|
975
|
-
language,
|
|
976
|
-
...getFallbackFromStruct(language, state.initialOptions.fallbackLanguage),
|
|
977
|
-
]);
|
|
978
|
-
}
|
|
979
|
-
function getFallbackNs() {
|
|
980
|
-
return getFallbackArray(state.initialOptions.fallbackNs);
|
|
981
|
-
}
|
|
982
|
-
function getDefaultNs(ns) {
|
|
983
|
-
return ns === undefined ? state.initialOptions.defaultNs : ns;
|
|
984
|
-
}
|
|
985
|
-
function getAvailableLanguages() {
|
|
986
|
-
if (state.initialOptions.availableLanguages) {
|
|
987
|
-
return state.initialOptions.availableLanguages;
|
|
988
|
-
}
|
|
989
|
-
else if (state.initialOptions.staticData) {
|
|
990
|
-
const languagesFromStaticData = Object.keys(state.initialOptions.staticData).map((key) => decodeCacheKey(key).language);
|
|
991
|
-
return Array.from(new Set(languagesFromStaticData));
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
function withDefaultNs(descriptor) {
|
|
995
|
-
return {
|
|
996
|
-
namespace: descriptor.namespace === undefined
|
|
997
|
-
? getInitialOptions().defaultNs
|
|
998
|
-
: descriptor.namespace,
|
|
999
|
-
language: descriptor.language,
|
|
1000
|
-
};
|
|
1001
|
-
}
|
|
1002
|
-
function overrideCredentials(credentials) {
|
|
1003
|
-
if (credentials) {
|
|
1004
|
-
devCredentials = Object.assign(Object.assign({}, credentials), { apiUrl: sanitizeUrl(credentials.apiUrl) });
|
|
1005
|
-
}
|
|
1006
|
-
else {
|
|
1007
|
-
devCredentials = undefined;
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
return Object.freeze({
|
|
1011
|
-
init,
|
|
1012
|
-
isRunning,
|
|
1013
|
-
setRunning,
|
|
1014
|
-
isInitialLoading,
|
|
1015
|
-
setInitialLoading,
|
|
1016
|
-
getLanguage,
|
|
1017
|
-
setLanguage,
|
|
1018
|
-
getPendingLanguage,
|
|
1019
|
-
setPendingLanguage,
|
|
1020
|
-
getInitialOptions,
|
|
1021
|
-
addActiveNs,
|
|
1022
|
-
removeActiveNs,
|
|
1023
|
-
getRequiredNamespaces,
|
|
1024
|
-
getFallbackLangs,
|
|
1025
|
-
getFallbackNs,
|
|
1026
|
-
getDefaultNs,
|
|
1027
|
-
getAvailableLanguages,
|
|
1028
|
-
withDefaultNs,
|
|
1029
|
-
overrideCredentials,
|
|
1030
|
-
});
|
|
1031
|
-
};
|
|
1032
|
-
|
|
1033
|
-
function parseCombinedOptions(_a) {
|
|
1034
|
-
var { ns, noWrap, orEmpty, params } = _a, rest = __rest$1(_a, ["ns", "noWrap", "orEmpty", "params"]);
|
|
1035
|
-
const options = {
|
|
1036
|
-
ns: ns,
|
|
1037
|
-
noWrap: noWrap,
|
|
1038
|
-
orEmpty: orEmpty,
|
|
1039
|
-
};
|
|
1040
|
-
return Object.assign(Object.assign({}, options), { params: Object.assign({}, rest) });
|
|
1041
|
-
}
|
|
1042
|
-
const getTranslateProps = (keyOrProps, ...params) => {
|
|
1043
|
-
let result = {};
|
|
1044
|
-
let options;
|
|
1045
|
-
if (typeof keyOrProps === 'object') {
|
|
1046
|
-
result = keyOrProps;
|
|
1047
|
-
}
|
|
1048
|
-
else {
|
|
1049
|
-
result.key = keyOrProps;
|
|
1050
|
-
if (typeof params[0] === 'string') {
|
|
1051
|
-
result.defaultValue = params[0];
|
|
1052
|
-
options = params[1];
|
|
1053
|
-
}
|
|
1054
|
-
else if (typeof params[0] === 'object') {
|
|
1055
|
-
options = params[0];
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
if (options) {
|
|
1059
|
-
result = Object.assign(Object.assign({}, parseCombinedOptions(options)), result);
|
|
1060
|
-
}
|
|
1061
|
-
return result;
|
|
1062
|
-
};
|
|
1063
|
-
|
|
1064
|
-
const Controller = ({ options }) => {
|
|
1065
|
-
const events = Events(getFallbackNs, getDefaultNs);
|
|
1066
|
-
const fetchingObserver = ValueObserver(false, () => cache.isFetching(), events.onFetchingChange.emit);
|
|
1067
|
-
const loadingObserver = ValueObserver(false, () => isLoading(), events.onLoadingChange.emit);
|
|
1068
|
-
const state = State(events.onLanguageChange, events.onPendingLanguageChange, events.onRunningChange);
|
|
1069
|
-
const pluginService = Plugins(state.getLanguage, state.getInitialOptions, state.getAvailableLanguages, getTranslationNs, getTranslation, changeTranslation);
|
|
1070
|
-
const cache = Cache(events.onCacheChange, pluginService.getBackendRecord, pluginService.getBackendDevRecord, state.withDefaultNs, state.isInitialLoading, fetchingObserver, loadingObserver);
|
|
1071
|
-
if (options) {
|
|
1072
|
-
init(options);
|
|
1073
|
-
}
|
|
1074
|
-
events.onUpdate.listen(() => {
|
|
1075
|
-
if (state.isRunning()) {
|
|
1076
|
-
pluginService.retranslate();
|
|
1077
|
-
}
|
|
1078
|
-
});
|
|
1079
|
-
function getFallbackNs() {
|
|
1080
|
-
return state.getFallbackNs();
|
|
1081
|
-
}
|
|
1082
|
-
function getDefaultNs(ns) {
|
|
1083
|
-
return state.getDefaultNs(ns);
|
|
1084
|
-
}
|
|
1085
|
-
// gets all namespaces where translation could be located
|
|
1086
|
-
// takes (ns|default, fallback ns)
|
|
1087
|
-
function getDefaultAndFallbackNs(ns) {
|
|
1088
|
-
return [...getFallbackArray(getDefaultNs(ns)), ...getFallbackNs()];
|
|
1089
|
-
}
|
|
1090
|
-
// gets all namespaces which need to be loaded
|
|
1091
|
-
// takes (ns|default, initial ns, fallback ns, active ns)
|
|
1092
|
-
function getRequiredNamespaces(ns) {
|
|
1093
|
-
return [
|
|
1094
|
-
...getFallbackArray(ns || getDefaultNs()),
|
|
1095
|
-
...state.getRequiredNamespaces(),
|
|
1096
|
-
];
|
|
1097
|
-
}
|
|
1098
|
-
function changeTranslation(descriptor, key, value) {
|
|
1099
|
-
const keyObject = state.withDefaultNs(descriptor);
|
|
1100
|
-
const previousValue = cache.getTranslation(keyObject, key);
|
|
1101
|
-
cache.changeTranslation(keyObject, key, value);
|
|
1102
|
-
return {
|
|
1103
|
-
revert: () => {
|
|
1104
|
-
cache.changeTranslation(keyObject, key, previousValue);
|
|
1105
|
-
},
|
|
1106
|
-
};
|
|
1107
|
-
}
|
|
1108
|
-
function init(options) {
|
|
1109
|
-
state.init(options);
|
|
1110
|
-
cache.addStaticData(state.getInitialOptions().staticData);
|
|
1111
|
-
}
|
|
1112
|
-
function isLoading(ns) {
|
|
1113
|
-
return cache.isLoading(state.getLanguage(), ns);
|
|
1114
|
-
}
|
|
1115
|
-
function isDev() {
|
|
1116
|
-
return Boolean(state.getInitialOptions().apiKey && state.getInitialOptions().apiUrl);
|
|
1117
|
-
}
|
|
1118
|
-
async function addActiveNs(ns, forget) {
|
|
1119
|
-
if (!forget) {
|
|
1120
|
-
state.addActiveNs(ns);
|
|
1121
|
-
}
|
|
1122
|
-
if (state.isRunning()) {
|
|
1123
|
-
await loadRequiredRecords(undefined, ns);
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
function getRequiredRecords(lang, ns) {
|
|
1127
|
-
const languages = state.getFallbackLangs(lang);
|
|
1128
|
-
const namespaces = getRequiredNamespaces(ns);
|
|
1129
|
-
const result = [];
|
|
1130
|
-
languages.forEach((language) => {
|
|
1131
|
-
namespaces.forEach((namespace) => {
|
|
1132
|
-
if (!cache.exists({ language, namespace }, true)) {
|
|
1133
|
-
result.push({ language, namespace });
|
|
1134
|
-
}
|
|
1135
|
-
});
|
|
1136
|
-
});
|
|
1137
|
-
return result;
|
|
1138
|
-
}
|
|
1139
|
-
function isLoaded(ns) {
|
|
1140
|
-
const language = state.getLanguage();
|
|
1141
|
-
if (!language) {
|
|
1142
|
-
return false;
|
|
1143
|
-
}
|
|
1144
|
-
const languages = state.getFallbackLangs(language);
|
|
1145
|
-
const namespaces = getRequiredNamespaces(ns);
|
|
1146
|
-
const result = [];
|
|
1147
|
-
languages.forEach((language) => {
|
|
1148
|
-
namespaces.forEach((namespace) => {
|
|
1149
|
-
if (!cache.exists({ language, namespace })) {
|
|
1150
|
-
result.push({ language, namespace });
|
|
1151
|
-
}
|
|
1152
|
-
});
|
|
1153
|
-
});
|
|
1154
|
-
return result.length === 0;
|
|
1155
|
-
}
|
|
1156
|
-
function loadRequiredRecords(lang, ns) {
|
|
1157
|
-
const descriptors = getRequiredRecords(lang, ns);
|
|
1158
|
-
if (descriptors.length) {
|
|
1159
|
-
return valueOrPromise(loadRecords(descriptors), () => { });
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
async function changeLanguage(language) {
|
|
1163
|
-
if (state.getPendingLanguage() === language &&
|
|
1164
|
-
state.getLanguage() === language) {
|
|
1165
|
-
return;
|
|
1166
|
-
}
|
|
1167
|
-
state.setPendingLanguage(language);
|
|
1168
|
-
if (state.isRunning()) {
|
|
1169
|
-
await loadRequiredRecords(language);
|
|
1170
|
-
}
|
|
1171
|
-
if (language === state.getPendingLanguage()) {
|
|
1172
|
-
// there might be parallel language change
|
|
1173
|
-
// we only want to apply latest
|
|
1174
|
-
state.setLanguage(language);
|
|
1175
|
-
pluginService.setStoredLanguage(language);
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
function getTranslationNs({ key, ns }) {
|
|
1179
|
-
const languages = state.getFallbackLangs();
|
|
1180
|
-
const namespaces = getDefaultAndFallbackNs(ns || undefined);
|
|
1181
|
-
return cache.getTranslationNs(namespaces, languages, key);
|
|
1182
|
-
}
|
|
1183
|
-
function getTranslation({ key, ns }) {
|
|
1184
|
-
const namespaces = getDefaultAndFallbackNs(ns || undefined);
|
|
1185
|
-
const languages = state.getFallbackLangs();
|
|
1186
|
-
return cache.getTranslationFallback(namespaces, languages, key);
|
|
1187
|
-
}
|
|
1188
|
-
function loadInitial() {
|
|
1189
|
-
const data = valueOrPromise(initializeLanguage(), () => {
|
|
1190
|
-
// fail if there is no language
|
|
1191
|
-
return loadRequiredRecords();
|
|
1192
|
-
});
|
|
1193
|
-
if (isPromise(data)) {
|
|
1194
|
-
state.setInitialLoading(true);
|
|
1195
|
-
fetchingObserver.notify();
|
|
1196
|
-
loadingObserver.notify();
|
|
1197
|
-
return Promise.resolve(data).then(() => {
|
|
1198
|
-
state.setInitialLoading(false);
|
|
1199
|
-
fetchingObserver.notify();
|
|
1200
|
-
loadingObserver.notify();
|
|
1201
|
-
events.onInitialLoaded.emit();
|
|
1202
|
-
});
|
|
1203
|
-
}
|
|
1204
|
-
else {
|
|
1205
|
-
events.onInitialLoaded.emit();
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
function initializeLanguage() {
|
|
1209
|
-
const existingLanguage = state.getLanguage();
|
|
1210
|
-
if (existingLanguage) {
|
|
1211
|
-
return;
|
|
1212
|
-
}
|
|
1213
|
-
if (!state.getInitialOptions().defaultLanguage) {
|
|
1214
|
-
throw new Error(missingOptionError('defaultLanguage'));
|
|
1215
|
-
}
|
|
1216
|
-
const languageOrPromise = pluginService.getInitialLanguage();
|
|
1217
|
-
return valueOrPromise(languageOrPromise, (lang) => {
|
|
1218
|
-
const language = lang ||
|
|
1219
|
-
state.getInitialOptions().defaultLanguage;
|
|
1220
|
-
language && state.setLanguage(language);
|
|
1221
|
-
});
|
|
1222
|
-
}
|
|
1223
|
-
async function loadRecord(descriptor) {
|
|
1224
|
-
return (await loadRecords([descriptor]))[0];
|
|
1225
|
-
}
|
|
1226
|
-
function loadRecords(descriptors) {
|
|
1227
|
-
return cache.loadRecords(descriptors, isDev());
|
|
1228
|
-
}
|
|
1229
|
-
const checkCorrectConfiguration = () => {
|
|
1230
|
-
const languageComputable = pluginService.getLanguageDetector() || pluginService.getLanguageStorage();
|
|
1231
|
-
if (languageComputable) {
|
|
1232
|
-
const availableLanguages = state.getAvailableLanguages();
|
|
1233
|
-
if (!availableLanguages) {
|
|
1234
|
-
throw new Error(missingOptionError('availableLanguages'));
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
if (!state.getLanguage() && !state.getInitialOptions().defaultLanguage) {
|
|
1238
|
-
if (languageComputable) {
|
|
1239
|
-
throw new Error(missingOptionError('defaultLanguage'));
|
|
1240
|
-
}
|
|
1241
|
-
else {
|
|
1242
|
-
throw new Error(missingOptionError('language'));
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
};
|
|
1246
|
-
function run() {
|
|
1247
|
-
let result = undefined;
|
|
1248
|
-
checkCorrectConfiguration();
|
|
1249
|
-
if (!state.isRunning()) {
|
|
1250
|
-
if (isDev()) {
|
|
1251
|
-
cache.invalidate();
|
|
1252
|
-
}
|
|
1253
|
-
state.setRunning(true);
|
|
1254
|
-
pluginService.run();
|
|
1255
|
-
result = loadInitial();
|
|
1256
|
-
}
|
|
1257
|
-
return Promise.resolve(result);
|
|
1258
|
-
}
|
|
1259
|
-
function stop() {
|
|
1260
|
-
if (state.isRunning()) {
|
|
1261
|
-
pluginService.stop();
|
|
1262
|
-
state.setRunning(false);
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
const t = (...args) => {
|
|
1266
|
-
// @ts-ignore
|
|
1267
|
-
const params = getTranslateProps(...args);
|
|
1268
|
-
const translation = getTranslation(params);
|
|
1269
|
-
return pluginService.formatTranslation(Object.assign(Object.assign({}, params), { translation }));
|
|
1270
|
-
};
|
|
1271
|
-
return Object.freeze(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, events), state), pluginService), cache), { init,
|
|
1272
|
-
changeLanguage,
|
|
1273
|
-
getTranslation,
|
|
1274
|
-
changeTranslation,
|
|
1275
|
-
addActiveNs,
|
|
1276
|
-
loadRecords,
|
|
1277
|
-
loadRecord,
|
|
1278
|
-
isLoading,
|
|
1279
|
-
isLoaded,
|
|
1280
|
-
t,
|
|
1281
|
-
isDev,
|
|
1282
|
-
run,
|
|
1283
|
-
stop }));
|
|
1284
|
-
};
|
|
1285
|
-
|
|
1286
|
-
const createTolgee = (options) => {
|
|
1287
|
-
const controller = Controller({
|
|
1288
|
-
options,
|
|
1289
|
-
});
|
|
1290
|
-
// restarts tolgee while applying callback
|
|
1291
|
-
const withRestart = (callback) => {
|
|
1292
|
-
const wasRunning = controller.isRunning();
|
|
1293
|
-
wasRunning && controller.stop();
|
|
1294
|
-
callback();
|
|
1295
|
-
wasRunning && controller.run();
|
|
1296
|
-
};
|
|
1297
|
-
const tolgee = Object.freeze({
|
|
1298
|
-
/**
|
|
1299
|
-
* Listen to tolgee events.
|
|
1300
|
-
*/
|
|
1301
|
-
on: controller.on,
|
|
1302
|
-
/**
|
|
1303
|
-
* Listen for specific namespaces changes.
|
|
1304
|
-
*
|
|
1305
|
-
* ```
|
|
1306
|
-
* const sub = tolgee.onUpdate(handler)
|
|
1307
|
-
*
|
|
1308
|
-
* // subscribe to selected namespace
|
|
1309
|
-
* sub.subscribeNs(['common'])
|
|
1310
|
-
*
|
|
1311
|
-
* // unsubscribe
|
|
1312
|
-
* sub.unsubscribe()
|
|
1313
|
-
* ```
|
|
1314
|
-
*/
|
|
1315
|
-
onNsUpdate: controller.onUpdate.listenSome,
|
|
1316
|
-
/**
|
|
1317
|
-
* Turn off/on events emitting. Is on by default.
|
|
1318
|
-
*/
|
|
1319
|
-
setEmmiterActive: controller.setEmmiterActive,
|
|
1320
|
-
/**
|
|
1321
|
-
* @return current language if set.
|
|
1322
|
-
*/
|
|
1323
|
-
getLanguage: controller.getLanguage,
|
|
1324
|
-
/**
|
|
1325
|
-
* `pendingLanguage` represents language which is currently being loaded.
|
|
1326
|
-
* @return current `pendingLanguage` if set.
|
|
1327
|
-
*/
|
|
1328
|
-
getPendingLanguage: controller.getPendingLanguage,
|
|
1329
|
-
/**
|
|
1330
|
-
* Change current language.
|
|
1331
|
-
* - if not running sets `pendingLanguage`, `language` to the new value
|
|
1332
|
-
* - if running sets `pendingLanguage` to the value, fetches necessary data and then changes `language`
|
|
1333
|
-
*
|
|
1334
|
-
* @return Promise which is resolved when `language` is changed.
|
|
1335
|
-
*/
|
|
1336
|
-
changeLanguage: controller.changeLanguage,
|
|
1337
|
-
/**
|
|
1338
|
-
* Temporarily change translation in cache.
|
|
1339
|
-
* @return object with revert method.
|
|
1340
|
-
*/
|
|
1341
|
-
changeTranslation: controller.changeTranslation,
|
|
1342
|
-
/**
|
|
1343
|
-
* Adds namespace(s) list of active namespaces. And if tolgee is running, loads required data.
|
|
1344
|
-
*/
|
|
1345
|
-
addActiveNs: controller.addActiveNs,
|
|
1346
|
-
/**
|
|
1347
|
-
* Remove namespace(s) from active namespaces.
|
|
1348
|
-
*
|
|
1349
|
-
* Tolgee internally counts how many times was each active namespace added,
|
|
1350
|
-
* so this method will remove namespace only if the counter goes down to 0.
|
|
1351
|
-
*/
|
|
1352
|
-
removeActiveNs: controller.removeActiveNs,
|
|
1353
|
-
/**
|
|
1354
|
-
* Manually load multiple records from `Backend` (or `DevBackend` when in dev mode)
|
|
1355
|
-
*
|
|
1356
|
-
* It loads data together and adds them to cache in one operation, to prevent partly loaded state.
|
|
1357
|
-
*/
|
|
1358
|
-
loadRecords: controller.loadRecords,
|
|
1359
|
-
/**
|
|
1360
|
-
* Manually load record from `Backend` (or `DevBackend` when in dev mode)
|
|
1361
|
-
*/
|
|
1362
|
-
loadRecord: controller.loadRecord,
|
|
1363
|
-
/**
|
|
1364
|
-
*
|
|
1365
|
-
*/
|
|
1366
|
-
addStaticData: controller.addStaticData,
|
|
1367
|
-
/**
|
|
1368
|
-
* Get record from cache.
|
|
1369
|
-
*/
|
|
1370
|
-
getRecord: controller.getRecord,
|
|
1371
|
-
/**
|
|
1372
|
-
* Get all records from cache.
|
|
1373
|
-
*/
|
|
1374
|
-
getAllRecords: controller.getAllRecords,
|
|
1375
|
-
/**
|
|
1376
|
-
* @param ns optional list of namespaces that you are interested in
|
|
1377
|
-
* @return `true` if there are data that need to be fetched.
|
|
1378
|
-
*/
|
|
1379
|
-
isLoaded: controller.isLoaded,
|
|
1380
|
-
/**
|
|
1381
|
-
* @return `true` if tolgee is loading initial data (triggered by `run`).
|
|
1382
|
-
*/
|
|
1383
|
-
isInitialLoading: controller.isInitialLoading,
|
|
1384
|
-
/**
|
|
1385
|
-
* @param ns optional list of namespaces that you are interested in
|
|
1386
|
-
* @return `true` if tolgee is loading some translations for the first time.
|
|
1387
|
-
*/
|
|
1388
|
-
isLoading: controller.isLoading,
|
|
1389
|
-
/**
|
|
1390
|
-
* @param ns optional list of namespaces that you are interested in
|
|
1391
|
-
* @return `true` if tolgee is fetching some translations.
|
|
1392
|
-
*/
|
|
1393
|
-
isFetching: controller.isFetching,
|
|
1394
|
-
/**
|
|
1395
|
-
* @return `true` if tolgee is running.
|
|
1396
|
-
*/
|
|
1397
|
-
isRunning: controller.isRunning,
|
|
1398
|
-
/**
|
|
1399
|
-
* Changes internal state to running: true and loads initial files.
|
|
1400
|
-
* Runs runnable plugins mainly Observer if present.
|
|
1401
|
-
*/
|
|
1402
|
-
run: controller.run,
|
|
1403
|
-
/**
|
|
1404
|
-
* Changes internal state to running: false and stops runnable plugins.
|
|
1405
|
-
*/
|
|
1406
|
-
stop: controller.stop,
|
|
1407
|
-
/**
|
|
1408
|
-
* Returns translated and formatted key.
|
|
1409
|
-
* If Observer is present and tolgee is running, wraps result to be identifiable in the DOM.
|
|
1410
|
-
*/
|
|
1411
|
-
t: controller.t,
|
|
1412
|
-
/**
|
|
1413
|
-
* Highlight keys that match selection.
|
|
1414
|
-
*/
|
|
1415
|
-
highlight: controller.highlight,
|
|
1416
|
-
/**
|
|
1417
|
-
* @return current Tolgee options.
|
|
1418
|
-
*/
|
|
1419
|
-
getInitialOptions: controller.getInitialOptions,
|
|
1420
|
-
/**
|
|
1421
|
-
* Tolgee is in dev mode if `DevTools` plugin is used and `apiKey` + `apiUrl` are specified.
|
|
1422
|
-
* @return `true` if tolgee is in dev mode.
|
|
1423
|
-
*/
|
|
1424
|
-
isDev: controller.isDev,
|
|
1425
|
-
/**
|
|
1426
|
-
* Wraps translation if there is `Observer` plugin
|
|
1427
|
-
*/
|
|
1428
|
-
wrap: controller.wrap,
|
|
1429
|
-
/**
|
|
1430
|
-
* Unwrap translation
|
|
1431
|
-
*/
|
|
1432
|
-
unwrap: controller.unwrap,
|
|
1433
|
-
/**
|
|
1434
|
-
* Override creadentials passed on initialization.
|
|
1435
|
-
*
|
|
1436
|
-
* When called in running state, tolgee stops and runs again.
|
|
1437
|
-
*/
|
|
1438
|
-
overrideCredentials(credentials) {
|
|
1439
|
-
withRestart(() => controller.overrideCredentials(credentials));
|
|
1440
|
-
},
|
|
1441
|
-
/**
|
|
1442
|
-
* Add tolgee plugin after initialization.
|
|
1443
|
-
*
|
|
1444
|
-
* When called in running state, tolgee stops and runs again.
|
|
1445
|
-
*/
|
|
1446
|
-
addPlugin(plugin) {
|
|
1447
|
-
if (plugin) {
|
|
1448
|
-
withRestart(() => controller.addPlugin(tolgee, plugin));
|
|
1449
|
-
}
|
|
1450
|
-
},
|
|
1451
|
-
/**
|
|
1452
|
-
* Updates options after instance creation. Extends existing options,
|
|
1453
|
-
* so it only changes the fields, that are listed.
|
|
1454
|
-
*
|
|
1455
|
-
* When called in running state, tolgee stops and runs again.
|
|
1456
|
-
*/
|
|
1457
|
-
updateOptions(options) {
|
|
1458
|
-
if (options) {
|
|
1459
|
-
withRestart(() => controller.init(options));
|
|
1460
|
-
}
|
|
1461
|
-
},
|
|
1462
|
-
});
|
|
1463
|
-
return tolgee;
|
|
1464
|
-
};
|
|
1465
|
-
/**
|
|
1466
|
-
* Tolgee chainable constructor.
|
|
1467
|
-
*
|
|
1468
|
-
* Usage:
|
|
1469
|
-
* ```
|
|
1470
|
-
* const tolgee = Tolgee().use(...).init(...)
|
|
1471
|
-
* ```
|
|
1472
|
-
*/
|
|
1473
|
-
const TolgeeCore = () => {
|
|
1474
|
-
const state = {
|
|
1475
|
-
plugins: [],
|
|
1476
|
-
options: {},
|
|
1477
|
-
};
|
|
1478
|
-
const tolgeeChain = Object.freeze({
|
|
1479
|
-
use(plugin) {
|
|
1480
|
-
state.plugins.push(plugin);
|
|
1481
|
-
return tolgeeChain;
|
|
1482
|
-
},
|
|
1483
|
-
updateDefaults(options) {
|
|
1484
|
-
state.options = combineOptions(state.options, options);
|
|
1485
|
-
return tolgeeChain;
|
|
1486
|
-
},
|
|
1487
|
-
init(options) {
|
|
1488
|
-
const tolgee = createTolgee(combineOptions(state.options, options));
|
|
1489
|
-
state.plugins.forEach(tolgee.addPlugin);
|
|
1490
|
-
return tolgee;
|
|
1491
|
-
},
|
|
1492
|
-
});
|
|
1493
|
-
return tolgeeChain;
|
|
1494
|
-
};
|
|
1495
|
-
|
|
1496
|
-
const ERROR_PARAM_EMPTY = 0, ERROR_UNEXPECTED_CHAR = 1, ERROR_UNEXPECTED_END = 2;
|
|
1497
|
-
class FormatError extends Error {
|
|
1498
|
-
constructor(code, index, text) {
|
|
1499
|
-
let error;
|
|
1500
|
-
if (code === ERROR_PARAM_EMPTY) {
|
|
1501
|
-
error = 'Empty parameter';
|
|
1502
|
-
}
|
|
1503
|
-
else if (code === ERROR_UNEXPECTED_CHAR) {
|
|
1504
|
-
error = 'Unexpected character';
|
|
1505
|
-
}
|
|
1506
|
-
else {
|
|
1507
|
-
error = 'Unexpected end';
|
|
1508
|
-
}
|
|
1509
|
-
super(`Tolgee parser: ${error} at ${index} in "${text}"`);
|
|
1510
|
-
this.code = code;
|
|
1511
|
-
this.index = index;
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
|
-
function isWhitespace(ch) {
|
|
1516
|
-
return /\s/.test(ch);
|
|
1517
|
-
}
|
|
1518
|
-
const STATE_TEXT = 0, STATE_ESCAPE_MAYBE = 1, STATE_ESCAPE = 2, STATE_PARAM = 3, STATE_PARAM_AFTER = 4;
|
|
1519
|
-
const END_STATES = new Set([
|
|
1520
|
-
STATE_ESCAPE,
|
|
1521
|
-
STATE_ESCAPE_MAYBE,
|
|
1522
|
-
STATE_TEXT,
|
|
1523
|
-
]);
|
|
1524
|
-
const CHAR_ESCAPE = "'";
|
|
1525
|
-
const ESCAPABLE = new Set(['{', '}', CHAR_ESCAPE]);
|
|
1526
|
-
const isAllowedInParam = (char) => {
|
|
1527
|
-
return /[0-9a-zA-Z_]/.test(char);
|
|
1528
|
-
};
|
|
1529
|
-
function formatParser(translation) {
|
|
1530
|
-
let state = STATE_TEXT;
|
|
1531
|
-
let text = '';
|
|
1532
|
-
let param = '';
|
|
1533
|
-
let ch = '';
|
|
1534
|
-
const texts = [];
|
|
1535
|
-
const params = [];
|
|
1536
|
-
let i = 0;
|
|
1537
|
-
function parsingError(code) {
|
|
1538
|
-
throw new FormatError(code, i, translation);
|
|
1539
|
-
}
|
|
1540
|
-
const addText = () => {
|
|
1541
|
-
texts.push(text);
|
|
1542
|
-
text = '';
|
|
1543
|
-
};
|
|
1544
|
-
const addParamChar = () => {
|
|
1545
|
-
if (!isAllowedInParam(ch)) {
|
|
1546
|
-
parsingError(ERROR_UNEXPECTED_CHAR);
|
|
1547
|
-
}
|
|
1548
|
-
param += ch;
|
|
1549
|
-
};
|
|
1550
|
-
const addParam = () => {
|
|
1551
|
-
if (param === '') {
|
|
1552
|
-
parsingError(ERROR_PARAM_EMPTY);
|
|
1553
|
-
}
|
|
1554
|
-
params.push(param);
|
|
1555
|
-
param = '';
|
|
1556
|
-
};
|
|
1557
|
-
for (i = 0; i < translation.length; i++) {
|
|
1558
|
-
ch = translation[i];
|
|
1559
|
-
switch (state) {
|
|
1560
|
-
case STATE_TEXT:
|
|
1561
|
-
if (ch === CHAR_ESCAPE) {
|
|
1562
|
-
text += ch;
|
|
1563
|
-
state = STATE_ESCAPE_MAYBE;
|
|
1564
|
-
}
|
|
1565
|
-
else if (ch === '{') {
|
|
1566
|
-
addText();
|
|
1567
|
-
state = STATE_PARAM;
|
|
1568
|
-
}
|
|
1569
|
-
else {
|
|
1570
|
-
text += ch;
|
|
1571
|
-
state = STATE_TEXT;
|
|
1572
|
-
}
|
|
1573
|
-
break;
|
|
1574
|
-
case STATE_ESCAPE_MAYBE:
|
|
1575
|
-
if (ESCAPABLE.has(ch)) {
|
|
1576
|
-
text = text.slice(0, -1) + ch;
|
|
1577
|
-
state = STATE_ESCAPE;
|
|
1578
|
-
}
|
|
1579
|
-
else {
|
|
1580
|
-
text += ch;
|
|
1581
|
-
state = STATE_TEXT;
|
|
1582
|
-
}
|
|
1583
|
-
break;
|
|
1584
|
-
case STATE_ESCAPE:
|
|
1585
|
-
if (ch === CHAR_ESCAPE) {
|
|
1586
|
-
state = STATE_TEXT;
|
|
1587
|
-
}
|
|
1588
|
-
else {
|
|
1589
|
-
text += ch;
|
|
1590
|
-
state = STATE_ESCAPE;
|
|
1591
|
-
}
|
|
1592
|
-
break;
|
|
1593
|
-
case STATE_PARAM:
|
|
1594
|
-
if (ch === '}') {
|
|
1595
|
-
addParam();
|
|
1596
|
-
state = STATE_TEXT;
|
|
1597
|
-
}
|
|
1598
|
-
else if (!isWhitespace(ch)) {
|
|
1599
|
-
addParamChar();
|
|
1600
|
-
state = STATE_PARAM;
|
|
1601
|
-
}
|
|
1602
|
-
else if (param !== '') {
|
|
1603
|
-
addParam();
|
|
1604
|
-
state = STATE_PARAM_AFTER;
|
|
1605
|
-
}
|
|
1606
|
-
break;
|
|
1607
|
-
case STATE_PARAM_AFTER:
|
|
1608
|
-
if (ch == '}') {
|
|
1609
|
-
state = STATE_TEXT;
|
|
1610
|
-
}
|
|
1611
|
-
else if (isWhitespace(ch)) {
|
|
1612
|
-
state = STATE_PARAM_AFTER;
|
|
1613
|
-
}
|
|
1614
|
-
else {
|
|
1615
|
-
parsingError(ERROR_UNEXPECTED_CHAR);
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1619
|
-
if (!END_STATES.has(state)) {
|
|
1620
|
-
parsingError(ERROR_UNEXPECTED_END);
|
|
1621
|
-
}
|
|
1622
|
-
addText();
|
|
1623
|
-
return [texts, params];
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
function formatter(translation, params) {
|
|
1627
|
-
const [texts, pars] = formatParser(translation);
|
|
1628
|
-
const result = [texts[0]];
|
|
1629
|
-
for (let i = 1; i < texts.length; i++) {
|
|
1630
|
-
const parameter = params === null || params === void 0 ? void 0 : params[pars[i - 1]];
|
|
1631
|
-
if (parameter === undefined) {
|
|
1632
|
-
throw new Error(`Missing parameter "${pars[i - 1]}" in "${translation}"`);
|
|
1633
|
-
}
|
|
1634
|
-
result.push(String(parameter));
|
|
1635
|
-
result.push(texts[i]);
|
|
1636
|
-
}
|
|
1637
|
-
return result.join('');
|
|
1638
|
-
}
|
|
1639
|
-
|
|
1640
|
-
function createFormatSimple() {
|
|
1641
|
-
return {
|
|
1642
|
-
format: ({ translation, params }) => formatter(translation, params),
|
|
1643
|
-
};
|
|
1644
|
-
}
|
|
1645
|
-
const FormatSimple = () => (tolgee, tools) => {
|
|
1646
|
-
tools.setFinalFormatter(createFormatSimple());
|
|
1647
|
-
return tolgee;
|
|
1648
|
-
};
|
|
1649
|
-
|
|
1650
|
-
const HIGHLIGHTER_BASE_STYLE = {
|
|
1651
|
-
pointerEvents: 'none',
|
|
1652
|
-
position: 'fixed',
|
|
1653
|
-
boxSizing: 'content-box',
|
|
1654
|
-
zIndex: String(Number.MAX_SAFE_INTEGER),
|
|
1655
|
-
contain: 'layout',
|
|
1656
|
-
display: 'block',
|
|
1657
|
-
borderStyle: 'solid',
|
|
1658
|
-
borderRadius: '4px',
|
|
1659
|
-
};
|
|
1660
|
-
const ElementHighlighter = ({ highlightColor, highlightWidth, }) => {
|
|
1661
|
-
function initHighlightFunction(element, elementMeta) {
|
|
1662
|
-
elementMeta.highlight = () => {
|
|
1663
|
-
if (!element.isConnected) {
|
|
1664
|
-
return;
|
|
1665
|
-
}
|
|
1666
|
-
let highlightEl = elementMeta.highlightEl;
|
|
1667
|
-
if (!highlightEl) {
|
|
1668
|
-
highlightEl = document.createElement('div');
|
|
1669
|
-
highlightEl.classList.add(TOLGEE_HIGHLIGHTER_CLASS);
|
|
1670
|
-
Object.entries(HIGHLIGHTER_BASE_STYLE).forEach(([key, value]) => {
|
|
1671
|
-
// @ts-ignore
|
|
1672
|
-
highlightEl.style[key] = value;
|
|
1673
|
-
});
|
|
1674
|
-
highlightEl.style.borderColor = highlightColor;
|
|
1675
|
-
elementMeta.highlightEl = highlightEl;
|
|
1676
|
-
document.body.appendChild(highlightEl);
|
|
1677
|
-
}
|
|
1678
|
-
const shape = element.getBoundingClientRect();
|
|
1679
|
-
highlightEl.style.borderWidth = highlightWidth + 'px';
|
|
1680
|
-
highlightEl.style.top = shape.top - highlightWidth + 'px';
|
|
1681
|
-
highlightEl.style.left = shape.left - highlightWidth + 'px';
|
|
1682
|
-
highlightEl.style.width = shape.width + 'px';
|
|
1683
|
-
highlightEl.style.height = shape.height + 'px';
|
|
1684
|
-
};
|
|
1685
|
-
}
|
|
1686
|
-
function initUnhighlightFunction(element, elementMeta) {
|
|
1687
|
-
elementMeta.unhighlight = () => {
|
|
1688
|
-
var _a;
|
|
1689
|
-
(_a = elementMeta.highlightEl) === null || _a === void 0 ? void 0 : _a.remove();
|
|
1690
|
-
elementMeta.highlightEl = undefined;
|
|
1691
|
-
};
|
|
1692
|
-
}
|
|
1693
|
-
function initHighlighter(element, elementMeta) {
|
|
1694
|
-
initHighlightFunction(element, elementMeta);
|
|
1695
|
-
initUnhighlightFunction(element, elementMeta);
|
|
1696
|
-
}
|
|
1697
|
-
return Object.freeze({ initHighlighter });
|
|
1698
|
-
};
|
|
1699
|
-
|
|
1700
|
-
const ElementStore = () => {
|
|
1701
|
-
const registredElements = new Map();
|
|
1702
|
-
function set(el, meta) {
|
|
1703
|
-
registredElements.set(el, meta);
|
|
1704
|
-
}
|
|
1705
|
-
function get(el) {
|
|
1706
|
-
return el && registredElements.get(el);
|
|
1707
|
-
}
|
|
1708
|
-
function remove(el) {
|
|
1709
|
-
return registredElements.delete(el);
|
|
1710
|
-
}
|
|
1711
|
-
function forEachElement(callback) {
|
|
1712
|
-
registredElements.forEach((value, key) => callback(key, value));
|
|
1713
|
-
}
|
|
1714
|
-
return Object.freeze({
|
|
1715
|
-
set,
|
|
1716
|
-
get,
|
|
1717
|
-
remove,
|
|
1718
|
-
forEachElement,
|
|
1719
|
-
});
|
|
1720
|
-
};
|
|
1721
|
-
|
|
1722
|
-
function xPathEvaluate(expression, targetNode) {
|
|
1723
|
-
var _a;
|
|
1724
|
-
const searchableElement = closestElement(targetNode);
|
|
1725
|
-
const result = [];
|
|
1726
|
-
if (!searchableElement) {
|
|
1727
|
-
return result;
|
|
1728
|
-
}
|
|
1729
|
-
const evaluated = document === null || document === void 0 ? void 0 : document.evaluate(expression, searchableElement, undefined, XPathResult.ANY_TYPE);
|
|
1730
|
-
let node;
|
|
1731
|
-
while ((node = (_a = evaluated === null || evaluated === void 0 ? void 0 : evaluated.iterateNext) === null || _a === void 0 ? void 0 : _a.call(evaluated))) {
|
|
1732
|
-
result.push(node);
|
|
1733
|
-
}
|
|
1734
|
-
return result;
|
|
1735
|
-
}
|
|
1736
|
-
function closestElement(node) {
|
|
1737
|
-
switch (node.nodeType) {
|
|
1738
|
-
case Node.ATTRIBUTE_NODE:
|
|
1739
|
-
return node.ownerElement || undefined;
|
|
1740
|
-
case Node.TEXT_NODE:
|
|
1741
|
-
return node.parentElement || undefined;
|
|
1742
|
-
case Node.DOCUMENT_NODE:
|
|
1743
|
-
case Node.ELEMENT_NODE:
|
|
1744
|
-
return node;
|
|
1745
|
-
default:
|
|
1746
|
-
// we are not interested in other nodes
|
|
1747
|
-
return undefined;
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
function getNodeText(node) {
|
|
1751
|
-
return node.textContent;
|
|
1752
|
-
}
|
|
1753
|
-
function setNodeText(node, text) {
|
|
1754
|
-
node.textContent = text;
|
|
1755
|
-
}
|
|
1756
|
-
function nodeContains(descendant, node) {
|
|
1757
|
-
if (descendant.contains(node)) {
|
|
1758
|
-
return true;
|
|
1759
|
-
}
|
|
1760
|
-
if (node instanceof Attr) {
|
|
1761
|
-
const ownerContainsAttr = node.ownerElement &&
|
|
1762
|
-
Object.values(node.ownerElement.attributes).indexOf(node) > -1;
|
|
1763
|
-
if (descendant.contains(node.ownerElement) && ownerContainsAttr) {
|
|
1764
|
-
return true;
|
|
1765
|
-
}
|
|
1766
|
-
}
|
|
1767
|
-
return false;
|
|
1768
|
-
}
|
|
1769
|
-
const compareDescriptors = (descriptor, criteria) => {
|
|
1770
|
-
var _a;
|
|
1771
|
-
const keyMatches = descriptor.key === undefined ||
|
|
1772
|
-
criteria.key === undefined ||
|
|
1773
|
-
criteria.key === descriptor.key;
|
|
1774
|
-
const nsMatches = descriptor.ns === undefined ||
|
|
1775
|
-
criteria.ns === undefined ||
|
|
1776
|
-
((_a = descriptor.ns) === null || _a === void 0 ? void 0 : _a.findIndex((ns) => { var _a; return (_a = criteria.ns) === null || _a === void 0 ? void 0 : _a.includes(ns); })) !== -1;
|
|
1777
|
-
return keyMatches && nsMatches;
|
|
1778
|
-
};
|
|
1779
|
-
|
|
1780
|
-
const eCapture = {
|
|
1781
|
-
capture: true,
|
|
1782
|
-
};
|
|
1783
|
-
const ePassive = {
|
|
1784
|
-
capture: true,
|
|
1785
|
-
passive: true,
|
|
1786
|
-
};
|
|
1787
|
-
const MouseEventHandler = ({ highlightKeys, elementStore, onClick, }) => {
|
|
1788
|
-
let keysDown = new Set();
|
|
1789
|
-
let highlighted;
|
|
1790
|
-
// let mouseOnChanged = new EventEmitterImpl<Element>();
|
|
1791
|
-
// let keysChanged: EventEmitterImpl<boolean> =
|
|
1792
|
-
// new EventEmitterImpl<boolean>();
|
|
1793
|
-
let cursorPosition;
|
|
1794
|
-
const highlight = (el) => {
|
|
1795
|
-
var _a;
|
|
1796
|
-
if (highlighted !== el) {
|
|
1797
|
-
unhighlight();
|
|
1798
|
-
const meta = elementStore.get(el);
|
|
1799
|
-
if (meta) {
|
|
1800
|
-
meta.preventClean = true;
|
|
1801
|
-
(_a = meta.highlight) === null || _a === void 0 ? void 0 : _a.call(meta);
|
|
1802
|
-
highlighted = el;
|
|
1803
|
-
// mouseOnChanged.emit(el);
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1806
|
-
};
|
|
1807
|
-
const unhighlight = () => {
|
|
1808
|
-
var _a;
|
|
1809
|
-
const meta = elementStore.get(highlighted);
|
|
1810
|
-
if (meta) {
|
|
1811
|
-
meta.preventClean = false;
|
|
1812
|
-
(_a = meta.unhighlight) === null || _a === void 0 ? void 0 : _a.call(meta);
|
|
1813
|
-
highlighted = undefined;
|
|
1814
|
-
// mouseOnChanged.emit(highlighted);
|
|
1815
|
-
}
|
|
1816
|
-
};
|
|
1817
|
-
function updateHighlight() {
|
|
1818
|
-
const position = cursorPosition;
|
|
1819
|
-
let newHighlighted;
|
|
1820
|
-
if (position && areKeysDown()) {
|
|
1821
|
-
const element = document.elementFromPoint(position.x, position.y);
|
|
1822
|
-
if (element) {
|
|
1823
|
-
newHighlighted = getClosestTolgeeElement(element);
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
highlight(newHighlighted);
|
|
1827
|
-
}
|
|
1828
|
-
function updateCursorPosition(position) {
|
|
1829
|
-
cursorPosition = position;
|
|
1830
|
-
updateHighlight();
|
|
1831
|
-
}
|
|
1832
|
-
const blockEvents = (e) => {
|
|
1833
|
-
if (areKeysDown() && !isInUiDialog(e.target)) {
|
|
1834
|
-
e.stopPropagation();
|
|
1835
|
-
e.preventDefault();
|
|
1836
|
-
}
|
|
1837
|
-
};
|
|
1838
|
-
const onMouseMove = (e) => {
|
|
1839
|
-
updateCursorPosition({ x: e.clientX, y: e.clientY });
|
|
1840
|
-
};
|
|
1841
|
-
const onBlur = () => {
|
|
1842
|
-
keysDown = new Set();
|
|
1843
|
-
// keysChanged.emit(areKeysDown());
|
|
1844
|
-
updateHighlight();
|
|
1845
|
-
};
|
|
1846
|
-
const onKeyDown = (e) => {
|
|
1847
|
-
const modifierKey = e.key;
|
|
1848
|
-
if (modifierKey !== undefined) {
|
|
1849
|
-
keysDown.add(modifierKey);
|
|
1850
|
-
// keysChanged.emit(areKeysDown());
|
|
1851
|
-
}
|
|
1852
|
-
updateHighlight();
|
|
1853
|
-
};
|
|
1854
|
-
const onKeyUp = (e) => {
|
|
1855
|
-
keysDown.delete(e.key);
|
|
1856
|
-
// keysChanged.emit(areKeysDown());
|
|
1857
|
-
updateHighlight();
|
|
1858
|
-
};
|
|
1859
|
-
const onScroll = () => {
|
|
1860
|
-
var _a;
|
|
1861
|
-
const meta = elementStore.get(highlighted);
|
|
1862
|
-
(_a = meta === null || meta === void 0 ? void 0 : meta.highlight) === null || _a === void 0 ? void 0 : _a.call(meta);
|
|
1863
|
-
};
|
|
1864
|
-
const handleClick = (e) => {
|
|
1865
|
-
blockEvents(e);
|
|
1866
|
-
if (areKeysDown()) {
|
|
1867
|
-
const element = getClosestTolgeeElement(e.target);
|
|
1868
|
-
if (element && element === highlighted) {
|
|
1869
|
-
onClick(e, element);
|
|
1870
|
-
unhighlight();
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
};
|
|
1874
|
-
function initEventListeners() {
|
|
1875
|
-
window.addEventListener('blur', onBlur, eCapture);
|
|
1876
|
-
window.addEventListener('keydown', onKeyDown, eCapture);
|
|
1877
|
-
window.addEventListener('keyup', onKeyUp, eCapture);
|
|
1878
|
-
window.addEventListener('mousemove', onMouseMove, ePassive);
|
|
1879
|
-
window.addEventListener('scroll', onScroll, ePassive);
|
|
1880
|
-
window.addEventListener('click', handleClick, eCapture);
|
|
1881
|
-
window.addEventListener('mouseenter', blockEvents, eCapture);
|
|
1882
|
-
window.addEventListener('mouseover', blockEvents, eCapture);
|
|
1883
|
-
window.addEventListener('mouseout', blockEvents, eCapture);
|
|
1884
|
-
window.addEventListener('mouseleave', blockEvents, eCapture);
|
|
1885
|
-
window.addEventListener('mousedown', blockEvents, eCapture);
|
|
1886
|
-
window.addEventListener('mouseup', blockEvents, eCapture);
|
|
1887
|
-
}
|
|
1888
|
-
function removeEventListeners() {
|
|
1889
|
-
window.removeEventListener('blur', onBlur, eCapture);
|
|
1890
|
-
window.removeEventListener('keydown', onKeyDown, eCapture);
|
|
1891
|
-
window.removeEventListener('keyup', onKeyUp, eCapture);
|
|
1892
|
-
window.removeEventListener('mousemove', onMouseMove, ePassive);
|
|
1893
|
-
window.removeEventListener('scroll', onScroll, ePassive);
|
|
1894
|
-
window.removeEventListener('click', handleClick, eCapture);
|
|
1895
|
-
window.removeEventListener('mouseenter', blockEvents, eCapture);
|
|
1896
|
-
window.removeEventListener('mouseover', blockEvents, eCapture);
|
|
1897
|
-
window.removeEventListener('mouseout', blockEvents, eCapture);
|
|
1898
|
-
window.removeEventListener('mouseleave', blockEvents, eCapture);
|
|
1899
|
-
window.removeEventListener('mousedown', blockEvents, eCapture);
|
|
1900
|
-
window.removeEventListener('mouseup', blockEvents, eCapture);
|
|
1901
|
-
}
|
|
1902
|
-
function isInUiDialog(element) {
|
|
1903
|
-
return Boolean(findAncestor(element, (el) => el.id === DEVTOOLS_ID));
|
|
1904
|
-
}
|
|
1905
|
-
function getClosestTolgeeElement(element) {
|
|
1906
|
-
return findAncestor(element, (el) => elementStore.get(el));
|
|
1907
|
-
}
|
|
1908
|
-
function findAncestor(element, func) {
|
|
1909
|
-
if (func(element)) {
|
|
1910
|
-
return element;
|
|
1911
|
-
}
|
|
1912
|
-
if (element === null || element === void 0 ? void 0 : element.parentElement) {
|
|
1913
|
-
return findAncestor(element.parentElement, func);
|
|
1914
|
-
}
|
|
1915
|
-
return undefined;
|
|
1916
|
-
}
|
|
1917
|
-
function areKeysDown() {
|
|
1918
|
-
for (const key of highlightKeys) {
|
|
1919
|
-
if (!keysDown.has(key)) {
|
|
1920
|
-
return false;
|
|
1921
|
-
}
|
|
1922
|
-
}
|
|
1923
|
-
return true;
|
|
1924
|
-
}
|
|
1925
|
-
function stop() {
|
|
1926
|
-
removeEventListeners();
|
|
1927
|
-
}
|
|
1928
|
-
function run() {
|
|
1929
|
-
initEventListeners();
|
|
1930
|
-
}
|
|
1931
|
-
return Object.freeze({
|
|
1932
|
-
run,
|
|
1933
|
-
stop,
|
|
1934
|
-
});
|
|
1935
|
-
};
|
|
1936
|
-
|
|
1937
|
-
const ElementRegistry = (options, onClick) => {
|
|
1938
|
-
const elementStore = ElementStore();
|
|
1939
|
-
const elementHighlighter = ElementHighlighter({
|
|
1940
|
-
highlightColor: options.highlightColor,
|
|
1941
|
-
highlightWidth: options.highlightWidth,
|
|
1942
|
-
});
|
|
1943
|
-
const eventHandler = MouseEventHandler({
|
|
1944
|
-
highlightKeys: options.highlightKeys,
|
|
1945
|
-
elementStore,
|
|
1946
|
-
onClick: (event, el) => {
|
|
1947
|
-
const meta = elementStore.get(el);
|
|
1948
|
-
onClick({
|
|
1949
|
-
event,
|
|
1950
|
-
keysAndDefaults: getKeysAndDefaults(meta),
|
|
1951
|
-
});
|
|
1952
|
-
},
|
|
1953
|
-
});
|
|
1954
|
-
function register(element, node, nodeMeta) {
|
|
1955
|
-
if (isRestricted(element)) {
|
|
1956
|
-
return;
|
|
1957
|
-
}
|
|
1958
|
-
const tolgeeElement = element;
|
|
1959
|
-
let elementMeta = elementStore.get(tolgeeElement);
|
|
1960
|
-
if (!elementMeta) {
|
|
1961
|
-
elementMeta = initElementMeta();
|
|
1962
|
-
elementStore.set(tolgeeElement, elementMeta);
|
|
1963
|
-
tolgeeElement.setAttribute(TOLGEE_ATTRIBUTE_NAME, 'true');
|
|
1964
|
-
}
|
|
1965
|
-
elementMeta.nodes.set(node, nodeMeta);
|
|
1966
|
-
elementHighlighter.initHighlighter(tolgeeElement, elementMeta);
|
|
1967
|
-
}
|
|
1968
|
-
function run(mouseHighlight) {
|
|
1969
|
-
if (mouseHighlight) {
|
|
1970
|
-
eventHandler.run();
|
|
1971
|
-
}
|
|
1972
|
-
}
|
|
1973
|
-
function stop() {
|
|
1974
|
-
eventHandler.stop();
|
|
1975
|
-
}
|
|
1976
|
-
function isRestricted(element) {
|
|
1977
|
-
const restrictedElements = options.restrictedElements;
|
|
1978
|
-
return (restrictedElements.indexOf(element.tagName.toLowerCase()) !== -1 ||
|
|
1979
|
-
element.closest(`[${TOLGEE_RESTRICT_ATTRIBUTE}]`) !== null);
|
|
1980
|
-
}
|
|
1981
|
-
function refreshAll() {
|
|
1982
|
-
elementStore.forEachElement((element, meta) => {
|
|
1983
|
-
if (meta.preventClean) {
|
|
1984
|
-
return;
|
|
1985
|
-
}
|
|
1986
|
-
cleanElementInactiveNodes(meta);
|
|
1987
|
-
if (meta.nodes.size === 0) {
|
|
1988
|
-
cleanElement(element, meta);
|
|
1989
|
-
}
|
|
1990
|
-
});
|
|
1991
|
-
}
|
|
1992
|
-
function findAll(key, ns) {
|
|
1993
|
-
const result = [];
|
|
1994
|
-
elementStore.forEachElement((_, meta) => {
|
|
1995
|
-
for (const nodeMeta of meta.nodes.values()) {
|
|
1996
|
-
const fits = nodeMeta.keys.find((val) => compareDescriptors({ key, ns: getFallback(ns) }, { key: val.key, ns: getFallback(val.ns) }));
|
|
1997
|
-
if (fits) {
|
|
1998
|
-
result.push(meta);
|
|
1999
|
-
break;
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
});
|
|
2003
|
-
return result;
|
|
2004
|
-
}
|
|
2005
|
-
function cleanElementInactiveNodes(meta) {
|
|
2006
|
-
meta.nodes = new Map(getActiveNodes(meta));
|
|
2007
|
-
}
|
|
2008
|
-
function getTargetElement() {
|
|
2009
|
-
return options.targetElement || document.body;
|
|
2010
|
-
}
|
|
2011
|
-
function* getActiveNodes(meta) {
|
|
2012
|
-
for (const [node, nodeMeta] of meta.nodes.entries()) {
|
|
2013
|
-
if (nodeContains(getTargetElement(), node)) {
|
|
2014
|
-
yield [node, nodeMeta];
|
|
2015
|
-
}
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
function cleanElement(element, meta) {
|
|
2019
|
-
var _a;
|
|
2020
|
-
if (meta.highlightEl) {
|
|
2021
|
-
(_a = meta.unhighlight) === null || _a === void 0 ? void 0 : _a.call(meta);
|
|
2022
|
-
}
|
|
2023
|
-
element.removeAttribute(TOLGEE_ATTRIBUTE_NAME);
|
|
2024
|
-
elementStore.remove(element);
|
|
2025
|
-
}
|
|
2026
|
-
function getKeyOptions(meta) {
|
|
2027
|
-
const nodes = Array.from(meta.nodes.values());
|
|
2028
|
-
return nodes.reduce((acc, curr) => [
|
|
2029
|
-
...acc,
|
|
2030
|
-
...curr.keys.map((k) => ({
|
|
2031
|
-
key: k.key,
|
|
2032
|
-
defaultValue: k.defaultValue,
|
|
2033
|
-
ns: k.ns,
|
|
2034
|
-
})),
|
|
2035
|
-
], []);
|
|
2036
|
-
}
|
|
2037
|
-
function getKeysAndDefaults(meta) {
|
|
2038
|
-
return getKeyOptions(meta);
|
|
2039
|
-
}
|
|
2040
|
-
return Object.freeze({
|
|
2041
|
-
register,
|
|
2042
|
-
forEachElement: elementStore.forEachElement,
|
|
2043
|
-
findAll,
|
|
2044
|
-
refreshAll,
|
|
2045
|
-
run,
|
|
2046
|
-
stop,
|
|
2047
|
-
});
|
|
2048
|
-
};
|
|
2049
|
-
|
|
2050
|
-
const NodeHandler = (options, wrapper) => {
|
|
2051
|
-
const handleText = (node) => {
|
|
2052
|
-
const xPath = wrapper.getTextXPath();
|
|
2053
|
-
const nodes = xPathEvaluate(xPath, node);
|
|
2054
|
-
return nodes;
|
|
2055
|
-
};
|
|
2056
|
-
const handleAttributes = (node) => {
|
|
2057
|
-
let result = [];
|
|
2058
|
-
for (const [tag, attributes] of Object.entries(options.tagAttributes)) {
|
|
2059
|
-
for (const attribute of attributes) {
|
|
2060
|
-
const expression = wrapper.getAttributeXPath({ tag, attribute });
|
|
2061
|
-
const nodes = xPathEvaluate(expression, node);
|
|
2062
|
-
result = [...result, ...nodes];
|
|
2063
|
-
}
|
|
2064
|
-
}
|
|
2065
|
-
return result;
|
|
2066
|
-
};
|
|
2067
|
-
const handleChildList = (node) => {
|
|
2068
|
-
let result = [];
|
|
2069
|
-
result = result.concat(handleAttributes(node));
|
|
2070
|
-
result = result.concat(handleText(node));
|
|
2071
|
-
// wrappedHandler(node);
|
|
2072
|
-
return result;
|
|
2073
|
-
};
|
|
2074
|
-
return Object.freeze({
|
|
2075
|
-
handleAttributes,
|
|
2076
|
-
handleChildList,
|
|
2077
|
-
handleText,
|
|
2078
|
-
});
|
|
2079
|
-
};
|
|
2080
|
-
|
|
2081
|
-
const GeneralObserver = (wrapper, options, onClick) => {
|
|
2082
|
-
let isObserving = false;
|
|
2083
|
-
const domHelper = DomHelper(options);
|
|
2084
|
-
const nodeHandler = NodeHandler(options, wrapper);
|
|
2085
|
-
const elementRegistry = ElementRegistry(options, onClick);
|
|
2086
|
-
function handleNodes(nodes) {
|
|
2087
|
-
for (const textNode of nodes) {
|
|
2088
|
-
const oldTextContent = getNodeText(textNode);
|
|
2089
|
-
const result = oldTextContent ? wrapper.unwrap(oldTextContent) : null;
|
|
2090
|
-
if (result) {
|
|
2091
|
-
const { text, keys } = result;
|
|
2092
|
-
setNodeText(textNode, text);
|
|
2093
|
-
const nodeMeta = initNodeMeta(oldTextContent, keys);
|
|
2094
|
-
const parentElement = domHelper.getSuitableParent(textNode);
|
|
2095
|
-
elementRegistry.register(parentElement, textNode, nodeMeta);
|
|
2096
|
-
}
|
|
2097
|
-
}
|
|
2098
|
-
}
|
|
2099
|
-
const handleKeyAttribute = (node) => {
|
|
2100
|
-
const xPath = `./descendant-or-self::*[@${TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE}]`;
|
|
2101
|
-
const elements = xPathEvaluate(xPath, node);
|
|
2102
|
-
elements.forEach((element) => {
|
|
2103
|
-
const node = element.getAttributeNode(TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE);
|
|
2104
|
-
const parentElement = domHelper.getSuitableParent(node);
|
|
2105
|
-
elementRegistry.register(parentElement, node, {
|
|
2106
|
-
oldTextContent: '',
|
|
2107
|
-
keys: [{ key: getNodeText(node) }],
|
|
2108
|
-
keyAttributeOnly: true,
|
|
2109
|
-
});
|
|
2110
|
-
});
|
|
2111
|
-
};
|
|
2112
|
-
const createMutationObserver = () => {
|
|
2113
|
-
return new MutationObserver((mutationsList) => {
|
|
2114
|
-
if (!isObserving) {
|
|
2115
|
-
return;
|
|
2116
|
-
}
|
|
2117
|
-
for (const mutation of mutationsList) {
|
|
2118
|
-
let result = [];
|
|
2119
|
-
switch (mutation.type) {
|
|
2120
|
-
case 'characterData':
|
|
2121
|
-
result = nodeHandler.handleText(mutation.target);
|
|
2122
|
-
break;
|
|
2123
|
-
case 'childList':
|
|
2124
|
-
handleKeyAttribute(mutation.target);
|
|
2125
|
-
result = nodeHandler.handleChildList(mutation.target);
|
|
2126
|
-
break;
|
|
2127
|
-
case 'attributes':
|
|
2128
|
-
handleKeyAttribute(mutation.target);
|
|
2129
|
-
result = nodeHandler.handleAttributes(mutation.target);
|
|
2130
|
-
break;
|
|
2131
|
-
}
|
|
2132
|
-
handleNodes(result);
|
|
2133
|
-
elementRegistry.refreshAll();
|
|
2134
|
-
}
|
|
2135
|
-
});
|
|
2136
|
-
};
|
|
2137
|
-
let observer;
|
|
2138
|
-
const run = ({ mouseHighlight }) => {
|
|
2139
|
-
if (isSSR()) {
|
|
2140
|
-
return;
|
|
2141
|
-
}
|
|
2142
|
-
if (!observer) {
|
|
2143
|
-
observer = createMutationObserver();
|
|
2144
|
-
}
|
|
2145
|
-
const targetElement = options.targetElement || document.body;
|
|
2146
|
-
isObserving = true;
|
|
2147
|
-
elementRegistry.run(mouseHighlight);
|
|
2148
|
-
// initially go through all elements
|
|
2149
|
-
handleKeyAttribute(targetElement);
|
|
2150
|
-
handleNodes(nodeHandler.handleChildList(targetElement));
|
|
2151
|
-
// then observe for changes
|
|
2152
|
-
observer.observe(options.targetElement || document.body, {
|
|
2153
|
-
attributes: true,
|
|
2154
|
-
childList: true,
|
|
2155
|
-
subtree: true,
|
|
2156
|
-
characterData: true,
|
|
2157
|
-
});
|
|
2158
|
-
};
|
|
2159
|
-
const stop = () => {
|
|
2160
|
-
isObserving = false;
|
|
2161
|
-
elementRegistry.stop();
|
|
2162
|
-
observer.disconnect();
|
|
2163
|
-
};
|
|
2164
|
-
const highlight = (key, ns) => {
|
|
2165
|
-
const elements = elementRegistry.findAll(key, ns);
|
|
2166
|
-
elements.forEach((el) => { var _a; return (_a = el.highlight) === null || _a === void 0 ? void 0 : _a.call(el); });
|
|
2167
|
-
return {
|
|
2168
|
-
unhighlight() {
|
|
2169
|
-
elements.forEach((el) => { var _a; return (_a = el.unhighlight) === null || _a === void 0 ? void 0 : _a.call(el); });
|
|
2170
|
-
},
|
|
2171
|
-
};
|
|
2172
|
-
};
|
|
2173
|
-
return Object.freeze({
|
|
2174
|
-
run,
|
|
2175
|
-
stop,
|
|
2176
|
-
wrap: wrapper.wrap,
|
|
2177
|
-
unwrap: wrapper.unwrap,
|
|
2178
|
-
forEachElement: elementRegistry.forEachElement,
|
|
2179
|
-
highlight,
|
|
2180
|
-
});
|
|
2181
|
-
};
|
|
2182
|
-
|
|
2183
|
-
// TextEncoder/TextDecoder polyfills for utf-8 - an implementation of TextEncoder/TextDecoder APIs
|
|
2184
|
-
// Written in 2013 by Viktor Mukhachev <vic99999@yandex.ru>
|
|
2185
|
-
// To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.
|
|
2186
|
-
// You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
2187
|
-
// Some important notes about the polyfill below:
|
|
2188
|
-
// Native TextEncoder/TextDecoder implementation is overwritten
|
|
2189
|
-
// String.prototype.codePointAt polyfill not included, as well as String.fromCodePoint
|
|
2190
|
-
// TextEncoder.prototype.encode returns a regular array instead of Uint8Array
|
|
2191
|
-
// No options (fatal of the TextDecoder constructor and stream of the TextDecoder.prototype.decode method) are supported.
|
|
2192
|
-
// TextDecoder.prototype.decode does not valid byte sequences
|
|
2193
|
-
// This is a demonstrative implementation not intended to have the best performance
|
|
2194
|
-
// http://encoding.spec.whatwg.org/#textencoder
|
|
2195
|
-
// http://encoding.spec.whatwg.org/#textencoder
|
|
2196
|
-
function PTextEncoder() { }
|
|
2197
|
-
PTextEncoder.prototype.encode = function (string) {
|
|
2198
|
-
const octets = [];
|
|
2199
|
-
const length = string.length;
|
|
2200
|
-
let i = 0;
|
|
2201
|
-
while (i < length) {
|
|
2202
|
-
const codePoint = string.codePointAt(i);
|
|
2203
|
-
let c = 0;
|
|
2204
|
-
let bits = 0;
|
|
2205
|
-
if (codePoint <= 0x0000007f) {
|
|
2206
|
-
c = 0;
|
|
2207
|
-
bits = 0x00;
|
|
2208
|
-
}
|
|
2209
|
-
else if (codePoint <= 0x000007ff) {
|
|
2210
|
-
c = 6;
|
|
2211
|
-
bits = 0xc0;
|
|
2212
|
-
}
|
|
2213
|
-
else if (codePoint <= 0x0000ffff) {
|
|
2214
|
-
c = 12;
|
|
2215
|
-
bits = 0xe0;
|
|
2216
|
-
}
|
|
2217
|
-
else if (codePoint <= 0x001fffff) {
|
|
2218
|
-
c = 18;
|
|
2219
|
-
bits = 0xf0;
|
|
2220
|
-
}
|
|
2221
|
-
octets.push(bits | (codePoint >> c));
|
|
2222
|
-
c -= 6;
|
|
2223
|
-
while (c >= 0) {
|
|
2224
|
-
octets.push(0x80 | ((codePoint >> c) & 0x3f));
|
|
2225
|
-
c -= 6;
|
|
2226
|
-
}
|
|
2227
|
-
i += codePoint >= 0x10000 ? 2 : 1;
|
|
2228
|
-
}
|
|
2229
|
-
return octets;
|
|
2230
|
-
};
|
|
2231
|
-
function PTextDecoder() { }
|
|
2232
|
-
PTextDecoder.prototype.decode = function (octets) {
|
|
2233
|
-
let string = '';
|
|
2234
|
-
let i = 0;
|
|
2235
|
-
while (i < octets.length) {
|
|
2236
|
-
let octet = octets[i];
|
|
2237
|
-
let bytesNeeded = 0;
|
|
2238
|
-
let codePoint = 0;
|
|
2239
|
-
if (octet <= 0x7f) {
|
|
2240
|
-
bytesNeeded = 0;
|
|
2241
|
-
codePoint = octet & 0xff;
|
|
2242
|
-
}
|
|
2243
|
-
else if (octet <= 0xdf) {
|
|
2244
|
-
bytesNeeded = 1;
|
|
2245
|
-
codePoint = octet & 0x1f;
|
|
2246
|
-
}
|
|
2247
|
-
else if (octet <= 0xef) {
|
|
2248
|
-
bytesNeeded = 2;
|
|
2249
|
-
codePoint = octet & 0x0f;
|
|
2250
|
-
}
|
|
2251
|
-
else if (octet <= 0xf4) {
|
|
2252
|
-
bytesNeeded = 3;
|
|
2253
|
-
codePoint = octet & 0x07;
|
|
2254
|
-
}
|
|
2255
|
-
if (octets.length - i - bytesNeeded > 0) {
|
|
2256
|
-
let k = 0;
|
|
2257
|
-
while (k < bytesNeeded) {
|
|
2258
|
-
octet = octets[i + k + 1];
|
|
2259
|
-
codePoint = (codePoint << 6) | (octet & 0x3f);
|
|
2260
|
-
k += 1;
|
|
2261
|
-
}
|
|
2262
|
-
}
|
|
2263
|
-
else {
|
|
2264
|
-
codePoint = 0xfffd;
|
|
2265
|
-
bytesNeeded = octets.length - i;
|
|
2266
|
-
}
|
|
2267
|
-
string += String.fromCodePoint(codePoint);
|
|
2268
|
-
i += bytesNeeded + 1;
|
|
2269
|
-
}
|
|
2270
|
-
return string;
|
|
2271
|
-
};
|
|
2272
|
-
const Encoder = (typeof TextEncoder === 'undefined'
|
|
2273
|
-
? PTextEncoder
|
|
2274
|
-
: TextEncoder);
|
|
2275
|
-
const Decoder = (typeof TextDecoder === 'undefined'
|
|
2276
|
-
? PTextDecoder
|
|
2277
|
-
: TextDecoder);
|
|
2278
|
-
|
|
2279
|
-
const INVISIBLE_CHARACTERS = ['\u200C', '\u200D'];
|
|
2280
|
-
const INVISIBLE_REGEX = RegExp(`([${INVISIBLE_CHARACTERS.join('')}]{9})+`, 'gu');
|
|
2281
|
-
const toBytes = (text) => {
|
|
2282
|
-
return Array.from(new Encoder().encode(text));
|
|
2283
|
-
};
|
|
2284
|
-
const fromBytes = (bytes) => {
|
|
2285
|
-
return new Decoder().decode(new Uint8Array(bytes));
|
|
2286
|
-
};
|
|
2287
|
-
const padToWholeBytes = (binary) => {
|
|
2288
|
-
const needsToAdd = 8 - binary.length;
|
|
2289
|
-
return '0'.repeat(needsToAdd) + binary;
|
|
2290
|
-
};
|
|
2291
|
-
const encodeMessage = (text) => {
|
|
2292
|
-
const bytes = toBytes(text).map(Number);
|
|
2293
|
-
const binary = bytes
|
|
2294
|
-
.map((byte) => padToWholeBytes(byte.toString(2)) + '0')
|
|
2295
|
-
.join('');
|
|
2296
|
-
const result = Array.from(binary)
|
|
2297
|
-
.map((b) => INVISIBLE_CHARACTERS[Number(b)])
|
|
2298
|
-
.join('');
|
|
2299
|
-
return result;
|
|
2300
|
-
};
|
|
2301
|
-
const decodeMessage = (message) => {
|
|
2302
|
-
const binary = Array.from(message)
|
|
2303
|
-
.map((character) => {
|
|
2304
|
-
return INVISIBLE_CHARACTERS.indexOf(character);
|
|
2305
|
-
})
|
|
2306
|
-
.map(String)
|
|
2307
|
-
.join('');
|
|
2308
|
-
const textBytes = binary.match(/(.{9})/g);
|
|
2309
|
-
const codes = Uint8Array.from((textBytes === null || textBytes === void 0 ? void 0 : textBytes.map((byte) => parseInt(byte.slice(0, 8), 2))) || []);
|
|
2310
|
-
return fromBytes(codes);
|
|
2311
|
-
};
|
|
2312
|
-
const decodeFromText = (text) => {
|
|
2313
|
-
var _a;
|
|
2314
|
-
const invisibleMessages = (_a = text
|
|
2315
|
-
.match(INVISIBLE_REGEX)) === null || _a === void 0 ? void 0 : _a.filter((m) => m.length > 8);
|
|
2316
|
-
return (invisibleMessages === null || invisibleMessages === void 0 ? void 0 : invisibleMessages.map(decodeMessage)) || [];
|
|
2317
|
-
};
|
|
2318
|
-
const removeSecrets = (text) => {
|
|
2319
|
-
return text.replace(INVISIBLE_REGEX, '');
|
|
2320
|
-
};
|
|
2321
|
-
const stringToCodePoints = (text) => {
|
|
2322
|
-
const result = [];
|
|
2323
|
-
for (const codePoint of text) {
|
|
2324
|
-
result.push(codePoint.codePointAt(0));
|
|
2325
|
-
}
|
|
2326
|
-
return result;
|
|
2327
|
-
};
|
|
2328
|
-
|
|
2329
|
-
const ValueMemory = () => {
|
|
2330
|
-
const values = [];
|
|
2331
|
-
const valueToNumber = (key) => {
|
|
2332
|
-
let index = values.indexOf(key);
|
|
2333
|
-
if (index === -1) {
|
|
2334
|
-
index = values.length;
|
|
2335
|
-
values.push(key);
|
|
2336
|
-
}
|
|
2337
|
-
return index;
|
|
2338
|
-
};
|
|
2339
|
-
const numberToValue = (num) => {
|
|
2340
|
-
return values[num];
|
|
2341
|
-
};
|
|
2342
|
-
return Object.freeze({ valueToNumber, numberToValue });
|
|
2343
|
-
};
|
|
2344
|
-
|
|
2345
|
-
const InvisibleWrapper = () => {
|
|
2346
|
-
const keyMemory = ValueMemory();
|
|
2347
|
-
const unwrap = (text) => {
|
|
2348
|
-
const keysAndParams = [];
|
|
2349
|
-
const messages = decodeFromText(text);
|
|
2350
|
-
messages.forEach((msg) => {
|
|
2351
|
-
const [valueCode] = stringToCodePoints(msg);
|
|
2352
|
-
const encodedValue = keyMemory.numberToValue(valueCode);
|
|
2353
|
-
const { k: key, d: defaultValue, n: ns } = decodeValue(encodedValue);
|
|
2354
|
-
keysAndParams.push({
|
|
2355
|
-
key,
|
|
2356
|
-
defaultValue,
|
|
2357
|
-
ns,
|
|
2358
|
-
});
|
|
2359
|
-
});
|
|
2360
|
-
const result = removeSecrets(text);
|
|
2361
|
-
return { text: result, keys: keysAndParams };
|
|
2362
|
-
};
|
|
2363
|
-
const encodeValue = (data) => {
|
|
2364
|
-
const value = {
|
|
2365
|
-
k: data.key,
|
|
2366
|
-
n: data.ns || undefined,
|
|
2367
|
-
d: data.defaultValue,
|
|
2368
|
-
};
|
|
2369
|
-
return JSON.stringify(value);
|
|
2370
|
-
};
|
|
2371
|
-
const decodeValue = (value) => {
|
|
2372
|
-
return JSON.parse(value);
|
|
2373
|
-
};
|
|
2374
|
-
const wrap = ({ key, defaultValue, translation, ns, }) => {
|
|
2375
|
-
const encodedValue = encodeValue({ key, ns, defaultValue });
|
|
2376
|
-
const code = keyMemory.valueToNumber(encodedValue);
|
|
2377
|
-
const value = translation || '';
|
|
2378
|
-
const invisibleMark = encodeMessage(String.fromCodePoint(code));
|
|
2379
|
-
return typeof value === 'string' ? value + invisibleMark : value;
|
|
2380
|
-
};
|
|
2381
|
-
const getTextXPath = () => {
|
|
2382
|
-
return `./descendant-or-self::text()[contains(., '${INVISIBLE_CHARACTERS[0]}${INVISIBLE_CHARACTERS[0]}') or contains(., '${INVISIBLE_CHARACTERS[1]}${INVISIBLE_CHARACTERS[0]}')]`;
|
|
2383
|
-
};
|
|
2384
|
-
const getAttributeXPath = ({ tag, attribute, }) => {
|
|
2385
|
-
return `descendant-or-self::${tag}/@${attribute}[contains(., '${INVISIBLE_CHARACTERS[0]}${INVISIBLE_CHARACTERS[0]}') or contains(., '${INVISIBLE_CHARACTERS[1]}${INVISIBLE_CHARACTERS[0]}')]`;
|
|
2386
|
-
};
|
|
2387
|
-
return Object.freeze({
|
|
2388
|
-
unwrap,
|
|
2389
|
-
wrap,
|
|
2390
|
-
getTextXPath,
|
|
2391
|
-
getAttributeXPath,
|
|
2392
|
-
});
|
|
2393
|
-
};
|
|
2394
|
-
|
|
2395
|
-
const InvisibleObserver = () => ({ onClick, options }) => {
|
|
2396
|
-
const wrapper = InvisibleWrapper();
|
|
2397
|
-
const observer = GeneralObserver(wrapper, options, onClick);
|
|
2398
|
-
return Object.assign(Object.assign({}, observer), { retranslate: () => { }, outputNotFormattable: false });
|
|
2399
|
-
};
|
|
2400
|
-
|
|
2401
|
-
function isCharEscaped(position, fullString) {
|
|
2402
|
-
let escapeCharsCount = 0;
|
|
2403
|
-
while (position > -1 && fullString[position - 1] === '\\') {
|
|
2404
|
-
escapeCharsCount++;
|
|
2405
|
-
position--;
|
|
2406
|
-
}
|
|
2407
|
-
return escapeCharsCount % 2 == 1;
|
|
2408
|
-
}
|
|
2409
|
-
|
|
2410
|
-
const TextWrapper = ({ inputPrefix, inputSuffix, translate, }) => {
|
|
2411
|
-
function getRawUnWrapRegex() {
|
|
2412
|
-
const escapedPrefix = escapeForRegExp(inputPrefix);
|
|
2413
|
-
const escapedSuffix = escapeForRegExp(inputSuffix);
|
|
2414
|
-
return `(\\\\?)(${escapedPrefix}(.*?)${escapedSuffix})`;
|
|
2415
|
-
}
|
|
2416
|
-
function parseUnwrapped(unwrappedString) {
|
|
2417
|
-
let escaped = false;
|
|
2418
|
-
let actual = '';
|
|
2419
|
-
let paramName = '';
|
|
2420
|
-
let readingState = 'KEY';
|
|
2421
|
-
const result = {
|
|
2422
|
-
key: '',
|
|
2423
|
-
ns: undefined,
|
|
2424
|
-
params: {},
|
|
2425
|
-
defaultValue: undefined,
|
|
2426
|
-
};
|
|
2427
|
-
const addNamespace = (ns) => {
|
|
2428
|
-
result.ns = ns;
|
|
2429
|
-
};
|
|
2430
|
-
for (const char of unwrappedString) {
|
|
2431
|
-
if (char === '\\' && !escaped) {
|
|
2432
|
-
escaped = true;
|
|
2433
|
-
continue;
|
|
2434
|
-
}
|
|
2435
|
-
if (escaped) {
|
|
2436
|
-
escaped = false;
|
|
2437
|
-
actual += char;
|
|
2438
|
-
continue;
|
|
2439
|
-
}
|
|
2440
|
-
if (readingState === 'KEY' && char === ',') {
|
|
2441
|
-
readingState = 'DEFAULT_VALUE';
|
|
2442
|
-
result.key = actual;
|
|
2443
|
-
actual = '';
|
|
2444
|
-
continue;
|
|
2445
|
-
}
|
|
2446
|
-
if (readingState === 'KEY' && char === '|') {
|
|
2447
|
-
readingState = 'NAMESPACE';
|
|
2448
|
-
result.key = actual;
|
|
2449
|
-
actual = '';
|
|
2450
|
-
continue;
|
|
2451
|
-
}
|
|
2452
|
-
if (readingState === 'NAMESPACE' && char === ',') {
|
|
2453
|
-
readingState = 'DEFAULT_VALUE';
|
|
2454
|
-
addNamespace(actual);
|
|
2455
|
-
actual = '';
|
|
2456
|
-
continue;
|
|
2457
|
-
}
|
|
2458
|
-
if (readingState === 'KEY' && char === ':') {
|
|
2459
|
-
readingState = 'PARAM_NAME';
|
|
2460
|
-
result.key = actual;
|
|
2461
|
-
actual = '';
|
|
2462
|
-
continue;
|
|
2463
|
-
}
|
|
2464
|
-
if (readingState === 'DEFAULT_VALUE' && char === ':') {
|
|
2465
|
-
readingState = 'PARAM_NAME';
|
|
2466
|
-
result.defaultValue = actual;
|
|
2467
|
-
actual = '';
|
|
2468
|
-
continue;
|
|
2469
|
-
}
|
|
2470
|
-
if (readingState === 'PARAM_NAME' && char === ':') {
|
|
2471
|
-
readingState = 'PARAM_VALUE';
|
|
2472
|
-
paramName = actual;
|
|
2473
|
-
actual = '';
|
|
2474
|
-
continue;
|
|
2475
|
-
}
|
|
2476
|
-
if (readingState === 'PARAM_VALUE' && char === ',') {
|
|
2477
|
-
readingState = 'PARAM_NAME';
|
|
2478
|
-
result.params[paramName] = actual;
|
|
2479
|
-
actual = '';
|
|
2480
|
-
continue;
|
|
2481
|
-
}
|
|
2482
|
-
actual += char;
|
|
2483
|
-
}
|
|
2484
|
-
if (readingState === 'KEY') {
|
|
2485
|
-
result.key = actual;
|
|
2486
|
-
}
|
|
2487
|
-
if (readingState === 'DEFAULT_VALUE') {
|
|
2488
|
-
result.defaultValue = actual;
|
|
2489
|
-
}
|
|
2490
|
-
if (readingState === 'PARAM_VALUE') {
|
|
2491
|
-
result.params[paramName] = actual;
|
|
2492
|
-
}
|
|
2493
|
-
if (readingState === 'NAMESPACE') {
|
|
2494
|
-
addNamespace(actual);
|
|
2495
|
-
}
|
|
2496
|
-
return result;
|
|
2497
|
-
}
|
|
2498
|
-
const unwrap = (text) => {
|
|
2499
|
-
const matchRegexp = new RegExp(getRawUnWrapRegex(), 'gs');
|
|
2500
|
-
const keysAndParams = [];
|
|
2501
|
-
let matched = false;
|
|
2502
|
-
let match;
|
|
2503
|
-
let start = 0;
|
|
2504
|
-
let result = '';
|
|
2505
|
-
while ((match = matchRegexp.exec(text)) !== null) {
|
|
2506
|
-
let pre = match[1];
|
|
2507
|
-
const [fullMatch, _, wrapped, unwrapped] = match;
|
|
2508
|
-
const { index, input } = match;
|
|
2509
|
-
result += input.substr(start, index - start);
|
|
2510
|
-
start = index + fullMatch.length;
|
|
2511
|
-
if (pre === '\\') {
|
|
2512
|
-
if (!isCharEscaped(index, text)) {
|
|
2513
|
-
result += wrapped;
|
|
2514
|
-
continue;
|
|
2515
|
-
}
|
|
2516
|
-
pre = '';
|
|
2517
|
-
}
|
|
2518
|
-
const translated = getTranslatedWithMetadata(unwrapped);
|
|
2519
|
-
keysAndParams.push({
|
|
2520
|
-
key: translated.key,
|
|
2521
|
-
params: translated.params,
|
|
2522
|
-
defaultValue: translated.defaultValue,
|
|
2523
|
-
ns: translated.ns,
|
|
2524
|
-
});
|
|
2525
|
-
matched = true;
|
|
2526
|
-
result += pre + translated.translated;
|
|
2527
|
-
}
|
|
2528
|
-
result += text.substring(start);
|
|
2529
|
-
if (matched) {
|
|
2530
|
-
return { text: result, keys: keysAndParams };
|
|
2531
|
-
}
|
|
2532
|
-
return { text: text, keys: [] };
|
|
2533
|
-
};
|
|
2534
|
-
const wrap = ({ key, params, defaultValue, ns, }) => {
|
|
2535
|
-
let paramString = Object.entries(params || {})
|
|
2536
|
-
.map(([name, value]) => `${escapeParam(name)}:${escapeParam(value)}`)
|
|
2537
|
-
.join(',');
|
|
2538
|
-
paramString = paramString.length ? `:${paramString}` : '';
|
|
2539
|
-
const defaultString = defaultValue !== undefined ? `,${escapeParam(defaultValue)}` : '';
|
|
2540
|
-
const nsArray = typeof ns === 'string' ? [ns] : ns;
|
|
2541
|
-
const namespaces = (nsArray === null || nsArray === void 0 ? void 0 : nsArray.length)
|
|
2542
|
-
? `|${nsArray.map((ns) => escapeParam(ns)).join('|')}`
|
|
2543
|
-
: '';
|
|
2544
|
-
return `${inputPrefix}${escapeParam(key)}${namespaces}${defaultString}${paramString}${inputSuffix}`;
|
|
2545
|
-
};
|
|
2546
|
-
function getTranslatedWithMetadata(text) {
|
|
2547
|
-
const { key, params, defaultValue, ns } = parseUnwrapped(text);
|
|
2548
|
-
const translated = translate({
|
|
2549
|
-
key,
|
|
2550
|
-
params,
|
|
2551
|
-
defaultValue,
|
|
2552
|
-
ns,
|
|
2553
|
-
noWrap: true,
|
|
2554
|
-
});
|
|
2555
|
-
return { translated, key, params, defaultValue, ns };
|
|
2556
|
-
}
|
|
2557
|
-
const escapeForRegExp = (string) => {
|
|
2558
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
2559
|
-
};
|
|
2560
|
-
const escapeParam = (param) => {
|
|
2561
|
-
if (typeof param === 'string') {
|
|
2562
|
-
return param.replace(/[,:|\\]/gs, '\\$&');
|
|
2563
|
-
}
|
|
2564
|
-
if (typeof param === 'number' || typeof param === 'bigint') {
|
|
2565
|
-
return param.toString();
|
|
2566
|
-
}
|
|
2567
|
-
// eslint-disable-next-line no-console
|
|
2568
|
-
console.warn(`Parameters of type "${typeof param}" are not supported in "text" wrapper mode.`);
|
|
2569
|
-
return param;
|
|
2570
|
-
};
|
|
2571
|
-
const getTextXPath = () => {
|
|
2572
|
-
return `./descendant-or-self::text()[contains(., '${inputPrefix}') and contains(., '${inputSuffix}')]`;
|
|
2573
|
-
};
|
|
2574
|
-
const getAttributeXPath = ({ tag, attribute, }) => {
|
|
2575
|
-
return `descendant-or-self::${tag}/@${attribute}[contains(., '${inputPrefix}') and contains(., '${inputSuffix}')]`;
|
|
2576
|
-
};
|
|
2577
|
-
return Object.freeze({
|
|
2578
|
-
wrap,
|
|
2579
|
-
unwrap,
|
|
2580
|
-
getTextXPath,
|
|
2581
|
-
getAttributeXPath,
|
|
2582
|
-
});
|
|
2583
|
-
};
|
|
2584
|
-
|
|
2585
|
-
const TextObserver = () => ({ translate, onClick, options }) => {
|
|
2586
|
-
const wrapper = TextWrapper({
|
|
2587
|
-
inputPrefix: options.inputPrefix,
|
|
2588
|
-
inputSuffix: options.inputSuffix,
|
|
2589
|
-
translate,
|
|
2590
|
-
});
|
|
2591
|
-
const { wrap, unwrap, stop, forEachElement, highlight, run } = GeneralObserver(wrapper, options, onClick);
|
|
2592
|
-
const retranslate = () => {
|
|
2593
|
-
forEachElement((_, elMeta) => {
|
|
2594
|
-
for (const [node, nodeMeta] of elMeta.nodes.entries()) {
|
|
2595
|
-
if (nodeMeta.keyAttributeOnly) {
|
|
2596
|
-
return;
|
|
2597
|
-
}
|
|
2598
|
-
const result = wrapper.unwrap(nodeMeta.oldTextContent);
|
|
2599
|
-
if (result) {
|
|
2600
|
-
setNodeText(node, result.text);
|
|
2601
|
-
}
|
|
2602
|
-
}
|
|
2603
|
-
});
|
|
2604
|
-
};
|
|
2605
|
-
return {
|
|
2606
|
-
wrap,
|
|
2607
|
-
unwrap,
|
|
2608
|
-
stop,
|
|
2609
|
-
run,
|
|
2610
|
-
retranslate,
|
|
2611
|
-
highlight,
|
|
2612
|
-
outputNotFormattable: true,
|
|
2613
|
-
};
|
|
2614
|
-
};
|
|
2615
|
-
|
|
2616
|
-
const ObserverPlugin = () => (tolgee, tools) => {
|
|
2617
|
-
if (tolgee.getInitialOptions().observerType === 'text') {
|
|
2618
|
-
tools.setObserver(TextObserver());
|
|
2619
|
-
}
|
|
2620
|
-
else {
|
|
2621
|
-
tools.setObserver(InvisibleObserver());
|
|
2622
|
-
}
|
|
2623
|
-
return tolgee;
|
|
2624
|
-
};
|
|
2625
|
-
|
|
2626
|
-
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
|
2627
|
-
function readChar(char) {
|
|
2628
|
-
const idx = alphabet.indexOf(char);
|
|
2629
|
-
if (idx === -1) {
|
|
2630
|
-
throw new Error('Invalid character found: ' + char);
|
|
2631
|
-
}
|
|
2632
|
-
return idx;
|
|
2633
|
-
}
|
|
2634
|
-
function arrayBufferToString(buffer) {
|
|
2635
|
-
const bufView = new Uint8Array(buffer);
|
|
2636
|
-
const length = bufView.length;
|
|
2637
|
-
let result = '';
|
|
2638
|
-
let addition = Math.pow(2, 16) - 1;
|
|
2639
|
-
for (let i = 0; i < length; i += addition) {
|
|
2640
|
-
if (i + addition > length) {
|
|
2641
|
-
addition = length - i;
|
|
2642
|
-
}
|
|
2643
|
-
result += String.fromCharCode.apply(null,
|
|
2644
|
-
// @ts-ignore
|
|
2645
|
-
bufView.subarray(i, i + addition));
|
|
2646
|
-
}
|
|
2647
|
-
return result;
|
|
2648
|
-
}
|
|
2649
|
-
function base32Decode(input) {
|
|
2650
|
-
input = input.toUpperCase();
|
|
2651
|
-
const length = input.length;
|
|
2652
|
-
let bits = 0;
|
|
2653
|
-
let value = 0;
|
|
2654
|
-
let index = 0;
|
|
2655
|
-
const output = new Uint8Array(((length * 5) / 8) | 0);
|
|
2656
|
-
for (let i = 0; i < length; i++) {
|
|
2657
|
-
value = (value << 5) | readChar(input[i]);
|
|
2658
|
-
bits += 5;
|
|
2659
|
-
if (bits >= 8) {
|
|
2660
|
-
output[index++] = (value >>> (bits - 8)) & 255;
|
|
2661
|
-
bits -= 8;
|
|
2662
|
-
}
|
|
2663
|
-
}
|
|
2664
|
-
return arrayBufferToString(output.buffer);
|
|
2665
|
-
}
|
|
2666
|
-
function getProjectIdFromApiKey(key) {
|
|
2667
|
-
if (!key) {
|
|
2668
|
-
return undefined;
|
|
2669
|
-
}
|
|
2670
|
-
try {
|
|
2671
|
-
const [prefix, rest] = key.split('_');
|
|
2672
|
-
if (prefix === 'tgpak') {
|
|
2673
|
-
const [projectId] = base32Decode(rest).split('_');
|
|
2674
|
-
return Number(projectId);
|
|
2675
|
-
}
|
|
2676
|
-
}
|
|
2677
|
-
catch (_a) {
|
|
2678
|
-
// eslint-disable-next-line no-console
|
|
2679
|
-
console.warn("Tolgee: Api key can't be parsed");
|
|
2680
|
-
}
|
|
2681
|
-
return undefined;
|
|
2682
|
-
}
|
|
2683
|
-
function getApiKeyType(key) {
|
|
2684
|
-
if (!key) {
|
|
2685
|
-
return undefined;
|
|
2686
|
-
}
|
|
2687
|
-
const [prefix] = key.split('_');
|
|
2688
|
-
if (prefix === 'tgpak') {
|
|
2689
|
-
return 'tgpak';
|
|
2690
|
-
}
|
|
2691
|
-
else if (prefix === 'tgpat') {
|
|
2692
|
-
return 'tgpat';
|
|
2693
|
-
}
|
|
2694
|
-
return 'legacy';
|
|
2695
|
-
}
|
|
2696
|
-
|
|
2697
|
-
const createDevBackend = () => ({
|
|
2698
|
-
getRecord({ apiUrl, apiKey, language, namespace, projectId }) {
|
|
2699
|
-
var _a;
|
|
2700
|
-
const pId = (_a = getProjectIdFromApiKey(apiKey)) !== null && _a !== void 0 ? _a : projectId;
|
|
2701
|
-
let url = pId !== undefined
|
|
2702
|
-
? `${apiUrl}/v2/projects/${pId}/translations/${language}`
|
|
2703
|
-
: `${apiUrl}/v2/projects/translations/${language}`;
|
|
2704
|
-
if (namespace) {
|
|
2705
|
-
url += `?ns=${namespace}`;
|
|
2706
|
-
}
|
|
2707
|
-
if (getApiKeyType(apiKey) === 'tgpat' && projectId === undefined) {
|
|
2708
|
-
throw new Error("You need to specify 'projectId' when using PAT key");
|
|
2709
|
-
}
|
|
2710
|
-
return fetch(url, {
|
|
2711
|
-
headers: {
|
|
2712
|
-
'X-API-Key': apiKey || '',
|
|
2713
|
-
'Content-Type': 'application/json',
|
|
2714
|
-
},
|
|
2715
|
-
}).then((r) => {
|
|
2716
|
-
if (r.ok) {
|
|
2717
|
-
return r.json().then((data) => data[language]);
|
|
2718
|
-
}
|
|
2719
|
-
else {
|
|
2720
|
-
throw new Error(r.statusText);
|
|
2721
|
-
}
|
|
2722
|
-
});
|
|
2723
|
-
},
|
|
2724
|
-
});
|
|
2725
|
-
const DevBackend = () => (tolgee, tools) => {
|
|
2726
|
-
tools.setDevBackend(createDevBackend());
|
|
2727
|
-
return tolgee;
|
|
2728
|
-
};
|
|
2729
|
-
|
|
2730
|
-
function listen(type, callback) {
|
|
2731
|
-
const handler = (e) => {
|
|
2732
|
-
var _a, _b;
|
|
2733
|
-
if (type.includes((_a = e.data) === null || _a === void 0 ? void 0 : _a.type)) {
|
|
2734
|
-
callback((_b = e.data) === null || _b === void 0 ? void 0 : _b.data);
|
|
2735
|
-
}
|
|
2736
|
-
};
|
|
2737
|
-
window.addEventListener('message', handler, false);
|
|
2738
|
-
return {
|
|
2739
|
-
unsubscribe: () => {
|
|
2740
|
-
window.removeEventListener('message', handler);
|
|
2741
|
-
},
|
|
2742
|
-
};
|
|
2743
|
-
}
|
|
2744
|
-
function sendAndRecieve({ message, recievingMessage, data, attempts = 1, }) {
|
|
2745
|
-
let cancelled = false;
|
|
2746
|
-
const makeAttempt = () => new Promise((resolve, reject) => {
|
|
2747
|
-
const listener = listen(recievingMessage, handler);
|
|
2748
|
-
window.postMessage({ type: message, data }, window.origin);
|
|
2749
|
-
const timer = setTimeout(timeout, 300);
|
|
2750
|
-
function handler(data) {
|
|
2751
|
-
clearTimeout(timer);
|
|
2752
|
-
removeEventListener();
|
|
2753
|
-
resolve(data);
|
|
2754
|
-
}
|
|
2755
|
-
function removeEventListener() {
|
|
2756
|
-
listener.unsubscribe();
|
|
2757
|
-
}
|
|
2758
|
-
function timeout() {
|
|
2759
|
-
removeEventListener();
|
|
2760
|
-
reject();
|
|
2761
|
-
}
|
|
2762
|
-
});
|
|
2763
|
-
const getData = async () => {
|
|
2764
|
-
for (let i = 0; i < attempts; i++) {
|
|
2765
|
-
if (cancelled) {
|
|
2766
|
-
return new Promise(() => { });
|
|
2767
|
-
}
|
|
2768
|
-
try {
|
|
2769
|
-
const result = await makeAttempt();
|
|
2770
|
-
return result;
|
|
2771
|
-
}
|
|
2772
|
-
catch (e) {
|
|
2773
|
-
continue;
|
|
2774
|
-
}
|
|
2775
|
-
}
|
|
2776
|
-
if (!cancelled) {
|
|
2777
|
-
throw `Didn't recieve ${recievingMessage.join(' or ')} in time.`;
|
|
2778
|
-
}
|
|
2779
|
-
return new Promise(() => { });
|
|
2780
|
-
};
|
|
2781
|
-
return {
|
|
2782
|
-
cancel: () => (cancelled = true),
|
|
2783
|
-
promise: getData(),
|
|
2784
|
-
};
|
|
2785
|
-
}
|
|
2786
|
-
function Handshaker() {
|
|
2787
|
-
let cancelLast = undefined;
|
|
2788
|
-
async function update(data) {
|
|
2789
|
-
cancelLast === null || cancelLast === void 0 ? void 0 : cancelLast();
|
|
2790
|
-
const { cancel, promise } = sendAndRecieve({
|
|
2791
|
-
message: 'TOLGEE_READY',
|
|
2792
|
-
recievingMessage: ['TOLGEE_PLUGIN_READY', 'TOLGEE_PLUGIN_UPDATED'],
|
|
2793
|
-
data,
|
|
2794
|
-
attempts: 4,
|
|
2795
|
-
});
|
|
2796
|
-
cancelLast = cancel;
|
|
2797
|
-
return promise;
|
|
2798
|
-
}
|
|
2799
|
-
return {
|
|
2800
|
-
update,
|
|
2801
|
-
};
|
|
2802
|
-
}
|
|
2803
|
-
|
|
2804
|
-
const IN_CONTEXT_FILE = 'tolgee-in-context-tools.umd.min.js';
|
|
2805
|
-
const IN_CONTEXT_UMD_NAME = '@tolgee/in-context-tools';
|
|
2806
|
-
const IN_CONTEXT_EXPORT_NAME = 'InContextTools';
|
|
2807
|
-
|
|
2808
|
-
const CDN_URL = 'https://cdn.jsdelivr.net/npm';
|
|
2809
|
-
function injectScript(src) {
|
|
2810
|
-
return new Promise((resolve, reject) => {
|
|
2811
|
-
const script = document.createElement('script');
|
|
2812
|
-
script.src = src;
|
|
2813
|
-
script.addEventListener('load', () => resolve());
|
|
2814
|
-
script.addEventListener('error', (e) => reject(e.error));
|
|
2815
|
-
document.body.appendChild(script);
|
|
2816
|
-
});
|
|
2817
|
-
}
|
|
2818
|
-
let injectPromise = null;
|
|
2819
|
-
function loadInContextLib(version) {
|
|
2820
|
-
if (!injectPromise) {
|
|
2821
|
-
injectPromise = injectScript(`${CDN_URL}/@tolgee/web@${version || 'rc'}/dist/${IN_CONTEXT_FILE}`).then(() => {
|
|
2822
|
-
// @ts-ignore
|
|
2823
|
-
return window[IN_CONTEXT_UMD_NAME][IN_CONTEXT_EXPORT_NAME];
|
|
2824
|
-
});
|
|
2825
|
-
}
|
|
2826
|
-
return injectPromise;
|
|
2827
|
-
}
|
|
2828
|
-
|
|
2829
|
-
const API_KEY_LOCAL_STORAGE = '__tolgee_apiKey';
|
|
2830
|
-
const API_URL_LOCAL_STORAGE = '__tolgee_apiUrl';
|
|
2831
|
-
function getCredentials() {
|
|
2832
|
-
const apiKey = sessionStorage.getItem(API_KEY_LOCAL_STORAGE) || undefined;
|
|
2833
|
-
const apiUrl = sessionStorage.getItem(API_URL_LOCAL_STORAGE) || undefined;
|
|
2834
|
-
if (!apiKey || !apiUrl) {
|
|
2835
|
-
return undefined;
|
|
2836
|
-
}
|
|
2837
|
-
return {
|
|
2838
|
-
apiKey,
|
|
2839
|
-
apiUrl,
|
|
2840
|
-
};
|
|
2841
|
-
}
|
|
2842
|
-
function clearSessionStorage() {
|
|
2843
|
-
sessionStorage.removeItem(API_KEY_LOCAL_STORAGE);
|
|
2844
|
-
sessionStorage.removeItem(API_URL_LOCAL_STORAGE);
|
|
2845
|
-
}
|
|
2846
|
-
function onDocumentReady(callback) {
|
|
2847
|
-
// in case the document is already rendered
|
|
2848
|
-
if (document.readyState !== 'loading') {
|
|
2849
|
-
Promise.resolve().then(() => {
|
|
2850
|
-
callback();
|
|
2851
|
-
});
|
|
2852
|
-
}
|
|
2853
|
-
// modern browsers
|
|
2854
|
-
else if (document.addEventListener) {
|
|
2855
|
-
document.addEventListener('DOMContentLoaded', callback);
|
|
2856
|
-
}
|
|
2857
|
-
}
|
|
2858
|
-
let BrowserExtensionPlugin = () => (tolgee) => tolgee;
|
|
2859
|
-
if (typeof window !== 'undefined') {
|
|
2860
|
-
BrowserExtensionPlugin = () => (tolgee) => {
|
|
2861
|
-
const handshaker = Handshaker();
|
|
2862
|
-
const getConfig = () => ({
|
|
2863
|
-
// prevent extension downloading ui library
|
|
2864
|
-
uiPresent: true,
|
|
2865
|
-
uiVersion: undefined,
|
|
2866
|
-
// tolgee mode
|
|
2867
|
-
mode: tolgee.isDev() ? 'development' : 'production',
|
|
2868
|
-
// pass credentials
|
|
2869
|
-
config: {
|
|
2870
|
-
apiUrl: tolgee.getInitialOptions().apiUrl || '',
|
|
2871
|
-
apiKey: tolgee.getInitialOptions().apiKey || '',
|
|
2872
|
-
},
|
|
2873
|
-
});
|
|
2874
|
-
const getTolgeePlugin = async () => {
|
|
2875
|
-
const InContextTools = await loadInContextLib('rc');
|
|
2876
|
-
return (tolgee) => {
|
|
2877
|
-
const credentials = getCredentials();
|
|
2878
|
-
tolgee.addPlugin(InContextTools({ credentials }));
|
|
2879
|
-
return tolgee;
|
|
2880
|
-
};
|
|
2881
|
-
};
|
|
2882
|
-
tolgee.on('running', ({ value: isRunning }) => {
|
|
2883
|
-
if (isRunning) {
|
|
2884
|
-
onDocumentReady(() => {
|
|
2885
|
-
handshaker.update(getConfig()).catch(clearSessionStorage);
|
|
2886
|
-
});
|
|
2887
|
-
}
|
|
2888
|
-
});
|
|
2889
|
-
const credentials = getCredentials();
|
|
2890
|
-
if (credentials) {
|
|
2891
|
-
getTolgeePlugin()
|
|
2892
|
-
.then((plugin) => {
|
|
2893
|
-
tolgee.addPlugin(plugin);
|
|
2894
|
-
})
|
|
2895
|
-
.catch((e) => {
|
|
2896
|
-
// eslint-disable-next-line no-console
|
|
2897
|
-
console.error('Tolgee: Failed to load in-context tools');
|
|
2898
|
-
// eslint-disable-next-line no-console
|
|
2899
|
-
console.error(e);
|
|
2900
|
-
});
|
|
2901
|
-
}
|
|
2902
|
-
return tolgee;
|
|
2903
|
-
};
|
|
2904
|
-
}
|
|
2905
|
-
|
|
2906
|
-
const CURRENT_LANGUAGE_LOCAL_STORAGE_KEY = '__tolgee_currentLanguage';
|
|
2907
|
-
const createLanguageStorage = () => {
|
|
2908
|
-
return {
|
|
2909
|
-
getLanguage() {
|
|
2910
|
-
throwIfSSR('LanguageStorage');
|
|
2911
|
-
const storedLanguage = localStorage.getItem(CURRENT_LANGUAGE_LOCAL_STORAGE_KEY);
|
|
2912
|
-
return storedLanguage || undefined;
|
|
2913
|
-
},
|
|
2914
|
-
setLanguage(language) {
|
|
2915
|
-
throwIfSSR('LanguageStorage');
|
|
2916
|
-
localStorage.setItem(CURRENT_LANGUAGE_LOCAL_STORAGE_KEY, language);
|
|
2917
|
-
},
|
|
2918
|
-
};
|
|
2919
|
-
};
|
|
2920
|
-
const LanguageStorage = () => (tolgee, tools) => {
|
|
2921
|
-
tools.setLanguageStorage(createLanguageStorage());
|
|
2922
|
-
return tolgee;
|
|
2923
|
-
};
|
|
2924
|
-
|
|
2925
|
-
const createLanguageDetector = () => {
|
|
2926
|
-
return {
|
|
2927
|
-
getLanguage({ availableLanguages }) {
|
|
2928
|
-
throwIfSSR('LanguageDetector');
|
|
2929
|
-
const preferred = window.navigator.language;
|
|
2930
|
-
const exactMatch = availableLanguages.find((l) => l === preferred);
|
|
2931
|
-
if (exactMatch) {
|
|
2932
|
-
return exactMatch;
|
|
2933
|
-
}
|
|
2934
|
-
const getTwoLetters = (fullTag) => fullTag.replace(/^(.+?)(-.*)?$/, '$1');
|
|
2935
|
-
const preferredTwoLetter = getTwoLetters(window.navigator.language);
|
|
2936
|
-
const twoLetterMatch = availableLanguages.find((l) => getTwoLetters(l) === preferredTwoLetter);
|
|
2937
|
-
if (twoLetterMatch) {
|
|
2938
|
-
return twoLetterMatch;
|
|
2939
|
-
}
|
|
2940
|
-
return undefined;
|
|
2941
|
-
},
|
|
2942
|
-
};
|
|
2943
|
-
};
|
|
2944
|
-
const LanguageDetector = () => (tolgee, tools) => {
|
|
2945
|
-
tools.setLanguageDetector(createLanguageDetector());
|
|
2946
|
-
return tolgee;
|
|
2947
|
-
};
|
|
2948
|
-
|
|
2949
|
-
/******************************************************************************
|
|
2950
|
-
Copyright (c) Microsoft Corporation.
|
|
2951
|
-
|
|
2952
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
2953
|
-
purpose with or without fee is hereby granted.
|
|
2954
|
-
|
|
2955
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
2956
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
2957
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
2958
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
2959
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
2960
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
2961
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
2962
|
-
***************************************************************************** */
|
|
2963
|
-
|
|
2964
|
-
function __rest(s, e) {
|
|
2965
|
-
var t = {};
|
|
2966
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
2967
|
-
t[p] = s[p];
|
|
2968
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
2969
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
2970
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
2971
|
-
t[p[i]] = s[p[i]];
|
|
2972
|
-
}
|
|
2973
|
-
return t;
|
|
2974
|
-
}
|
|
2975
|
-
|
|
2976
|
-
const trimSlashes = (path) => {
|
|
2977
|
-
if (path.endsWith('/')) {
|
|
2978
|
-
return path.slice(0, -1);
|
|
2979
|
-
}
|
|
2980
|
-
return path;
|
|
2981
|
-
};
|
|
2982
|
-
const defaultGetPath = ({ namespace, language, prefix }) => {
|
|
2983
|
-
if (namespace) {
|
|
2984
|
-
return `${trimSlashes(prefix)}/${namespace}/${language}.json`;
|
|
2985
|
-
}
|
|
2986
|
-
else {
|
|
2987
|
-
return `${trimSlashes(prefix)}/${language}.json`;
|
|
2988
|
-
}
|
|
2989
|
-
};
|
|
2990
|
-
const defaultGetData = (r) => r.json();
|
|
2991
|
-
const DEFAULT_OPTIONS = {
|
|
2992
|
-
prefix: '/i18n',
|
|
2993
|
-
getPath: defaultGetPath,
|
|
2994
|
-
getData: defaultGetData,
|
|
2995
|
-
headers: {
|
|
2996
|
-
Accept: 'application/json',
|
|
2997
|
-
},
|
|
2998
|
-
};
|
|
2999
|
-
const createBackendFetch = (options) => {
|
|
3000
|
-
const _a = Object.assign(Object.assign(Object.assign({}, DEFAULT_OPTIONS), options), { headers: Object.assign(Object.assign({}, DEFAULT_OPTIONS.headers), options === null || options === void 0 ? void 0 : options.headers) }), { prefix, getPath, getData, headers } = _a, fetchOptions = __rest(_a, ["prefix", "getPath", "getData", "headers"]);
|
|
3001
|
-
return {
|
|
3002
|
-
getRecord({ namespace, language }) {
|
|
3003
|
-
const path = getPath({
|
|
3004
|
-
namespace,
|
|
3005
|
-
language,
|
|
3006
|
-
prefix,
|
|
3007
|
-
});
|
|
3008
|
-
return fetch(path, Object.assign({ headers }, fetchOptions)).then((r) => {
|
|
3009
|
-
if (!r.ok) {
|
|
3010
|
-
throw new Error(`${r.url} ${r.status}`);
|
|
3011
|
-
}
|
|
3012
|
-
return getData(r);
|
|
3013
|
-
});
|
|
3014
|
-
},
|
|
3015
|
-
};
|
|
3016
|
-
};
|
|
3017
|
-
const BackendFetch = (options) => (tolgee, tools) => {
|
|
3018
|
-
tools.addBackend(createBackendFetch(options));
|
|
3019
|
-
return tolgee;
|
|
3020
|
-
};
|
|
3021
|
-
|
|
3022
|
-
const Tolgee = () => {
|
|
3023
|
-
return TolgeeCore().use(BrowserExtensionPlugin());
|
|
3024
|
-
};
|
|
3025
|
-
|
|
3026
|
-
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
3027
|
-
const DevTools = process.env.NODE_ENV === 'production'
|
|
3028
|
-
? () => (tolgee) => tolgee
|
|
3029
|
-
: require('../dist/tolgee-in-context-tools.umd').InContextTools;
|
|
3030
|
-
|
|
3031
|
-
export { BackendFetch, BrowserExtensionPlugin, DEVTOOLS_ID, DevBackend, DevTools, FormatSimple, LanguageDetector, LanguageStorage, ObserverPlugin, PREFERRED_LANGUAGES_LOCAL_STORAGE_KEY, TOLGEE_ATTRIBUTE_NAME, TOLGEE_RESTRICT_ATTRIBUTE, TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE, Tolgee, TolgeeCore, getFallback, getFallbackArray, getProjectIdFromApiKey, getTranslateProps };
|
|
3032
|
-
//# sourceMappingURL=tolgee-web.universal.esm.js.map
|