textbrowser 0.47.0 → 0.48.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +2 -1
- package/.eslintrc.cjs +3 -1
- package/CHANGES.md +11 -0
- package/README.md +2 -6
- package/dist/WorkInfo-es.js +978 -345
- package/dist/activateCallback-es.js +6 -2
- package/dist/assets/index-_11XnUty.css +9 -0
- package/dist/assets/languages-1sAACTKG.json +67 -0
- package/dist/index-es.js +2738 -1134
- package/dist/index-es.min.js +4 -4
- package/dist/sw-helper.js +314 -0
- package/index.html +2 -0
- package/package.json +46 -45
- package/resources/activateCallback.js +6 -2
- package/resources/index.js +6 -14
- package/resources/resultsDisplay.js +49 -40
- package/resources/templates/languageSelect.js +3 -3
- package/resources/templates/resultsDisplayServerOrClient.js +76 -65
- package/resources/templates/workDisplay.js +123 -103
- package/resources/templates/workSelect.js +4 -4
- package/resources/utils/IntlURLSearchParams.js +4 -1
- package/resources/utils/Languages.js +6 -1
- package/resources/utils/Metadata.js +22 -21
- package/resources/utils/Plugin.js +4 -4
- package/resources/utils/ServiceWorker.js +21 -14
- package/resources/utils/WorkInfo.js +10 -5
- package/resources/utils/dialogs.js +2 -1
- package/resources/utils/getLocaleFallbackResults.js +6 -3
- package/resources/utils/sanitize.js +3 -3
- package/resources/workDisplay.js +13 -14
- package/resources/workSelect.js +3 -2
- package/server/main.js +6 -5
- package/sw-sample.js +5 -307
- package/dist/index-umd.js +0 -18864
- package/dist/index-umd.min.js +0 -19
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import activateCallback from './activateCallback-es.js';
|
|
2
|
+
import {getWorkFiles} from './WorkInfo-es.js';
|
|
3
|
+
|
|
4
|
+
// VERSION 0.1.0
|
|
5
|
+
|
|
6
|
+
const CURRENT_CACHES = {
|
|
7
|
+
prefetch: 'prefetch-cache-v'
|
|
8
|
+
};
|
|
9
|
+
const minutes = 60 * 1000;
|
|
10
|
+
|
|
11
|
+
// UTILITIES
|
|
12
|
+
|
|
13
|
+
async function getJSON (url) {
|
|
14
|
+
const resp = await fetch(url);
|
|
15
|
+
return await resp.json();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function swHelper (self) {
|
|
19
|
+
// These could be made configurable by argument too
|
|
20
|
+
const defaultUserStaticFiles = [
|
|
21
|
+
'/', // Needs a separate entry from `index.html` (at least in Chrome)
|
|
22
|
+
'index.html',
|
|
23
|
+
'files.json',
|
|
24
|
+
'site.json',
|
|
25
|
+
'resources/user.js'
|
|
26
|
+
// We do not put the user.json here as that is obtained live with this
|
|
27
|
+
// service worker via `pathToUserJSON`
|
|
28
|
+
];
|
|
29
|
+
// Todo: We could supply `new URL(fileName, moduleURL).href` to
|
|
30
|
+
// get these as reliable full paths without hard-coding or needing to
|
|
31
|
+
// actually be in `node_modules/textbrowser`; see `resources/index.js`
|
|
32
|
+
const textbrowserStaticResourceFiles = [
|
|
33
|
+
'node_modules/textbrowser/appdata/languages.json',
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
// Only needed atm for browser validation
|
|
37
|
+
'node_modules/textbrowser/general-schemas/files.jsonschema',
|
|
38
|
+
'node_modules/textbrowser/general-schemas/languages.jsonschema',
|
|
39
|
+
'node_modules/textbrowser-data-schemas/schemas/locale.jsonschema',
|
|
40
|
+
'node_modules/textbrowser-data-schemas/schemas/metadata.jsonschema',
|
|
41
|
+
'node_modules/textbrowser-data-schemas/schemas/table.jsonschema', // Not currently using for validation or meta-data
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
'node_modules/intl-locale-textinfo-polyfill/lib/polyfill.js',
|
|
45
|
+
'node_modules/textbrowser/dist/assets/languages-1sAACTKG.json',
|
|
46
|
+
'node_modules/textbrowser/dist/assets/index-_11XnUty.css',
|
|
47
|
+
|
|
48
|
+
'node_modules/textbrowser/dist/index-es.js'
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @typedef {PlainObject} ConfigObject
|
|
53
|
+
* @property {string} namespace
|
|
54
|
+
* @property {string} basePath
|
|
55
|
+
* @property {string} languages
|
|
56
|
+
* @property {string} files
|
|
57
|
+
* @property {string[]} userStaticFiles
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
*
|
|
62
|
+
* @param {ConfigObject} args
|
|
63
|
+
* @returns {ConfigObject}
|
|
64
|
+
* @todo Since some of these reused, move to external file (or
|
|
65
|
+
* use `setServiceWorkerDefaults`?)
|
|
66
|
+
*/
|
|
67
|
+
function getConfigDefaults (args) {
|
|
68
|
+
return {
|
|
69
|
+
namespace: 'textbrowser',
|
|
70
|
+
basePath: '',
|
|
71
|
+
languages: new URL('../appdata/languages.json', import.meta.url).href,
|
|
72
|
+
files: 'files.json',
|
|
73
|
+
userStaticFiles: defaultUserStaticFiles,
|
|
74
|
+
// Opportunity to override
|
|
75
|
+
...args
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
*
|
|
81
|
+
* @param {string[]} messages
|
|
82
|
+
* @returns {Promise<void>}
|
|
83
|
+
*/
|
|
84
|
+
function log (...messages) {
|
|
85
|
+
const message = messages.join(' ');
|
|
86
|
+
console.log(message);
|
|
87
|
+
return post({message, type: 'log'});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
*
|
|
92
|
+
* @param {Error} error
|
|
93
|
+
* @param {string[]} messages
|
|
94
|
+
* @returns {Promise<void>} Resolves to `undefined`
|
|
95
|
+
*/
|
|
96
|
+
function logError (error, ...messages) {
|
|
97
|
+
const message = messages.join(' ');
|
|
98
|
+
console.error(error, message);
|
|
99
|
+
return post({message, errorType: error.type, name: error.name, type: 'error'});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @callback DelayCallback
|
|
104
|
+
* @param {Float} time
|
|
105
|
+
* @returns {void}
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
*
|
|
110
|
+
* @param {DelayCallback} cb
|
|
111
|
+
* @param {PositiveInteger} timeout
|
|
112
|
+
* @param {string} errMessage
|
|
113
|
+
* @param {PositiveInteger} [time]
|
|
114
|
+
* @returns {Promise<void>}
|
|
115
|
+
*/
|
|
116
|
+
async function tryAndRetry (cb, timeout, errMessage, time = 0) {
|
|
117
|
+
time++;
|
|
118
|
+
try {
|
|
119
|
+
await cb(time); // eslint-disable-line n/callback-return -- Needed for retries
|
|
120
|
+
return undefined;
|
|
121
|
+
} catch (err) {
|
|
122
|
+
console.log('errrr', err);
|
|
123
|
+
logError(err, err.message || errMessage);
|
|
124
|
+
return new Promise((resolve) => {
|
|
125
|
+
setTimeout(() => {
|
|
126
|
+
resolve(tryAndRetry(cb, timeout, errMessage, time));
|
|
127
|
+
}, timeout);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
*
|
|
134
|
+
* @param {PlainObject} args
|
|
135
|
+
* @param {"log"|"error"|"beginInstall"|"finishedInstall"|"beginActivate"|"finishedActivate"} args.type
|
|
136
|
+
* @param {string} [args.message]
|
|
137
|
+
* @returns {Promise<void>}
|
|
138
|
+
*/
|
|
139
|
+
async function post ({type, message = type}) {
|
|
140
|
+
const clients = await self.clients.matchAll({
|
|
141
|
+
// Are there any uncontrolled within activate anyways?
|
|
142
|
+
includeUncontrolled: true,
|
|
143
|
+
type: 'window'
|
|
144
|
+
}) || [];
|
|
145
|
+
if (message.includes('Posting finished')) {
|
|
146
|
+
message += ` (count: ${clients.length})`;
|
|
147
|
+
}
|
|
148
|
+
clients.forEach((client) => {
|
|
149
|
+
// Although we only need one client to which to send
|
|
150
|
+
// arguments, we want to signal phase complete to all
|
|
151
|
+
// eslint-disable-next-line unicorn/require-post-message-target-origin -- In worker
|
|
152
|
+
client.postMessage({message, type});
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
*
|
|
158
|
+
* @param {PositiveInteger} time
|
|
159
|
+
* @throws {Error}
|
|
160
|
+
* @returns {Promise<void>}
|
|
161
|
+
*/
|
|
162
|
+
async function install (time) {
|
|
163
|
+
post({type: 'beginInstall'});
|
|
164
|
+
log(`Install: Trying, attempt ${time}`);
|
|
165
|
+
const now = Date.now();
|
|
166
|
+
const json = await getJSON(pathToUserJSON);
|
|
167
|
+
|
|
168
|
+
const {
|
|
169
|
+
namespace, languages, files, userStaticFiles
|
|
170
|
+
} = getConfigDefaults(json);
|
|
171
|
+
|
|
172
|
+
const {version} = await getJSON('./package.json');
|
|
173
|
+
|
|
174
|
+
console.log('opening cache', namespace + CURRENT_CACHES.prefetch + version);
|
|
175
|
+
const [
|
|
176
|
+
cache,
|
|
177
|
+
userDataFiles,
|
|
178
|
+
{languages: langs}
|
|
179
|
+
] = await Promise.all([
|
|
180
|
+
caches.open(namespace + CURRENT_CACHES.prefetch + version),
|
|
181
|
+
getWorkFiles(files),
|
|
182
|
+
getJSON(languages)
|
|
183
|
+
]);
|
|
184
|
+
log('Install: Retrieved dependency values');
|
|
185
|
+
|
|
186
|
+
const langPathParts = languages.split('/');
|
|
187
|
+
|
|
188
|
+
// Todo: Ought to make these locales only conditionally required and
|
|
189
|
+
// then only show those specified in languages menu or go directly
|
|
190
|
+
// to work selection
|
|
191
|
+
// Todo: We might give option to only download
|
|
192
|
+
// one locale and avoid language splash page
|
|
193
|
+
const localeFiles = langs.map(
|
|
194
|
+
({locale: {$ref}}) => {
|
|
195
|
+
return (langPathParts.length > 1
|
|
196
|
+
? langPathParts.slice(0, -1).join('/') + '/'
|
|
197
|
+
: ''
|
|
198
|
+
) + $ref;
|
|
199
|
+
}
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const urlsToPrefetch = [
|
|
203
|
+
...textbrowserStaticResourceFiles,
|
|
204
|
+
...localeFiles,
|
|
205
|
+
...userStaticFiles,
|
|
206
|
+
...userDataFiles,
|
|
207
|
+
...stylesheets
|
|
208
|
+
];
|
|
209
|
+
// .map((url) => url === 'index.html' ? new Request(url, {cache: 'reload'}) : url)
|
|
210
|
+
try {
|
|
211
|
+
const cachePromises = urlsToPrefetch.map(async (urlToPrefetch) => {
|
|
212
|
+
// This constructs a new URL object using the service worker's script
|
|
213
|
+
// location as the base for relative URLs.
|
|
214
|
+
const url = new URL(urlToPrefetch, location.href);
|
|
215
|
+
url.search += (url.search ? '&' : '?') + 'cache-bust=' + now;
|
|
216
|
+
const request = new Request(url, {mode: 'no-cors'});
|
|
217
|
+
try {
|
|
218
|
+
const response = await fetch(request);
|
|
219
|
+
if (response.status >= 400) {
|
|
220
|
+
throw new Error('request for ' + urlToPrefetch +
|
|
221
|
+
' failed with status ' + response.statusText);
|
|
222
|
+
}
|
|
223
|
+
return cache.put(urlToPrefetch, response);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
logError(error, 'Not caching ' + urlToPrefetch + ' due to ' + error);
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
await Promise.all(cachePromises);
|
|
230
|
+
log('Install: Pre-fetching complete.');
|
|
231
|
+
} catch (error) {
|
|
232
|
+
logError(error, `Install: Pre-fetching failed: ${error}`);
|
|
233
|
+
// Failing gives chance for a new client to re-trigger install?
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// An install update event will not be reported until controlled,
|
|
238
|
+
// so we need to inform the clients
|
|
239
|
+
log(`Install: Posting finished message to clients`);
|
|
240
|
+
|
|
241
|
+
// Although we only need one client to which to send
|
|
242
|
+
// arguments, we want to signal phase complete to all
|
|
243
|
+
post({type: 'finishedInstall'});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
*
|
|
248
|
+
* @param {PositiveInteger} time
|
|
249
|
+
* @returns {Promise<void>}
|
|
250
|
+
*/
|
|
251
|
+
async function activate (time) {
|
|
252
|
+
post({type: 'beginActivate'});
|
|
253
|
+
log(`Activate: Trying, attempt ${time}`);
|
|
254
|
+
const [json, cacheNames] = await Promise.all([
|
|
255
|
+
getJSON(pathToUserJSON),
|
|
256
|
+
caches.keys()
|
|
257
|
+
]);
|
|
258
|
+
const {namespace, files, basePath} = getConfigDefaults(json);
|
|
259
|
+
const {version} = await getJSON('./package.json');
|
|
260
|
+
|
|
261
|
+
const expectedCacheNames = Object.values(CURRENT_CACHES).map((n) => namespace + n + version);
|
|
262
|
+
cacheNames.map(async (cacheName) => {
|
|
263
|
+
if (!expectedCacheNames.includes(cacheName)) {
|
|
264
|
+
log('Activate: Deleting out of date cache:', cacheName);
|
|
265
|
+
await caches.delete(cacheName);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
await activateCallback({namespace, files, basePath, log});
|
|
270
|
+
log('Activate: Database changes completed');
|
|
271
|
+
|
|
272
|
+
log(`Activate: Posting finished message to clients`);
|
|
273
|
+
// Signal phase complete to all clients
|
|
274
|
+
post({type: 'finishedActivate'});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const params = new URL(location).searchParams;
|
|
278
|
+
const pathToUserJSON = params.get('pathToUserJSON');
|
|
279
|
+
const stylesheets = JSON.parse(params.get('stylesheets') || []);
|
|
280
|
+
|
|
281
|
+
console.log('sw info', pathToUserJSON);
|
|
282
|
+
console.log('sw stylesheets', stylesheets);
|
|
283
|
+
|
|
284
|
+
self.addEventListener('install', (e) => {
|
|
285
|
+
e.waitUntil(tryAndRetry(install, 5 * minutes, 'Error installing'));
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
self.addEventListener('activate', (e) => {
|
|
289
|
+
// Erring is of no present use here:
|
|
290
|
+
// https://github.com/w3c/ServiceWorker/issues/659#issuecomment-384919053
|
|
291
|
+
e.waitUntil(tryAndRetry(activate, 5 * minutes, 'Error activating'));
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// We cannot make this async as `e.respondWith` must be called synchronously
|
|
295
|
+
self.addEventListener('fetch', (e) => {
|
|
296
|
+
// DevTools opening will trigger these o-i-c requests
|
|
297
|
+
const {request} = e;
|
|
298
|
+
const {cache, mode, url} = request;
|
|
299
|
+
if (cache === 'only-if-cached' &&
|
|
300
|
+
mode !== 'same-origin') {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
console.log('fetching', url);
|
|
304
|
+
e.respondWith((async () => {
|
|
305
|
+
const cached = await caches.match(request);
|
|
306
|
+
if (!cached) {
|
|
307
|
+
console.log('no cached found', url);
|
|
308
|
+
}
|
|
309
|
+
return cached || fetch(request);
|
|
310
|
+
})());
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export default swHelper;
|
package/index.html
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
<title></title>
|
|
5
5
|
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
|
|
6
6
|
|
|
7
|
+
<!-- Browser polyfills -->
|
|
8
|
+
<script type="module" src="node_modules/intl-locale-textinfo-polyfill/lib/polyfill.js"></script>
|
|
7
9
|
<!-- Site-specific customization -->
|
|
8
10
|
<script type="module" src="resources/user.js"></script>
|
|
9
11
|
</head><body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "textbrowser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.1",
|
|
4
4
|
"description": "Multilinear text browser",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index-es.min.js",
|
|
@@ -9,18 +9,6 @@
|
|
|
9
9
|
"bin": {
|
|
10
10
|
"textbrowser": "./server/main.js"
|
|
11
11
|
},
|
|
12
|
-
"scripts": {
|
|
13
|
-
"server": "./server/main.js --allowPlugins --namespace=test",
|
|
14
|
-
"start": "static -p 8081",
|
|
15
|
-
"rollup": "rollup -c",
|
|
16
|
-
"lint": "npm run eslint",
|
|
17
|
-
"eslint": "eslint --ext=js,md,html .",
|
|
18
|
-
"mocha": "mocha --require test/bootstrap/node.js --require chai/register-assert.js test/textbrowserTests.js",
|
|
19
|
-
"node": "npm run eslint && npm run rollup && npm run mocha",
|
|
20
|
-
"open-test": "open-cli http://127.0.0.1:8081/test/",
|
|
21
|
-
"start-open-test": "run-p start open-test",
|
|
22
|
-
"test": "npm run eslint && npm run rollup && npm run start-open-test"
|
|
23
|
-
},
|
|
24
12
|
"browserslist": [
|
|
25
13
|
"cover 100%"
|
|
26
14
|
],
|
|
@@ -42,59 +30,72 @@
|
|
|
42
30
|
"text"
|
|
43
31
|
],
|
|
44
32
|
"dependencies": {
|
|
45
|
-
"@babel/core": "^7.
|
|
33
|
+
"@babel/core": "^7.23.3",
|
|
46
34
|
"@brettz9/node-static": "^0.1.1",
|
|
47
35
|
"command-line-args": "^5.2.1",
|
|
48
|
-
"dom-parser": "^
|
|
36
|
+
"dom-parser": "^1.1.5",
|
|
49
37
|
"form-serialization": "^0.11.0",
|
|
50
|
-
"indexeddbshim": "^
|
|
51
|
-
"intl-dom": "^0.
|
|
52
|
-
"
|
|
53
|
-
"
|
|
38
|
+
"indexeddbshim": "^12.0.0",
|
|
39
|
+
"intl-dom": "^0.19.0",
|
|
40
|
+
"intl-locale-textinfo-polyfill": "^2.1.1",
|
|
41
|
+
"jamilih": "0.58.2",
|
|
42
|
+
"jsdom": "^22.1.0",
|
|
54
43
|
"json-refs": "^3.0.15",
|
|
55
44
|
"load-stylesheets": "0.10.0",
|
|
56
45
|
"node-fetch": "^2.6.6",
|
|
57
|
-
"rtl-detect": "1.0.4",
|
|
58
46
|
"simple-get-json": "^9.0.1"
|
|
59
47
|
},
|
|
60
48
|
"devDependencies": {
|
|
61
|
-
"@babel/eslint-parser": "^7.
|
|
49
|
+
"@babel/eslint-parser": "^7.23.3",
|
|
62
50
|
"@babel/plugin-proposal-object-rest-spread": "^7.20.2",
|
|
63
51
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
|
64
|
-
"@babel/preset-env": "^7.
|
|
52
|
+
"@babel/preset-env": "^7.23.3",
|
|
65
53
|
"@brettz9/eslint-plugin": "^1.0.4",
|
|
66
|
-
"@rollup/plugin-babel": "^6.0.
|
|
67
|
-
"@rollup/plugin-commonjs": "^
|
|
68
|
-
"@rollup/plugin-json": "^
|
|
69
|
-
"@rollup/plugin-node-resolve": "^15.
|
|
70
|
-
"@rollup/plugin-terser": "^0.
|
|
54
|
+
"@rollup/plugin-babel": "^6.0.4",
|
|
55
|
+
"@rollup/plugin-commonjs": "^25.0.7",
|
|
56
|
+
"@rollup/plugin-json": "^6.0.1",
|
|
57
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
58
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
71
59
|
"@stadtlandnetz/rollup-plugin-postprocess": "^1.1.0",
|
|
72
|
-
"
|
|
60
|
+
"@web/rollup-plugin-import-meta-assets": "^2.2.0",
|
|
61
|
+
"ajv": "8.12.0",
|
|
73
62
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
|
74
|
-
"chai": "^4.3.
|
|
75
|
-
"eslint": "^8.
|
|
76
|
-
"eslint-config-ash-nazg": "^
|
|
77
|
-
"eslint-config-standard": "^17.
|
|
78
|
-
"eslint-plugin-array-func": "^
|
|
79
|
-
"eslint-plugin-compat": "^4.0
|
|
63
|
+
"chai": "^4.3.10",
|
|
64
|
+
"eslint": "^8.53.0",
|
|
65
|
+
"eslint-config-ash-nazg": "^35.1.0",
|
|
66
|
+
"eslint-config-standard": "^17.1.0",
|
|
67
|
+
"eslint-plugin-array-func": "^4.0.0",
|
|
68
|
+
"eslint-plugin-compat": "^4.2.0",
|
|
80
69
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
81
70
|
"eslint-plugin-html": "^7.1.0",
|
|
82
|
-
"eslint-plugin-import": "^2.
|
|
83
|
-
"eslint-plugin-jsdoc": "^
|
|
84
|
-
"eslint-plugin-markdown": "^3.0.
|
|
85
|
-
"eslint-plugin-n": "^
|
|
86
|
-
"eslint-plugin-no-unsanitized": "^4.0.
|
|
71
|
+
"eslint-plugin-import": "^2.29.0",
|
|
72
|
+
"eslint-plugin-jsdoc": "^46.9.0",
|
|
73
|
+
"eslint-plugin-markdown": "^3.0.1",
|
|
74
|
+
"eslint-plugin-n": "^16.3.1",
|
|
75
|
+
"eslint-plugin-no-unsanitized": "^4.0.2",
|
|
87
76
|
"eslint-plugin-no-use-extend-native": "^0.5.0",
|
|
88
77
|
"eslint-plugin-promise": "^6.1.1",
|
|
89
|
-
"eslint-plugin-sonarjs": "^0.
|
|
78
|
+
"eslint-plugin-sonarjs": "^0.23.0",
|
|
90
79
|
"eslint-plugin-standard": "^4.1.0",
|
|
91
|
-
"eslint-plugin-unicorn": "^
|
|
80
|
+
"eslint-plugin-unicorn": "^49.0.0",
|
|
92
81
|
"json-metaschema": "1.3.0",
|
|
93
|
-
"mocha": "^10.
|
|
82
|
+
"mocha": "^10.2.0",
|
|
94
83
|
"npm-run-all": "^4.1.5",
|
|
95
|
-
"open-cli": "^7.
|
|
96
|
-
"rollup": "^
|
|
84
|
+
"open-cli": "^7.2.0",
|
|
85
|
+
"rollup": "^4.4.0",
|
|
97
86
|
"rollup-plugin-re": "^1.0.7",
|
|
98
87
|
"textbrowser-data-schemas": "^0.2.0"
|
|
88
|
+
},
|
|
89
|
+
"scripts": {
|
|
90
|
+
"server": "./server/main.js --allowPlugins --namespace=test",
|
|
91
|
+
"start": "static -p 8081",
|
|
92
|
+
"rollup": "rollup -c",
|
|
93
|
+
"lint": "npm run eslint",
|
|
94
|
+
"eslint": "eslint --ext=js,md,html .",
|
|
95
|
+
"mocha": "mocha --require test/bootstrap/node.js --require chai/register-assert.js test/textbrowserTests.js",
|
|
96
|
+
"node": "npm run eslint && npm run rollup && npm run mocha",
|
|
97
|
+
"open-test": "open-cli http://127.0.0.1:8081/test/",
|
|
98
|
+
"start-open-test": "run-p start open-test",
|
|
99
|
+
"test": "npm run eslint && npm run rollup && npm run start-open-test"
|
|
99
100
|
}
|
|
100
|
-
}
|
|
101
|
+
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Note that this should be kept as a polyglot client-server file
|
|
3
|
+
* (besides the server, it is to be invoked by the project service worker).
|
|
4
|
+
*/
|
|
1
5
|
/* eslint-env worker */
|
|
2
6
|
|
|
3
7
|
const {ceil} = Math;
|
|
4
8
|
const arrayChunk = (arr, size) => {
|
|
5
|
-
return
|
|
9
|
+
return Array.from({length: ceil(arr.length / size)}, (_, i) => {
|
|
6
10
|
const offset = i * size;
|
|
7
11
|
return arr.slice(offset, offset + size);
|
|
8
12
|
});
|
|
@@ -25,7 +29,7 @@ const arrayChunk = (arr, size) => {
|
|
|
25
29
|
* @param {string} cfg.namespace
|
|
26
30
|
* @param {string[]} cfg.files
|
|
27
31
|
* @param {Logger} cfg.log
|
|
28
|
-
* @param {string} [cfg.basePath
|
|
32
|
+
* @param {string} [cfg.basePath]
|
|
29
33
|
* @returns {Promise<void>}
|
|
30
34
|
*/
|
|
31
35
|
export default async function activateCallback ({
|
package/resources/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import {serialize as formSerialize} from 'form-serialization';
|
|
|
8
8
|
import getLocaleFallbackResults from './utils/getLocaleFallbackResults.js';
|
|
9
9
|
import {dialogs} from './utils/dialogs.js';
|
|
10
10
|
import {getFieldNameAndValueAliases, getBrowseFieldData} from './utils/Metadata.js';
|
|
11
|
-
import {
|
|
11
|
+
import {getWorkData} from './utils/WorkInfo.js';
|
|
12
12
|
import {
|
|
13
13
|
respondToState, listenForWorkerUpdate, registerServiceWorker, setServiceWorkerDefaults
|
|
14
14
|
} from './utils/ServiceWorker.js';
|
|
@@ -22,14 +22,14 @@ import workSelect from './workSelect.js';
|
|
|
22
22
|
import workDisplay from './workDisplay.js';
|
|
23
23
|
import {resultsDisplayClient} from './resultsDisplay.js';
|
|
24
24
|
|
|
25
|
-
/* eslint-disable no-unused-vars */
|
|
25
|
+
/* eslint-disable no-unused-vars, @stylistic/brace-style */
|
|
26
26
|
/**
|
|
27
27
|
*
|
|
28
28
|
* @param {null|number|string|PlainObject|GenericArray} obj
|
|
29
29
|
* @returns {void}
|
|
30
30
|
*/
|
|
31
31
|
function s (obj) { dialogs.alert(JSON.stringify(obj)); } // lgtm [js/unused-local-variable]
|
|
32
|
-
/* eslint-enable no-unused-vars */
|
|
32
|
+
/* eslint-enable no-unused-vars, @stylistic/brace-style */
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
*
|
|
@@ -85,7 +85,7 @@ async function prepareForServiceWorker () {
|
|
|
85
85
|
* @returns {Promise<void>}
|
|
86
86
|
*/
|
|
87
87
|
async function requestPermissions (langs, l) {
|
|
88
|
-
return await new Promise((resolve
|
|
88
|
+
return await new Promise((resolve) => {
|
|
89
89
|
// Todo: We could run the dialog code below for every page if
|
|
90
90
|
// `Notification.permission === 'default'` (i.e., not choice
|
|
91
91
|
// yet made by user), but user may avoid denying with intent
|
|
@@ -165,10 +165,6 @@ async function requestPermissions (langs, l) {
|
|
|
165
165
|
|
|
166
166
|
class TextBrowser {
|
|
167
167
|
constructor (options) {
|
|
168
|
-
// Todo: Replace the `languages` default with `import.meta.url`
|
|
169
|
-
// (`new URL('../appdata/languages.json', import.meta.url).href`?)
|
|
170
|
-
// https://github.com/tc39/proposal-import-meta
|
|
171
|
-
const moduleURL = new URL('node_modules/textbrowser/resources/index.js', location);
|
|
172
168
|
this.site = options.site || 'site.json';
|
|
173
169
|
const stylesheets = options.stylesheets || ['@builtin'];
|
|
174
170
|
const builtinIndex = stylesheets.indexOf('@builtin');
|
|
@@ -176,7 +172,7 @@ class TextBrowser {
|
|
|
176
172
|
stylesheets.splice(
|
|
177
173
|
builtinIndex,
|
|
178
174
|
1,
|
|
179
|
-
new URL('index.css',
|
|
175
|
+
new URL('index.css', import.meta.url).href
|
|
180
176
|
);
|
|
181
177
|
}
|
|
182
178
|
this.stylesheets = stylesheets;
|
|
@@ -203,10 +199,7 @@ class TextBrowser {
|
|
|
203
199
|
|
|
204
200
|
async init () {
|
|
205
201
|
this._stylesheetElements = await loadStylesheets(this.stylesheets);
|
|
206
|
-
return this.displayLanguages();
|
|
207
|
-
}
|
|
208
202
|
|
|
209
|
-
async displayLanguages () {
|
|
210
203
|
// We use getJSON instead of JsonRefs as we do not need to resolve the locales here
|
|
211
204
|
try {
|
|
212
205
|
const [langData, siteData] = await getJSON([this.languages, this.site]);
|
|
@@ -242,7 +235,7 @@ class TextBrowser {
|
|
|
242
235
|
|
|
243
236
|
// Need for directionality even if language specified (and we don't want
|
|
244
237
|
// to require it as a param)
|
|
245
|
-
// Todo: Use
|
|
238
|
+
// Todo: Use intl-locale-textinfo-polyfill (already included)
|
|
246
239
|
getDirectionForLanguageCode (code) {
|
|
247
240
|
const langs = this.langData.languages;
|
|
248
241
|
const exactMatch = langs.find((lang) => {
|
|
@@ -587,6 +580,5 @@ class TextBrowser {
|
|
|
587
580
|
// Todo: Definable as public fields?
|
|
588
581
|
TextBrowser.prototype.workDisplay = workDisplay;
|
|
589
582
|
TextBrowser.prototype.resultsDisplayClient = resultsDisplayClient;
|
|
590
|
-
TextBrowser.prototype.getWorkFiles = getWorkFiles;
|
|
591
583
|
|
|
592
584
|
export default TextBrowser;
|