textbrowser 0.41.1 → 0.42.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 +0 -1
- package/{.eslintrc.js → .eslintrc.cjs} +0 -0
- package/CHANGES.md +15 -0
- package/README.md +25 -21
- package/dist/WorkInfo-es.js +26 -95
- package/dist/index-es.js +233 -1276
- package/dist/index-es.min.js +3 -3
- package/dist/index-umd.js +8 -1003
- package/dist/index-umd.min.js +3 -3
- package/package.json +35 -35
- package/resources/index.js +1 -2
- package/resources/resultsDisplay.js +3 -2
- package/resources/utils/Metadata.js +1 -1
- package/resources/utils/WorkInfo.js +0 -22
- package/resources/utils/dialogs.js +0 -4
- package/server/main.js +14 -12
- package/sw-sample.js +0 -2
- package/server/main-cjs.js +0 -4052
package/server/main-cjs.js
DELETED
|
@@ -1,4052 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
require('url-search-params-polyfill');
|
|
5
|
-
var simpleGetJson = require('simple-get-json');
|
|
6
|
-
var rtlDetect = require('rtl-detect');
|
|
7
|
-
var jamilih = require('jamilih');
|
|
8
|
-
var formSerialization = require('form-serialization');
|
|
9
|
-
var imf = require('imf');
|
|
10
|
-
|
|
11
|
-
function ownKeys(object, enumerableOnly) {
|
|
12
|
-
var keys = Object.keys(object);
|
|
13
|
-
|
|
14
|
-
if (Object.getOwnPropertySymbols) {
|
|
15
|
-
var symbols = Object.getOwnPropertySymbols(object);
|
|
16
|
-
enumerableOnly && (symbols = symbols.filter(function (sym) {
|
|
17
|
-
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
18
|
-
})), keys.push.apply(keys, symbols);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return keys;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function _objectSpread2(target) {
|
|
25
|
-
for (var i = 1; i < arguments.length; i++) {
|
|
26
|
-
var source = null != arguments[i] ? arguments[i] : {};
|
|
27
|
-
i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
|
|
28
|
-
_defineProperty(target, key, source[key]);
|
|
29
|
-
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
|
|
30
|
-
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return target;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function _defineProperty(obj, key, value) {
|
|
38
|
-
if (key in obj) {
|
|
39
|
-
Object.defineProperty(obj, key, {
|
|
40
|
-
value: value,
|
|
41
|
-
enumerable: true,
|
|
42
|
-
configurable: true,
|
|
43
|
-
writable: true
|
|
44
|
-
});
|
|
45
|
-
} else {
|
|
46
|
-
obj[key] = value;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return obj;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function _getRequireWildcardCache(nodeInterop) {
|
|
53
|
-
if (typeof WeakMap !== "function") return null;
|
|
54
|
-
var cacheBabelInterop = new WeakMap();
|
|
55
|
-
var cacheNodeInterop = new WeakMap();
|
|
56
|
-
return (_getRequireWildcardCache = function (nodeInterop) {
|
|
57
|
-
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
58
|
-
})(nodeInterop);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function _interopRequireWildcard(obj, nodeInterop) {
|
|
62
|
-
if (!nodeInterop && obj && obj.__esModule) {
|
|
63
|
-
return obj;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
67
|
-
return {
|
|
68
|
-
default: obj
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
var cache = _getRequireWildcardCache(nodeInterop);
|
|
73
|
-
|
|
74
|
-
if (cache && cache.has(obj)) {
|
|
75
|
-
return cache.get(obj);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
var newObj = {};
|
|
79
|
-
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
80
|
-
|
|
81
|
-
for (var key in obj) {
|
|
82
|
-
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
83
|
-
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
84
|
-
|
|
85
|
-
if (desc && (desc.get || desc.set)) {
|
|
86
|
-
Object.defineProperty(newObj, key, desc);
|
|
87
|
-
} else {
|
|
88
|
-
newObj[key] = obj[key];
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
newObj.default = obj;
|
|
94
|
-
|
|
95
|
-
if (cache) {
|
|
96
|
-
cache.set(obj, newObj);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return newObj;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function _objectWithoutPropertiesLoose(source, excluded) {
|
|
103
|
-
if (source == null) return {};
|
|
104
|
-
var target = {};
|
|
105
|
-
var sourceKeys = Object.keys(source);
|
|
106
|
-
var key, i;
|
|
107
|
-
|
|
108
|
-
for (i = 0; i < sourceKeys.length; i++) {
|
|
109
|
-
key = sourceKeys[i];
|
|
110
|
-
if (excluded.indexOf(key) >= 0) continue;
|
|
111
|
-
target[key] = source[key];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return target;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function _objectWithoutProperties(source, excluded) {
|
|
118
|
-
if (source == null) return {};
|
|
119
|
-
|
|
120
|
-
var target = _objectWithoutPropertiesLoose(source, excluded);
|
|
121
|
-
|
|
122
|
-
var key, i;
|
|
123
|
-
|
|
124
|
-
if (Object.getOwnPropertySymbols) {
|
|
125
|
-
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
|
|
126
|
-
|
|
127
|
-
for (i = 0; i < sourceSymbolKeys.length; i++) {
|
|
128
|
-
key = sourceSymbolKeys[i];
|
|
129
|
-
if (excluded.indexOf(key) >= 0) continue;
|
|
130
|
-
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
|
|
131
|
-
target[key] = source[key];
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return target;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
*
|
|
140
|
-
* @param {string} param
|
|
141
|
-
* @param {boolean} skip
|
|
142
|
-
* @this IntlURLSearchParams
|
|
143
|
-
* @returns {string}
|
|
144
|
-
*/
|
|
145
|
-
function _prepareParam(param, skip) {
|
|
146
|
-
if (skip || !this.localizeParamNames) {
|
|
147
|
-
// (lang)
|
|
148
|
-
return param;
|
|
149
|
-
} // start, end, toggle
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const endNums = /\d+(-\d+)?$/; // eslint-disable-line unicorn/no-unsafe-regex
|
|
153
|
-
|
|
154
|
-
const indexed = param.match(endNums);
|
|
155
|
-
|
|
156
|
-
if (indexed) {
|
|
157
|
-
// Todo: We could i18nize numbers as well
|
|
158
|
-
return this.l10n(['params', 'indexed', param.replace(endNums, '')]) + indexed[0];
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return this.l10n(['params', param]);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
class IntlURLSearchParams {
|
|
165
|
-
constructor({
|
|
166
|
-
l10n,
|
|
167
|
-
params
|
|
168
|
-
} = {}) {
|
|
169
|
-
this.l10n = l10n;
|
|
170
|
-
|
|
171
|
-
if (!params) {
|
|
172
|
-
params = location.hash.slice(1);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (typeof params === 'string') {
|
|
176
|
-
params = new URLSearchParams(params);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
this.params = params;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
get(param, skip) {
|
|
183
|
-
return this.params.get(_prepareParam.call(this, param, skip));
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
getAll(param, skip) {
|
|
187
|
-
return this.params.getAll(_prepareParam.call(this, param, skip));
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
has(param, skip) {
|
|
191
|
-
return this.params.has(_prepareParam.call(this, param, skip));
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
delete(param, skip) {
|
|
195
|
-
return this.params.delete(_prepareParam.call(this, param, skip));
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
set(param, value, skip) {
|
|
199
|
-
return this.params.set(_prepareParam.call(this, param, skip), value);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
append(param, value, skip) {
|
|
203
|
-
return this.params.append(_prepareParam.call(this, param, skip), value);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
toString() {
|
|
207
|
-
return this.params.toString();
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/* eslint-env browser */
|
|
213
|
-
var languageSelect = {
|
|
214
|
-
main({
|
|
215
|
-
langs,
|
|
216
|
-
languages,
|
|
217
|
-
followParams,
|
|
218
|
-
$p
|
|
219
|
-
}) {
|
|
220
|
-
jamilih.jml('form', {
|
|
221
|
-
class: 'focus',
|
|
222
|
-
id: 'languageSelectionContainer',
|
|
223
|
-
$on: {
|
|
224
|
-
submit(e) {
|
|
225
|
-
e.preventDefault();
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
}, [['select', {
|
|
230
|
-
name: 'lang',
|
|
231
|
-
size: langs.length,
|
|
232
|
-
$on: {
|
|
233
|
-
click({
|
|
234
|
-
target: {
|
|
235
|
-
parentNode: {
|
|
236
|
-
selectedOptions
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}) {
|
|
240
|
-
followParams('#languageSelectionContainer', () => {
|
|
241
|
-
$p.set('lang', selectedOptions[0].value, true);
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
}, langs.map(({
|
|
247
|
-
code
|
|
248
|
-
}) => ['option', {
|
|
249
|
-
value: code
|
|
250
|
-
}, [languages.getLanguageFromCode(code)]])]], jamilih.$('#main'));
|
|
251
|
-
|
|
252
|
-
if (history.state && typeof history.state === 'object') {
|
|
253
|
-
formSerialization.deserialize(document.querySelector('#languageSelectionContainer'), history.state);
|
|
254
|
-
}
|
|
255
|
-
} // Todo: Add in Go button (with 'submitgo' localization string) to
|
|
256
|
-
// avoid need for pull-down if using first selection?
|
|
257
|
-
|
|
258
|
-
/* Works too:
|
|
259
|
-
langs.map(({code, name}) =>
|
|
260
|
-
['div', [
|
|
261
|
-
['a', {href: '#', dataset: {code}}, [name]]
|
|
262
|
-
]]
|
|
263
|
-
), $('#main')
|
|
264
|
-
*/
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
/* eslint-env browser */
|
|
270
|
-
var workSelect = (({
|
|
271
|
-
groups,
|
|
272
|
-
lf,
|
|
273
|
-
getNextAlias,
|
|
274
|
-
$p,
|
|
275
|
-
followParams
|
|
276
|
-
}) => {
|
|
277
|
-
const form = jamilih.jml('form', {
|
|
278
|
-
id: 'workSelect',
|
|
279
|
-
class: 'focus',
|
|
280
|
-
$on: {
|
|
281
|
-
submit(e) {
|
|
282
|
-
e.preventDefault();
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
}, groups.map((group, i) => ['div', [i > 0 ? ['br', 'br', 'br'] : '', ['div', [lf({
|
|
287
|
-
key: group.directions.localeKey,
|
|
288
|
-
fallback: true
|
|
289
|
-
})]], ['br'], ['select', {
|
|
290
|
-
class: 'file',
|
|
291
|
-
name: 'work' + i,
|
|
292
|
-
dataset: {
|
|
293
|
-
name: group.name.localeKey
|
|
294
|
-
},
|
|
295
|
-
$on: {
|
|
296
|
-
change({
|
|
297
|
-
target: {
|
|
298
|
-
value
|
|
299
|
-
}
|
|
300
|
-
}) {
|
|
301
|
-
/*
|
|
302
|
-
// If using click, but click doesn't always fire
|
|
303
|
-
if (e.target.nodeName.toLowerCase() === 'select') {
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
*/
|
|
307
|
-
followParams('#workSelect', () => {
|
|
308
|
-
$p.set('work', value);
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
}, [['option', {
|
|
314
|
-
value: ''
|
|
315
|
-
}, ['--']], ...group.files.map(({
|
|
316
|
-
name: fileName
|
|
317
|
-
}) => ['option', {
|
|
318
|
-
value: lf(['workNames', group.id, fileName])
|
|
319
|
-
}, [getNextAlias()]])]] // Todo: Add in Go button (with 'submitgo' localization string) to
|
|
320
|
-
// avoid need for pull-down if using first selection?
|
|
321
|
-
]]), jamilih.$('#main'));
|
|
322
|
-
|
|
323
|
-
if (history.state && typeof history.state === 'object') {
|
|
324
|
-
formSerialization.deserialize(document.querySelector('#workSelect'), history.state);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return form;
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
const colors = ['aqua', 'black', 'blue', 'fuchsia', 'gray', 'green', 'lime', 'maroon', 'navy', 'olive', 'purple', 'red', 'silver', 'teal', 'white', 'yellow'];
|
|
331
|
-
const fonts = ['Helvetica, sans-serif', 'Verdana, sans-serif', 'Gill Sans, sans-serif', 'Avantgarde, sans-serif', 'Helvetica Narrow, sans-serif', 'sans-serif', 'Times, serif', 'Times New Roman, serif', 'Palatino, serif', 'Bookman, serif', 'New Century Schoolbook, serif', 'serif', 'Andale Mono, monospace', 'Courier New, monospace', 'Courier, monospace', 'Lucidatypewriter, monospace', 'Fixed, monospace', 'monospace', 'Comic Sans, Comic Sans MS, cursive', 'Zapf Chancery, cursive', 'Coronetscript, cursive', 'Florence, cursive', 'Parkavenue, cursive', 'cursive', 'Impact, fantasy', 'Arnoldboecklin, fantasy', 'Oldtown, fantasy', 'Blippo, fantasy', 'Brushstroke, fantasy', 'fantasy'];
|
|
332
|
-
|
|
333
|
-
const nbsp2 = jamilih.nbsp.repeat(2);
|
|
334
|
-
const nbsp3 = jamilih.nbsp.repeat(3);
|
|
335
|
-
|
|
336
|
-
const getDataForSerializingParamsAsURL = () => ({
|
|
337
|
-
form: jamilih.$('form#browse'),
|
|
338
|
-
// Todo: We don't need any default once random
|
|
339
|
-
// functionality is completed
|
|
340
|
-
random: jamilih.$('#rand') || {},
|
|
341
|
-
checkboxes: jamilih.$$('input[type=checkbox]')
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
var workDisplay = {
|
|
345
|
-
bdo: ({
|
|
346
|
-
fallbackDirection,
|
|
347
|
-
message
|
|
348
|
-
}) => // Displaying as div with inline display instead of span since
|
|
349
|
-
// Firefox puts punctuation at left otherwise (bdo dir
|
|
350
|
-
// seemed to have issues in Firefox)
|
|
351
|
-
['div', {
|
|
352
|
-
style: 'display: inline; direction: ' + fallbackDirection
|
|
353
|
-
}, [message]],
|
|
354
|
-
columnsTable: ({
|
|
355
|
-
ld,
|
|
356
|
-
fieldInfo,
|
|
357
|
-
$p,
|
|
358
|
-
le,
|
|
359
|
-
iil,
|
|
360
|
-
l,
|
|
361
|
-
metadataObj,
|
|
362
|
-
preferredLocale,
|
|
363
|
-
schemaItems,
|
|
364
|
-
fieldMatchesLocale
|
|
365
|
-
}) => ['table', {
|
|
366
|
-
border: '1',
|
|
367
|
-
cellpadding: '5',
|
|
368
|
-
align: 'center'
|
|
369
|
-
}, [['tr', [['th', [ld('fieldno')]], ['th', {
|
|
370
|
-
align: 'left',
|
|
371
|
-
width: '20'
|
|
372
|
-
}, [ld('field_enabled')]], ['th', [ld('field_title')]], ['th', [ld('fieldinterlin')]], ['th', [ld('fieldcss')]]
|
|
373
|
-
/*
|
|
374
|
-
Todo: Support search?
|
|
375
|
-
,
|
|
376
|
-
['th', [
|
|
377
|
-
ld('fieldsearch')
|
|
378
|
-
]]
|
|
379
|
-
*/
|
|
380
|
-
]], ...fieldInfo.map((fieldInfoItem, i) => {
|
|
381
|
-
const idx = i + 1;
|
|
382
|
-
const checkedIndex = 'checked' + idx;
|
|
383
|
-
const fieldIndex = 'field' + idx;
|
|
384
|
-
const fieldParam = $p.get(fieldIndex);
|
|
385
|
-
return ['tr', [// Todo: Get Jamilih to accept numbers and
|
|
386
|
-
// booleans (`toString` is too dangerous)
|
|
387
|
-
['td', [String(idx)]], le('check-columns-to-browse', 'td', 'title', {}, [le('yes', 'input', 'value', {
|
|
388
|
-
class: 'fieldSelector',
|
|
389
|
-
id: checkedIndex,
|
|
390
|
-
name: iil('checked') + idx,
|
|
391
|
-
checked: $p.get(checkedIndex) !== l('no') && ($p.has(checkedIndex) || fieldInfoItem.onByDefault !== false),
|
|
392
|
-
type: 'checkbox'
|
|
393
|
-
})]), le('check-sequence', 'td', 'title', {}, [['select', {
|
|
394
|
-
name: iil('field') + idx,
|
|
395
|
-
id: fieldIndex,
|
|
396
|
-
size: '1'
|
|
397
|
-
}, fieldInfo.map(({
|
|
398
|
-
field,
|
|
399
|
-
fieldAliasOrName
|
|
400
|
-
}, j) => {
|
|
401
|
-
const matchedFieldParam = fieldParam && fieldParam === fieldAliasOrName;
|
|
402
|
-
return ['option', {
|
|
403
|
-
dataset: {
|
|
404
|
-
name: field
|
|
405
|
-
},
|
|
406
|
-
value: fieldAliasOrName,
|
|
407
|
-
selected: matchedFieldParam || j === i && !$p.has(fieldIndex)
|
|
408
|
-
}, [fieldAliasOrName]];
|
|
409
|
-
})]]), ['td', [// Todo: Make as tag selector with fields as options
|
|
410
|
-
le('interlinear-tips', 'input', 'title', {
|
|
411
|
-
name: iil('interlin') + idx,
|
|
412
|
-
value: $p.get('interlin' + idx)
|
|
413
|
-
}) // Todo: Could allow i18n of numbers here
|
|
414
|
-
]], ['td', [// Todo: Make as CodeMirror-highlighted CSS
|
|
415
|
-
['input', {
|
|
416
|
-
name: iil('css') + idx,
|
|
417
|
-
value: $p.get('css' + idx)
|
|
418
|
-
}]]]
|
|
419
|
-
/*
|
|
420
|
-
,
|
|
421
|
-
['td', [ // Todo: Allow plain or regexp searching
|
|
422
|
-
['input', {name: iil('search') + idx, value: $p.get('search' + idx)}]
|
|
423
|
-
]]
|
|
424
|
-
*/
|
|
425
|
-
]];
|
|
426
|
-
}), ['tr', [['td', {
|
|
427
|
-
colspan: 3
|
|
428
|
-
}, [le('check_all', 'input', 'value', {
|
|
429
|
-
type: 'button',
|
|
430
|
-
$on: {
|
|
431
|
-
click() {
|
|
432
|
-
jamilih.$$('.fieldSelector').forEach(checkbox => {
|
|
433
|
-
checkbox.checked = true;
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
}
|
|
438
|
-
}), le('uncheck_all', 'input', 'value', {
|
|
439
|
-
type: 'button',
|
|
440
|
-
$on: {
|
|
441
|
-
click() {
|
|
442
|
-
jamilih.$$('.fieldSelector').forEach(checkbox => {
|
|
443
|
-
checkbox.checked = false;
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
}
|
|
448
|
-
}), le('checkmark_locale_fields_only', 'input', 'value', {
|
|
449
|
-
type: 'button',
|
|
450
|
-
$on: {
|
|
451
|
-
click() {
|
|
452
|
-
fieldInfo.forEach(({
|
|
453
|
-
field
|
|
454
|
-
}, i) => {
|
|
455
|
-
const idx = i + 1; // The following is redundant with 'field' but may need to
|
|
456
|
-
// retrieve later out of order?
|
|
457
|
-
|
|
458
|
-
const fld = jamilih.$('#field' + idx).selectedOptions[0].dataset.name;
|
|
459
|
-
jamilih.$('#checked' + idx).checked = fieldMatchesLocale(fld);
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
}
|
|
464
|
-
})]]]]]],
|
|
465
|
-
advancedFormatting: ({
|
|
466
|
-
ld,
|
|
467
|
-
il,
|
|
468
|
-
l,
|
|
469
|
-
lo,
|
|
470
|
-
le,
|
|
471
|
-
$p,
|
|
472
|
-
hideFormattingSection
|
|
473
|
-
}) => ['td', {
|
|
474
|
-
id: 'advancedformatting',
|
|
475
|
-
style: {
|
|
476
|
-
display: hideFormattingSection ? 'none' : 'block'
|
|
477
|
-
}
|
|
478
|
-
}, [['h3', [ld('advancedformatting')]], ['label', [ld('textcolor'), nbsp2, ['select', {
|
|
479
|
-
name: il('colorName')
|
|
480
|
-
}, colors.map((color, i) => {
|
|
481
|
-
const atts = {
|
|
482
|
-
value: l(['param_values', 'colors', color])
|
|
483
|
-
};
|
|
484
|
-
|
|
485
|
-
if ($p.get('colorName') === l(['param_values', 'colors', color]) || i === 1 && !$p.has('colorName')) {
|
|
486
|
-
atts.selected = 'selected';
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
return lo(['param_values', 'colors', color], atts);
|
|
490
|
-
})]]], ['label', [jamilih.nbsp, ld('or_entercolor'), nbsp2, ['input', {
|
|
491
|
-
name: il('color'),
|
|
492
|
-
type: 'text',
|
|
493
|
-
value: $p.get('color') || '#',
|
|
494
|
-
size: '7',
|
|
495
|
-
maxlength: '7'
|
|
496
|
-
}]]], ['br'], ['br'], ['label', [ld('backgroundcolor'), nbsp2, ['select', {
|
|
497
|
-
name: il('bgcolorName')
|
|
498
|
-
}, colors.map((color, i) => {
|
|
499
|
-
const atts = {
|
|
500
|
-
value: l(['param_values', 'colors', color])
|
|
501
|
-
};
|
|
502
|
-
|
|
503
|
-
if ($p.get('bgcolorName') === l(['param_values', 'colors', color]) || i === 14 && !$p.has('bgcolorName')) {
|
|
504
|
-
atts.selected = 'selected';
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
return lo(['param_values', 'colors', color], atts);
|
|
508
|
-
})]]], ['label', [jamilih.nbsp, ld('or_entercolor'), nbsp2, ['input', {
|
|
509
|
-
name: il('bgcolor'),
|
|
510
|
-
type: 'text',
|
|
511
|
-
value: $p.get('bgcolor') || '#',
|
|
512
|
-
size: '7',
|
|
513
|
-
maxlength: '7'
|
|
514
|
-
}]]], ['br'], ['br'], ['label', [ld('text_font'), nbsp2, // Todo: remove hard-coded direction if i81nizing; also i18nize fontSeq param
|
|
515
|
-
['select', {
|
|
516
|
-
name: il('fontSeq'),
|
|
517
|
-
dir: 'ltr'
|
|
518
|
-
}, fonts.map((fontSeq, i) => {
|
|
519
|
-
const atts = {
|
|
520
|
-
value: fontSeq
|
|
521
|
-
};
|
|
522
|
-
|
|
523
|
-
if ($p.get('fontSeq') === fontSeq || i === 7 && !$p.has('fontSeq')) {
|
|
524
|
-
atts.selected = 'selected';
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
return ['option', atts, [fontSeq]];
|
|
528
|
-
})]]], ['br'], ['br'], ['label', [ld('font_style'), nbsp2, ['select', {
|
|
529
|
-
name: il('fontstyle')
|
|
530
|
-
}, ['italic', 'normal', 'oblique'].map((fontstyle, i) => {
|
|
531
|
-
const atts = {
|
|
532
|
-
value: l(['param_values', 'fontstyle', fontstyle])
|
|
533
|
-
};
|
|
534
|
-
|
|
535
|
-
if ($p.get('fontstyle') === l(['param_values', 'fontstyle', fontstyle]) || i === 1 && !$p.has('fontstyle')) {
|
|
536
|
-
atts.selected = 'selected';
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
return lo(['param_values', 'fontstyle', fontstyle], atts);
|
|
540
|
-
})]]], ['br'], ['div', [ld('font_variant'), nbsp3, ['label', [['input', {
|
|
541
|
-
name: il('fontvariant'),
|
|
542
|
-
type: 'radio',
|
|
543
|
-
value: l(['param_values', 'fontvariant', 'normal']),
|
|
544
|
-
checked: $p.get('fontvariant') !== ld(['param_values', 'fontvariant', 'small-caps'])
|
|
545
|
-
}], ld(['param_values', 'fontvariant', 'normal']), jamilih.nbsp]], ['label', [['input', {
|
|
546
|
-
name: il('fontvariant'),
|
|
547
|
-
type: 'radio',
|
|
548
|
-
value: l(['param_values', 'fontvariant', 'small-caps']),
|
|
549
|
-
checked: $p.get('fontvariant') === ld(['param_values', 'fontvariant', 'small-caps'])
|
|
550
|
-
}], ld(['param_values', 'fontvariant', 'small-caps']), jamilih.nbsp]]]], ['br'], ['label', [// Todo: i18n and allow for normal/bold pulldown and float input?
|
|
551
|
-
ld('font_weight'), ' (normal, bold, 100-900, etc.):', nbsp2, ['input', {
|
|
552
|
-
name: il('fontweight'),
|
|
553
|
-
type: 'text',
|
|
554
|
-
value: $p.has('fontweight') ? $p.get('fontweight') : 'normal',
|
|
555
|
-
size: '7',
|
|
556
|
-
maxlength: '12'
|
|
557
|
-
}]]], ['br'], ['label', [ld('font_size'), ' (14pt, 14px, small, 75%, etc.):', nbsp2, ['input', {
|
|
558
|
-
name: il('fontsize'),
|
|
559
|
-
type: 'text',
|
|
560
|
-
value: $p.get('fontsize'),
|
|
561
|
-
size: '7',
|
|
562
|
-
maxlength: '12'
|
|
563
|
-
}]]], ['br'], // Todo: i18nize title and values?
|
|
564
|
-
// Todo: remove hard-coded direction if i18nizing
|
|
565
|
-
['label', {
|
|
566
|
-
dir: 'ltr'
|
|
567
|
-
}, [ld('font_stretch'), jamilih.nbsp, ['select', {
|
|
568
|
-
name: il('fontstretch')
|
|
569
|
-
}, ['ultra-condensed', 'extra-condensed', 'condensed', 'semi-condensed', 'normal', 'semi-expanded', 'expanded', 'extra-expanded', 'ultra-expanded'].map(stretch => {
|
|
570
|
-
const atts = {
|
|
571
|
-
value: ld(['param_values', 'font-stretch', stretch])
|
|
572
|
-
};
|
|
573
|
-
|
|
574
|
-
if ($p.get('fontstretch') === stretch || !$p.has('fontstretch') && stretch === 'normal') {
|
|
575
|
-
atts.selected = 'selected';
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
return ['option', atts, [ld(['param_values', 'font-stretch', stretch])]];
|
|
579
|
-
})]]],
|
|
580
|
-
/**/
|
|
581
|
-
['br'], ['br'], ['label', [ld('letter_spacing'), ' (normal, .9em, -.05cm): ', ['input', {
|
|
582
|
-
name: il('letterspacing'),
|
|
583
|
-
type: 'text',
|
|
584
|
-
value: $p.has('letterspacing') ? $p.get('letterspacing') : 'normal',
|
|
585
|
-
size: '7',
|
|
586
|
-
maxlength: '12'
|
|
587
|
-
}]]], ['br'], ['label', [ld('line_height'), ' (normal, 1.5, 22px, 150%): ', ['input', {
|
|
588
|
-
name: il('lineheight'),
|
|
589
|
-
type: 'text',
|
|
590
|
-
value: $p.has('lineheight') ? $p.get('lineheight') : 'normal',
|
|
591
|
-
size: '7',
|
|
592
|
-
maxlength: '12'
|
|
593
|
-
}]]], ['br'], ['br'], le('tableformatting_tips', 'h3', 'title', {}, [ld('tableformatting')]), ['div', [ld('header_wstyles'), nbsp2, ...[['yes', ld(['param_values', 'y'])], ['no', ld(['param_values', 'n'])], ['none', ld(['param_values', '0'])]].map(([key, val], i, arr) => ['label', [['input', {
|
|
594
|
-
name: il('header'),
|
|
595
|
-
type: 'radio',
|
|
596
|
-
value: val,
|
|
597
|
-
checked: $p.get('header') === val || !$p.has('header') && i === 1
|
|
598
|
-
}], ld(key), i === arr.length - 1 ? '' : nbsp3]])]], ['div', [ld('footer_wstyles'), nbsp2, ...[['yes', ld(['param_values', 'y'])], ['no', ld(['param_values', 'n'])], ['none', ld(['param_values', '0'])]].map(([key, val], i, arr) => ['label', [['input', {
|
|
599
|
-
name: il('footer'),
|
|
600
|
-
type: 'radio',
|
|
601
|
-
value: val,
|
|
602
|
-
checked: $p.get('footer') === val || !$p.has('footer') && i === 2
|
|
603
|
-
}], ld(key), i === arr.length - 1 ? '' : nbsp3]])]], ['label', [['input', {
|
|
604
|
-
name: il('headerfooterfixed'),
|
|
605
|
-
type: 'checkbox',
|
|
606
|
-
value: l('yes'),
|
|
607
|
-
checked: $p.get('headerfooterfixed') === l('yes')
|
|
608
|
-
}], nbsp2, ld('headerfooterfixed-wishtoscroll')]], ['br'], ['div', [ld('caption_wstyles'), nbsp2, ...[['yes', ld(['param_values', 'y'])], ['no', ld(['param_values', 'n'])], ['none', ld(['param_values', '0'])]].map(([key, val], i, arr) => ['label', [['input', {
|
|
609
|
-
name: il('caption'),
|
|
610
|
-
type: 'radio',
|
|
611
|
-
value: val,
|
|
612
|
-
checked: $p.get('caption') === val || !$p.has('caption') && i === 2
|
|
613
|
-
}], ld(key), i === arr.length - 1 ? '' : nbsp3]])]], ['br'], ['div', [ld('table_wborder'), nbsp2, ['label', [['input', {
|
|
614
|
-
name: il('border'),
|
|
615
|
-
type: 'radio',
|
|
616
|
-
value: '1',
|
|
617
|
-
checked: $p.get('border') !== '0'
|
|
618
|
-
}], ld('yes'), nbsp3]], ['label', [['input', {
|
|
619
|
-
name: il('border'),
|
|
620
|
-
type: 'radio',
|
|
621
|
-
value: '0',
|
|
622
|
-
checked: $p.get('border') === '0'
|
|
623
|
-
}], ld('no')]]]], ['div', [ld('interlin_repeat_field_names'), nbsp2, ['label', [['input', {
|
|
624
|
-
name: il('interlintitle'),
|
|
625
|
-
type: 'radio',
|
|
626
|
-
value: '1',
|
|
627
|
-
checked: $p.get('interlintitle') !== '0'
|
|
628
|
-
}], ld('yes'), nbsp3]], ['label', [['input', {
|
|
629
|
-
name: il('interlintitle'),
|
|
630
|
-
type: 'radio',
|
|
631
|
-
value: '0',
|
|
632
|
-
checked: $p.get('interlintitle') === '0'
|
|
633
|
-
}], ld('no')]]]], ['label', [ld('interlintitle_css'), nbsp2, ['input', {
|
|
634
|
-
name: il('interlintitle_css'),
|
|
635
|
-
type: 'text',
|
|
636
|
-
value: $p.get('interlintitle_css') || '',
|
|
637
|
-
size: '12'
|
|
638
|
-
}]]], ['br'],
|
|
639
|
-
/*
|
|
640
|
-
['br'],
|
|
641
|
-
['label', [
|
|
642
|
-
['input', {
|
|
643
|
-
name: il('transpose'),
|
|
644
|
-
type: 'checkbox',
|
|
645
|
-
value: l('yes'),
|
|
646
|
-
checked: $p.get('transpose') === l('yes')
|
|
647
|
-
}],
|
|
648
|
-
nbsp2, ld('transpose')
|
|
649
|
-
]],
|
|
650
|
-
*/
|
|
651
|
-
['br'], le('pageformatting_tips', 'h3', 'title', {}, [ld('pageformatting')]),
|
|
652
|
-
/*
|
|
653
|
-
['label', [
|
|
654
|
-
ld('speech_controls'), nbsp2,
|
|
655
|
-
['label', [
|
|
656
|
-
['input', {
|
|
657
|
-
name: il('speech'),
|
|
658
|
-
type: 'radio',
|
|
659
|
-
value: '1',
|
|
660
|
-
checked: $p.get('speech') === '1'
|
|
661
|
-
}],
|
|
662
|
-
ld('yes'), nbsp3
|
|
663
|
-
]],
|
|
664
|
-
['label', [
|
|
665
|
-
['input', {
|
|
666
|
-
name: il('speech'),
|
|
667
|
-
type: 'radio',
|
|
668
|
-
value: '0',
|
|
669
|
-
checked: $p.get('speech') !== '1'
|
|
670
|
-
}],
|
|
671
|
-
ld('no')
|
|
672
|
-
]]
|
|
673
|
-
]],
|
|
674
|
-
['br'],
|
|
675
|
-
*/
|
|
676
|
-
['label', [ld('page_css'), nbsp2, ['textarea', {
|
|
677
|
-
name: il('pagecss'),
|
|
678
|
-
title: l('page_css_tips'),
|
|
679
|
-
value: $p.get('pagecss')
|
|
680
|
-
}]]], ['br'], le('outputmode_tips', 'label', 'title', {}, [ld('outputmode'), nbsp2, // Todo: Could i18nize, but would need smaller values
|
|
681
|
-
['select', {
|
|
682
|
-
name: il('outputmode')
|
|
683
|
-
}, ['table', 'div' // , 'json-array',
|
|
684
|
-
// 'json-object'
|
|
685
|
-
].map(mode => {
|
|
686
|
-
const atts = {
|
|
687
|
-
value: mode
|
|
688
|
-
};
|
|
689
|
-
|
|
690
|
-
if ($p.get('outputmode') === mode) {
|
|
691
|
-
atts.selected = 'selected';
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
return lo(['param_values', 'outputmode', mode], atts);
|
|
695
|
-
})]])]],
|
|
696
|
-
|
|
697
|
-
addRandomFormFields({
|
|
698
|
-
il,
|
|
699
|
-
ld,
|
|
700
|
-
l,
|
|
701
|
-
le,
|
|
702
|
-
$p,
|
|
703
|
-
serializeParamsAsURL,
|
|
704
|
-
content
|
|
705
|
-
}) {
|
|
706
|
-
const addRowContent = rowContent => {
|
|
707
|
-
if (!rowContent || !rowContent.length) {
|
|
708
|
-
return;
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
content.push(['tr', rowContent]);
|
|
712
|
-
};
|
|
713
|
-
|
|
714
|
-
[[['td', {
|
|
715
|
-
colspan: 12,
|
|
716
|
-
align: 'center'
|
|
717
|
-
}, [['br'], ld('or'), ['br'], ['br']]]], [['td', {
|
|
718
|
-
colspan: 12,
|
|
719
|
-
align: 'center'
|
|
720
|
-
}, [// Todo: Could allow random with fixed starting and/or ending range
|
|
721
|
-
['label', [ld('rnd'), nbsp3, ['input', {
|
|
722
|
-
id: 'rand',
|
|
723
|
-
name: il('rand'),
|
|
724
|
-
type: 'checkbox',
|
|
725
|
-
value: l('yes'),
|
|
726
|
-
checked: $p.get('rand') === l('yes')
|
|
727
|
-
}]]], nbsp3, ['label', [ld('verses-context'), jamilih.nbsp, ['input', {
|
|
728
|
-
name: il('context'),
|
|
729
|
-
type: 'number',
|
|
730
|
-
min: 1,
|
|
731
|
-
size: 4,
|
|
732
|
-
value: $p.get('context')
|
|
733
|
-
}]]], nbsp3, le('view-random-URL', 'input', 'value', {
|
|
734
|
-
type: 'button',
|
|
735
|
-
$on: {
|
|
736
|
-
click() {
|
|
737
|
-
const url = serializeParamsAsURL(_objectSpread2(_objectSpread2({}, getDataForSerializingParamsAsURL()), {}, {
|
|
738
|
-
type: 'randomResult'
|
|
739
|
-
}));
|
|
740
|
-
jamilih.$('#randomURL').value = url;
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
}
|
|
744
|
-
}), ['input', {
|
|
745
|
-
id: 'randomURL',
|
|
746
|
-
type: 'text'
|
|
747
|
-
}]]]]].forEach(addRowContent);
|
|
748
|
-
},
|
|
749
|
-
|
|
750
|
-
getPreferences: ({
|
|
751
|
-
languageParam,
|
|
752
|
-
lf,
|
|
753
|
-
paramsSetter,
|
|
754
|
-
replaceHash,
|
|
755
|
-
getFieldAliasOrNames,
|
|
756
|
-
work,
|
|
757
|
-
langs,
|
|
758
|
-
imfl,
|
|
759
|
-
l,
|
|
760
|
-
localizeParamNames,
|
|
761
|
-
namespace,
|
|
762
|
-
hideFormattingSection,
|
|
763
|
-
groups,
|
|
764
|
-
preferencesPlugin
|
|
765
|
-
}) => ['div', {
|
|
766
|
-
style: {
|
|
767
|
-
textAlign: 'left'
|
|
768
|
-
},
|
|
769
|
-
id: 'preferences',
|
|
770
|
-
hidden: 'true'
|
|
771
|
-
}, [['div', {
|
|
772
|
-
style: 'margin-top: 10px;'
|
|
773
|
-
}, [['label', [l('localizeParamNames'), ['input', {
|
|
774
|
-
id: 'localizeParamNames',
|
|
775
|
-
type: 'checkbox',
|
|
776
|
-
checked: localizeParamNames,
|
|
777
|
-
$on: {
|
|
778
|
-
change({
|
|
779
|
-
target: {
|
|
780
|
-
checked
|
|
781
|
-
}
|
|
782
|
-
}) {
|
|
783
|
-
localStorage.setItem(namespace + '-localizeParamNames', checked);
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
}
|
|
787
|
-
}]]]]], ['div', [['label', [l('Hide formatting section'), ['input', {
|
|
788
|
-
id: 'hideFormattingSection',
|
|
789
|
-
type: 'checkbox',
|
|
790
|
-
checked: hideFormattingSection,
|
|
791
|
-
$on: {
|
|
792
|
-
change({
|
|
793
|
-
target: {
|
|
794
|
-
checked
|
|
795
|
-
}
|
|
796
|
-
}) {
|
|
797
|
-
jamilih.$('#advancedformatting').style.display = checked ? 'none' : 'block';
|
|
798
|
-
localStorage.setItem(namespace + '-hideFormattingSection', checked);
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
}
|
|
802
|
-
}]]]]], ['div', [['label', {
|
|
803
|
-
for: 'prefLangs'
|
|
804
|
-
}, [l('Preferred language(s)')]], ['br'], ['select', {
|
|
805
|
-
id: 'prefLangs',
|
|
806
|
-
multiple: 'multiple',
|
|
807
|
-
size: langs.length,
|
|
808
|
-
$on: {
|
|
809
|
-
change({
|
|
810
|
-
target: {
|
|
811
|
-
selectedOptions
|
|
812
|
-
}
|
|
813
|
-
}) {
|
|
814
|
-
// Todo: EU disclaimer re: storage?
|
|
815
|
-
localStorage.setItem(namespace + '-langCodes', JSON.stringify([...selectedOptions].map(opt => opt.value)));
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
}
|
|
819
|
-
}, langs.map(lan => {
|
|
820
|
-
let langCodes = localStorage.getItem(namespace + '-langCodes');
|
|
821
|
-
langCodes = langCodes && JSON.parse(langCodes);
|
|
822
|
-
const atts = {
|
|
823
|
-
value: lan.code
|
|
824
|
-
};
|
|
825
|
-
|
|
826
|
-
if (langCodes && langCodes.includes(lan.code)) {
|
|
827
|
-
atts.selected = 'selected';
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
return ['option', atts, [imfl(['languages', lan.code])]];
|
|
831
|
-
})]]], preferencesPlugin ? preferencesPlugin({
|
|
832
|
-
$: jamilih.$,
|
|
833
|
-
l,
|
|
834
|
-
jml: jamilih.jml,
|
|
835
|
-
paramsSetter,
|
|
836
|
-
getDataForSerializingParamsAsURL,
|
|
837
|
-
work,
|
|
838
|
-
replaceHash,
|
|
839
|
-
getFieldAliasOrNames
|
|
840
|
-
}) : '']],
|
|
841
|
-
|
|
842
|
-
addBrowseFields({
|
|
843
|
-
browseFields,
|
|
844
|
-
fieldInfo,
|
|
845
|
-
ld,
|
|
846
|
-
i,
|
|
847
|
-
iil,
|
|
848
|
-
$p,
|
|
849
|
-
content
|
|
850
|
-
}) {
|
|
851
|
-
const work = $p.get('work');
|
|
852
|
-
|
|
853
|
-
const addRowContent = rowContent => {
|
|
854
|
-
if (!rowContent || !rowContent.length) {
|
|
855
|
-
return;
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
content.push(['tr', rowContent]);
|
|
859
|
-
};
|
|
860
|
-
|
|
861
|
-
[// Todo: Separate formatting to CSS
|
|
862
|
-
i > 0 ? [['td', {
|
|
863
|
-
colspan: 12,
|
|
864
|
-
align: 'center'
|
|
865
|
-
}, [['br'], ld('or'), ['br'], ['br']]]] : '', [...(() => {
|
|
866
|
-
const addBrowseFieldSet = setType => browseFields.reduce((rowContent, {
|
|
867
|
-
fieldName,
|
|
868
|
-
aliases,
|
|
869
|
-
fieldSchema: {
|
|
870
|
-
minimum,
|
|
871
|
-
maximum
|
|
872
|
-
}
|
|
873
|
-
}, j) => {
|
|
874
|
-
// Namespace by work for sake of browser auto-complete caching
|
|
875
|
-
const name = work + '-' + iil(setType) + (i + 1) + '-' + (j + 1);
|
|
876
|
-
const id = name;
|
|
877
|
-
rowContent['#'].push(['td', [['label', {
|
|
878
|
-
for: name
|
|
879
|
-
}, [fieldName]]]], ['td', [aliases ? ['datalist', {
|
|
880
|
-
id: 'dl-' + id
|
|
881
|
-
}, aliases.map(alias => ['option', [alias]])] : '', aliases ? ['input', {
|
|
882
|
-
name,
|
|
883
|
-
id,
|
|
884
|
-
class: 'browseField',
|
|
885
|
-
list: 'dl-' + id,
|
|
886
|
-
value: $p.get(name, true),
|
|
887
|
-
$on: setType === 'start' ? {
|
|
888
|
-
change(e) {
|
|
889
|
-
jamilih.$$('input.browseField').forEach(bf => {
|
|
890
|
-
if (bf.id.includes(i + 1 + '-' + (j + 1))) {
|
|
891
|
-
bf.value = e.target.value;
|
|
892
|
-
}
|
|
893
|
-
});
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
} : undefined
|
|
897
|
-
}] : ['input', {
|
|
898
|
-
name,
|
|
899
|
-
id,
|
|
900
|
-
type: 'number',
|
|
901
|
-
min: minimum,
|
|
902
|
-
max: maximum,
|
|
903
|
-
value: $p.get(name, true)
|
|
904
|
-
}], nbsp3]]);
|
|
905
|
-
return rowContent;
|
|
906
|
-
}, {
|
|
907
|
-
'#': []
|
|
908
|
-
});
|
|
909
|
-
|
|
910
|
-
return [addBrowseFieldSet('start'), ['td', [['b', [ld('to')]], nbsp3]], addBrowseFieldSet('end')];
|
|
911
|
-
})(), ['td', [browseFields.length > 1 ? ld('versesendingdataoptional') : '']]], [['td', {
|
|
912
|
-
colspan: 4 * browseFields.length + 2 + 1,
|
|
913
|
-
align: 'center'
|
|
914
|
-
}, [['table', [['tr', [browseFields.reduce((rowContent, {
|
|
915
|
-
fieldName,
|
|
916
|
-
aliases,
|
|
917
|
-
fieldSchema: {
|
|
918
|
-
minimum,
|
|
919
|
-
maximum
|
|
920
|
-
}
|
|
921
|
-
}, j) => {
|
|
922
|
-
// Namespace by work for sake of browser auto-complete caching
|
|
923
|
-
const name = work + '-' + iil('anchor') + (i + 1) + '-' + (j + 1);
|
|
924
|
-
const id = name;
|
|
925
|
-
rowContent['#'].push(['td', [['label', {
|
|
926
|
-
for: name
|
|
927
|
-
}, [fieldName]]]], ['td', [aliases ? ['datalist', {
|
|
928
|
-
id: 'dl-' + id
|
|
929
|
-
}, aliases.map(alias => ['option', [alias]])] : '', aliases ? ['input', {
|
|
930
|
-
name,
|
|
931
|
-
id,
|
|
932
|
-
class: 'browseField',
|
|
933
|
-
list: 'dl-' + id,
|
|
934
|
-
value: $p.get(name, true)
|
|
935
|
-
}] : ['input', {
|
|
936
|
-
name,
|
|
937
|
-
id,
|
|
938
|
-
type: 'number',
|
|
939
|
-
min: minimum,
|
|
940
|
-
max: maximum,
|
|
941
|
-
value: $p.get(name, true)
|
|
942
|
-
}], nbsp2]]);
|
|
943
|
-
return rowContent;
|
|
944
|
-
}, {
|
|
945
|
-
'#': [['td', {
|
|
946
|
-
style: 'font-weight: bold; vertical-align: bottom;'
|
|
947
|
-
}, [ld('anchored-at') + nbsp3]]]
|
|
948
|
-
}), ['td', [['label', [ld('field') + nbsp2, ['select', {
|
|
949
|
-
name: iil('anchorfield') + (i + 1),
|
|
950
|
-
size: '1'
|
|
951
|
-
}, fieldInfo.map(({
|
|
952
|
-
fieldAliasOrName
|
|
953
|
-
}) => {
|
|
954
|
-
const val = $p.get(iil('anchorfield') + (i + 1), true);
|
|
955
|
-
|
|
956
|
-
if (val === fieldAliasOrName) {
|
|
957
|
-
return ['option', {
|
|
958
|
-
selected: true
|
|
959
|
-
}, [fieldAliasOrName]];
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
return ['option', [fieldAliasOrName]];
|
|
963
|
-
})]]]]]]]]]]]]].forEach(addRowContent);
|
|
964
|
-
},
|
|
965
|
-
|
|
966
|
-
main({
|
|
967
|
-
lf,
|
|
968
|
-
languageParam,
|
|
969
|
-
l,
|
|
970
|
-
namespace,
|
|
971
|
-
heading,
|
|
972
|
-
fallbackDirection,
|
|
973
|
-
imfl,
|
|
974
|
-
langs,
|
|
975
|
-
fieldInfo,
|
|
976
|
-
localizeParamNames,
|
|
977
|
-
serializeParamsAsURL,
|
|
978
|
-
paramsSetter,
|
|
979
|
-
replaceHash,
|
|
980
|
-
getFieldAliasOrNames,
|
|
981
|
-
hideFormattingSection,
|
|
982
|
-
$p,
|
|
983
|
-
metadataObj,
|
|
984
|
-
il,
|
|
985
|
-
le,
|
|
986
|
-
ld,
|
|
987
|
-
iil,
|
|
988
|
-
fieldMatchesLocale,
|
|
989
|
-
preferredLocale,
|
|
990
|
-
schemaItems,
|
|
991
|
-
content,
|
|
992
|
-
groups,
|
|
993
|
-
preferencesPlugin
|
|
994
|
-
}) {
|
|
995
|
-
const work = $p.get('work');
|
|
996
|
-
|
|
997
|
-
const serializeParamsAsURLWithData = ({
|
|
998
|
-
type
|
|
999
|
-
}) => {
|
|
1000
|
-
return serializeParamsAsURL(_objectSpread2(_objectSpread2({}, getDataForSerializingParamsAsURL()), {}, {
|
|
1001
|
-
type
|
|
1002
|
-
}));
|
|
1003
|
-
};
|
|
1004
|
-
|
|
1005
|
-
const lo = (key, atts) => ['option', atts, [l({
|
|
1006
|
-
key,
|
|
1007
|
-
|
|
1008
|
-
fallback({
|
|
1009
|
-
message
|
|
1010
|
-
}) {
|
|
1011
|
-
atts.dir = fallbackDirection;
|
|
1012
|
-
return message;
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
})]]; // Returns element with localized or fallback attribute value (as Jamilih);
|
|
1016
|
-
// also adds direction
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
jamilih.jml('div', {
|
|
1020
|
-
class: 'focus'
|
|
1021
|
-
}, [['div', {
|
|
1022
|
-
style: 'float: left;'
|
|
1023
|
-
}, [['button', {
|
|
1024
|
-
$on: {
|
|
1025
|
-
click() {
|
|
1026
|
-
const prefs = jamilih.$('#preferences');
|
|
1027
|
-
prefs.hidden = !prefs.hidden;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
}
|
|
1031
|
-
}, [l('Preferences')]], Templates.workDisplay.getPreferences({
|
|
1032
|
-
languageParam,
|
|
1033
|
-
lf,
|
|
1034
|
-
paramsSetter,
|
|
1035
|
-
replaceHash,
|
|
1036
|
-
getFieldAliasOrNames,
|
|
1037
|
-
work,
|
|
1038
|
-
langs,
|
|
1039
|
-
imfl,
|
|
1040
|
-
l,
|
|
1041
|
-
localizeParamNames,
|
|
1042
|
-
namespace,
|
|
1043
|
-
groups,
|
|
1044
|
-
hideFormattingSection,
|
|
1045
|
-
preferencesPlugin
|
|
1046
|
-
})]], ['h2', [heading]], ['br'], ['form', {
|
|
1047
|
-
id: 'browse',
|
|
1048
|
-
$custom: {
|
|
1049
|
-
$submit() {
|
|
1050
|
-
const thisParams = serializeParamsAsURLWithData({
|
|
1051
|
-
type: 'saveSettings'
|
|
1052
|
-
}).replace(/^[^#]*#/, ''); // Don't change the visible URL
|
|
1053
|
-
|
|
1054
|
-
console.log('history thisParams', thisParams);
|
|
1055
|
-
history.replaceState(thisParams, document.title, location.href);
|
|
1056
|
-
const newURL = serializeParamsAsURLWithData({
|
|
1057
|
-
type: 'result'
|
|
1058
|
-
});
|
|
1059
|
-
location.href = newURL;
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
},
|
|
1063
|
-
$on: {
|
|
1064
|
-
keydown({
|
|
1065
|
-
key,
|
|
1066
|
-
target
|
|
1067
|
-
}) {
|
|
1068
|
-
// Chrome is not having submit event triggered now with enter key
|
|
1069
|
-
// presses on inputs, despite having a `type=submit` input in the
|
|
1070
|
-
// form, and despite not using `preventDefault`
|
|
1071
|
-
if (key === 'Enter' && target.localName.toLowerCase() !== 'textarea') {
|
|
1072
|
-
this.$submit();
|
|
1073
|
-
}
|
|
1074
|
-
},
|
|
1075
|
-
|
|
1076
|
-
submit(e) {
|
|
1077
|
-
e.preventDefault();
|
|
1078
|
-
this.$submit();
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
},
|
|
1082
|
-
name: il('browse')
|
|
1083
|
-
}, [['table', {
|
|
1084
|
-
align: 'center'
|
|
1085
|
-
}, content], ['br'], ['div', {
|
|
1086
|
-
style: 'margin-left: 20px'
|
|
1087
|
-
}, [['br'], ['br'], ['table', {
|
|
1088
|
-
border: '1',
|
|
1089
|
-
align: 'center',
|
|
1090
|
-
cellpadding: '5'
|
|
1091
|
-
}, [['tr', {
|
|
1092
|
-
valign: 'top'
|
|
1093
|
-
}, [['td', [Templates.workDisplay.columnsTable({
|
|
1094
|
-
ld,
|
|
1095
|
-
fieldInfo,
|
|
1096
|
-
$p,
|
|
1097
|
-
le,
|
|
1098
|
-
iil,
|
|
1099
|
-
l,
|
|
1100
|
-
metadataObj,
|
|
1101
|
-
preferredLocale,
|
|
1102
|
-
schemaItems,
|
|
1103
|
-
fieldMatchesLocale
|
|
1104
|
-
}), le('save-settings-URL', 'input', 'value', {
|
|
1105
|
-
type: 'button',
|
|
1106
|
-
$on: {
|
|
1107
|
-
click() {
|
|
1108
|
-
const url = serializeParamsAsURLWithData({
|
|
1109
|
-
type: 'saveSettings'
|
|
1110
|
-
});
|
|
1111
|
-
jamilih.$('#settings-URL').value = url;
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
}
|
|
1115
|
-
}), ['input', {
|
|
1116
|
-
id: 'settings-URL'
|
|
1117
|
-
}], ['br'], ['button', {
|
|
1118
|
-
$on: {
|
|
1119
|
-
async click(e) {
|
|
1120
|
-
e.preventDefault();
|
|
1121
|
-
const paramsCopy = paramsSetter(_objectSpread2(_objectSpread2({}, getDataForSerializingParamsAsURL()), {}, {
|
|
1122
|
-
workName: work,
|
|
1123
|
-
// Delete work of current page
|
|
1124
|
-
type: 'startEndResult'
|
|
1125
|
-
}));
|
|
1126
|
-
const url = replaceHash(paramsCopy) + `&work=${work}&${work}-startEnd1=%s`; // %s will be escaped if set as param; also add changeable workName here
|
|
1127
|
-
|
|
1128
|
-
try {
|
|
1129
|
-
await navigator.clipboard.writeText(url);
|
|
1130
|
-
} catch (err) {// User rejected
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
}
|
|
1135
|
-
}, [l('Copy_shortcut_URL')]]]], Templates.workDisplay.advancedFormatting({
|
|
1136
|
-
ld,
|
|
1137
|
-
il,
|
|
1138
|
-
l,
|
|
1139
|
-
lo,
|
|
1140
|
-
le,
|
|
1141
|
-
$p,
|
|
1142
|
-
hideFormattingSection
|
|
1143
|
-
})
|
|
1144
|
-
/*
|
|
1145
|
-
// Todo: Is this still the case? No way to control with CSS?
|
|
1146
|
-
,arabicContent ?
|
|
1147
|
-
// If there is Arabic content, a text box will be created for
|
|
1148
|
-
// each field with such content to allow the user to choose
|
|
1149
|
-
// how wide the field should be (since the Arabic is smaller).
|
|
1150
|
-
// Todo: Allow naming of the field differently for Persian?
|
|
1151
|
-
// Allowing any column to be resized would probably be most
|
|
1152
|
-
// consistent with this project's aim to not make arbitrary
|
|
1153
|
-
// decisions on what should be customizable, but rather make
|
|
1154
|
-
// as much as possible customizable. It may also be helpful
|
|
1155
|
-
// for Chinese, etc. If adding, also need $p.get() for
|
|
1156
|
-
// defaulting behavior
|
|
1157
|
-
{'#': arabicContent.map((item, i) =>
|
|
1158
|
-
{'#': [
|
|
1159
|
-
'Width of Arabic column: ', // Todo: i18n
|
|
1160
|
-
['input', {
|
|
1161
|
-
name: il('arw') + i,
|
|
1162
|
-
type: 'text',
|
|
1163
|
-
value: '',
|
|
1164
|
-
size: '7',
|
|
1165
|
-
maxlength: '12'
|
|
1166
|
-
}]
|
|
1167
|
-
]}
|
|
1168
|
-
)} :
|
|
1169
|
-
''
|
|
1170
|
-
*/
|
|
1171
|
-
]]]]]], ['p', {
|
|
1172
|
-
align: 'center'
|
|
1173
|
-
}, [le('submitgo', 'input', 'value', {
|
|
1174
|
-
type: 'submit'
|
|
1175
|
-
})]]]]], jamilih.$('#main'));
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
};
|
|
1179
|
-
|
|
1180
|
-
var resultsDisplayServerOrClient$1 = {
|
|
1181
|
-
caption({
|
|
1182
|
-
heading,
|
|
1183
|
-
ranges
|
|
1184
|
-
}) {
|
|
1185
|
-
return heading + ' ' + ranges;
|
|
1186
|
-
},
|
|
1187
|
-
|
|
1188
|
-
startSeparator({
|
|
1189
|
-
l
|
|
1190
|
-
}) {
|
|
1191
|
-
return l('colon');
|
|
1192
|
-
},
|
|
1193
|
-
|
|
1194
|
-
innerBrowseFieldSeparator({
|
|
1195
|
-
l
|
|
1196
|
-
}) {
|
|
1197
|
-
return l('comma-space');
|
|
1198
|
-
},
|
|
1199
|
-
|
|
1200
|
-
ranges({
|
|
1201
|
-
l,
|
|
1202
|
-
startRange,
|
|
1203
|
-
endVals,
|
|
1204
|
-
rangeNames
|
|
1205
|
-
}) {
|
|
1206
|
-
return startRange + // l('to').toLowerCase() + ' ' +
|
|
1207
|
-
'-' + endVals.join(Templates.resultsDisplayServerOrClient.startSeparator({
|
|
1208
|
-
l
|
|
1209
|
-
})) + ' (' + rangeNames + ')';
|
|
1210
|
-
},
|
|
1211
|
-
|
|
1212
|
-
fieldValueAlias({
|
|
1213
|
-
key,
|
|
1214
|
-
value
|
|
1215
|
-
}) {
|
|
1216
|
-
return value + ' (' + key + ')';
|
|
1217
|
-
},
|
|
1218
|
-
|
|
1219
|
-
interlinearSegment({
|
|
1220
|
-
lang,
|
|
1221
|
-
dir,
|
|
1222
|
-
html
|
|
1223
|
-
}) {
|
|
1224
|
-
return `<span${lang ? ` lang="${lang}"` : ''}${dir ? ` dir="${dir}"` : ''}>${html}</span>`;
|
|
1225
|
-
},
|
|
1226
|
-
|
|
1227
|
-
interlinearTitle({
|
|
1228
|
-
l,
|
|
1229
|
-
val
|
|
1230
|
-
}) {
|
|
1231
|
-
const colonSpace = l('colon-space');
|
|
1232
|
-
return `<span class="interlintitle">${val}</span>${colonSpace}`;
|
|
1233
|
-
},
|
|
1234
|
-
|
|
1235
|
-
styles({
|
|
1236
|
-
$p,
|
|
1237
|
-
$pRaw,
|
|
1238
|
-
$pRawEsc,
|
|
1239
|
-
$pEscArbitrary,
|
|
1240
|
-
escapeQuotedCSS,
|
|
1241
|
-
escapeCSS,
|
|
1242
|
-
tableWithFixedHeaderAndFooter,
|
|
1243
|
-
checkedFieldIndexes,
|
|
1244
|
-
hasCaption
|
|
1245
|
-
}) {
|
|
1246
|
-
const colorEsc = !$p.has('color', true) || $p.get('color', true) === '#' ? $pRawEsc('colorName') : $pEscArbitrary('color');
|
|
1247
|
-
const bgcolorEsc = !$p.has('bgcolor', true) || $p.get('bgcolor', true) === '#' ? $pRawEsc('bgcolorName') : $pEscArbitrary('bgcolor');
|
|
1248
|
-
const tableHeight = '100%';
|
|
1249
|
-
const topToCaptionStart = 1;
|
|
1250
|
-
const captionSizeDelta = (hasCaption ? 2 : 0) + 0;
|
|
1251
|
-
const topToHeadingsStart = topToCaptionStart + captionSizeDelta;
|
|
1252
|
-
const headingsDelta = 1;
|
|
1253
|
-
const topToBodyStart = topToHeadingsStart + headingsDelta + 'em';
|
|
1254
|
-
const footerHeight = '2em';
|
|
1255
|
-
const bodyToFooterPadding = '0.5em';
|
|
1256
|
-
const topToBodyEnd = `100% - ${topToBodyStart} - ${footerHeight} - ${bodyToFooterPadding}`;
|
|
1257
|
-
const topToBodyEndCalc = `calc(${topToBodyEnd})`;
|
|
1258
|
-
const topToFooter = `calc(${topToBodyEnd} + ${bodyToFooterPadding})`;
|
|
1259
|
-
return ['style', [(tableWithFixedHeaderAndFooter ? `
|
|
1260
|
-
html, body, #main, #main > div {
|
|
1261
|
-
height: 100%; /* Needed to ensure descendent heights retain 100%; could be avoided if didn't want percent on table height */
|
|
1262
|
-
overflow-y: hidden; /* Not sure why we're getting extra here, but... */
|
|
1263
|
-
}
|
|
1264
|
-
.anchor-table-header {
|
|
1265
|
-
background-color: ${bgcolorEsc || 'white'}; /* Header background (not just for div text inside header, but for whole header area) */
|
|
1266
|
-
overflow-x: hidden; /* Not sure why we're getting extra here, but... */
|
|
1267
|
-
position: relative; /* Ensures we are still flowing, but provides anchor for absolutely positioned thead below (absolute positioning positions relative to nearest non-static ancestor; clear demo at https://www.w3schools.com/cssref/playit.asp?filename=playcss_position&preval=fixed ) */
|
|
1268
|
-
padding-top: ${topToBodyStart}; /* Provides space for the header (and caption) (the one that is absolutely positioned below relative to here) */
|
|
1269
|
-
height: ${tableHeight}; /* Percent of the whole screen taken by the table */
|
|
1270
|
-
}
|
|
1271
|
-
.anchor-table-body {
|
|
1272
|
-
overflow-y: auto; /* Provides scrollbars when text fills up beyond the height */
|
|
1273
|
-
height: ${topToBodyEndCalc}; /* If < 100%, the header anchor background color will seep below table */
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
.caption, .thead .th, .tfoot .th {
|
|
1277
|
-
line-height: 0; /* th div will have own line-height; reducing here avoids fattening it by an inner div */
|
|
1278
|
-
color: transparent; /* Hides the non-div duplicated header text */
|
|
1279
|
-
white-space: nowrap; /* Ensures column header text uses up full width without overlap (esp. wrap no longer seems to work); this must be applied outside of the div */
|
|
1280
|
-
border: none; /* We don't want a border for this invisible section */
|
|
1281
|
-
}
|
|
1282
|
-
div.inner-caption, .thead .th div.th-inner, .tfoot .th div.th-inner { /* divs are used as th is supposedly problematic */
|
|
1283
|
-
position: absolute; /* Puts relative to nearest non-static ancestor (our relative header anchor) */
|
|
1284
|
-
color: initial; /* Header must have an explicit color or it will get transparent container color */
|
|
1285
|
-
line-height: normal; /* Revert ancestor line height of 0 */
|
|
1286
|
-
}
|
|
1287
|
-
.thead .th div.th-inner {
|
|
1288
|
-
top: ${topToHeadingsStart}em; /* Ensures our header stays fixed at top outside of normal flow of table */
|
|
1289
|
-
}
|
|
1290
|
-
div.inner-caption {
|
|
1291
|
-
top: ${topToCaptionStart}em;
|
|
1292
|
-
}
|
|
1293
|
-
.tfoot .th div.th-inner { /* divs are used as th is supposedly problematic */
|
|
1294
|
-
top: ${topToFooter}; /* Ensures our header stays fixed at top outside of normal flow of table */
|
|
1295
|
-
}
|
|
1296
|
-
.zupa div.zupa1 {
|
|
1297
|
-
margin: 0 auto !important;
|
|
1298
|
-
width: 0 !important;
|
|
1299
|
-
}
|
|
1300
|
-
.zupa div.th-inner, .zupa div.inner-caption {
|
|
1301
|
-
width: 100%;
|
|
1302
|
-
margin-left: -50%;
|
|
1303
|
-
text-align: center;
|
|
1304
|
-
}
|
|
1305
|
-
` : '') + ($pRaw('caption') === 'y' ? tableWithFixedHeaderAndFooter ? '.caption div.inner-caption, ' : '.caption, ' : '') + ($pRaw('header') === 'y' ? tableWithFixedHeaderAndFooter ? `` // `.thead .th, .thead .th div.th-inner, ` // Problems at least in Chrome
|
|
1306
|
-
: `.thead .th, ` : '') + ($pRaw('footer') === 'y' ? tableWithFixedHeaderAndFooter ? `` // `.tfoot .th, .tfoot .th div.th-inner, ` // Problems at least in Chrome
|
|
1307
|
-
: `.tfoot .th, ` : '') + '.tbody td' + ` {
|
|
1308
|
-
vertical-align: top;
|
|
1309
|
-
font-style: ${$pRawEsc('fontstyle')};
|
|
1310
|
-
font-variant: ${$pRawEsc('fontvariant')};
|
|
1311
|
-
font-weight: ${$pEscArbitrary('fontweight')};
|
|
1312
|
-
${$pEscArbitrary('fontsize') ? `font-size: ${$pEscArbitrary('fontsize')};` : ''}
|
|
1313
|
-
font-family: ${$pEscArbitrary('fontSeq')};
|
|
1314
|
-
|
|
1315
|
-
font-stretch: ${$pRawEsc('fontstretch')};
|
|
1316
|
-
letter-spacing: ${$pEscArbitrary('letterspacing')};
|
|
1317
|
-
line-height: ${$pEscArbitrary('lineheight')};
|
|
1318
|
-
${colorEsc ? `color: ${escapeCSS(colorEsc)};` : ''}
|
|
1319
|
-
${bgcolorEsc ? `background-color: ${escapeCSS(bgcolorEsc)};` : ''}
|
|
1320
|
-
}
|
|
1321
|
-
${escapeCSS($pEscArbitrary('pagecss') || '')}
|
|
1322
|
-
` + checkedFieldIndexes.map((idx, i) => ($pRaw('header') === 'y' ? tableWithFixedHeaderAndFooter ? `.thead .th:nth-child(${i + 1}) div.th-inner, ` : `.thead .th:nth-child(${i + 1}), ` : '') + ($pRaw('footer') === 'y' ? tableWithFixedHeaderAndFooter ? `.tfoot .th:nth-child(${i + 1}) div.th-inner, ` : `.tfoot .th:nth-child(${i + 1}), ` : '') + `.tbody td:nth-child(${i + 1}) ` + `{
|
|
1323
|
-
${$pEscArbitrary('css' + (idx + 1))}
|
|
1324
|
-
}
|
|
1325
|
-
`).join('') + ($pEscArbitrary('interlintitle_css') ? `
|
|
1326
|
-
/* http://salzerdesign.com/test/fixedTable.html */
|
|
1327
|
-
.interlintitle {
|
|
1328
|
-
${escapeCSS($pEscArbitrary('interlintitle_css'))}
|
|
1329
|
-
}
|
|
1330
|
-
` : '') + (bgcolorEsc ? `
|
|
1331
|
-
body {
|
|
1332
|
-
background-color: ${bgcolorEsc};
|
|
1333
|
-
}
|
|
1334
|
-
` : '')]];
|
|
1335
|
-
},
|
|
1336
|
-
|
|
1337
|
-
main({
|
|
1338
|
-
tableData,
|
|
1339
|
-
$p,
|
|
1340
|
-
$pRaw,
|
|
1341
|
-
$pRawEsc,
|
|
1342
|
-
$pEscArbitrary,
|
|
1343
|
-
// Todo: escaping should be done in business logic!
|
|
1344
|
-
escapeQuotedCSS,
|
|
1345
|
-
escapeCSS,
|
|
1346
|
-
escapeHTML,
|
|
1347
|
-
l,
|
|
1348
|
-
localizedFieldNames,
|
|
1349
|
-
fieldLangs,
|
|
1350
|
-
fieldDirs,
|
|
1351
|
-
caption,
|
|
1352
|
-
hasCaption,
|
|
1353
|
-
showInterlinTitles,
|
|
1354
|
-
determineEnd,
|
|
1355
|
-
getCanonicalID,
|
|
1356
|
-
canonicalBrowseFieldSetName,
|
|
1357
|
-
getCellValue,
|
|
1358
|
-
checkedAndInterlinearFieldInfo,
|
|
1359
|
-
interlinearSeparator = '<br /><br />'
|
|
1360
|
-
}) {
|
|
1361
|
-
const tableOptions = {
|
|
1362
|
-
table: [['table', {
|
|
1363
|
-
class: 'table',
|
|
1364
|
-
border: $pRaw('border') || '0'
|
|
1365
|
-
}], ['tr', {
|
|
1366
|
-
class: 'tr'
|
|
1367
|
-
}], ['td'], // , {class: 'td'} // Too much data to add class to each
|
|
1368
|
-
['th', {
|
|
1369
|
-
class: 'th'
|
|
1370
|
-
}], ['caption', {
|
|
1371
|
-
class: 'caption'
|
|
1372
|
-
}], ['thead', {
|
|
1373
|
-
class: 'thead'
|
|
1374
|
-
}], ['tbody', {
|
|
1375
|
-
class: 'tbody'
|
|
1376
|
-
}], ['tfoot', {
|
|
1377
|
-
class: 'tfoot'
|
|
1378
|
-
}] // ['colgroup', {class: 'colgroup'}],
|
|
1379
|
-
// ['col', {class: 'col'}]
|
|
1380
|
-
],
|
|
1381
|
-
div: [['div', {
|
|
1382
|
-
class: 'table',
|
|
1383
|
-
style: 'display: table;'
|
|
1384
|
-
}], ['div', {
|
|
1385
|
-
class: 'tr',
|
|
1386
|
-
style: 'display: table-row;'
|
|
1387
|
-
}], ['div', {
|
|
1388
|
-
class: 'td',
|
|
1389
|
-
style: 'display: table-cell;'
|
|
1390
|
-
}], ['div', {
|
|
1391
|
-
class: 'th',
|
|
1392
|
-
style: 'display: table-cell;'
|
|
1393
|
-
}], ['div', {
|
|
1394
|
-
class: 'caption',
|
|
1395
|
-
style: 'display: table-caption;'
|
|
1396
|
-
}], ['div', {
|
|
1397
|
-
class: 'thead',
|
|
1398
|
-
style: 'display: table-header-group;'
|
|
1399
|
-
}], ['div', {
|
|
1400
|
-
class: 'tbody',
|
|
1401
|
-
style: 'display: table-row-group;'
|
|
1402
|
-
}], ['div', {
|
|
1403
|
-
class: 'tfoot',
|
|
1404
|
-
style: 'display: table-footer-group;'
|
|
1405
|
-
}] // ['div', {class: 'colgroup', style: 'display: table-column-group;'}],
|
|
1406
|
-
// ['div', {class: 'col', style: 'display: table-column;'}]
|
|
1407
|
-
],
|
|
1408
|
-
'json-array': 'json',
|
|
1409
|
-
'json-object': 'json'
|
|
1410
|
-
};
|
|
1411
|
-
const outputmode = $p.get('outputmode', true); // Why not $pRaw?
|
|
1412
|
-
|
|
1413
|
-
switch (outputmode) {
|
|
1414
|
-
case 'json-object': // Can later fall through if supporting
|
|
1415
|
-
|
|
1416
|
-
case 'json-array':
|
|
1417
|
-
// tableOptions[outputmode] = tableOptions.table;
|
|
1418
|
-
// break;
|
|
1419
|
-
throw new Error('JSON object support is currently not available');
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
const tableElems = tableOptions[Object.prototype.hasOwnProperty.call(tableOptions, outputmode) ? outputmode : 'table' // Default
|
|
1423
|
-
];
|
|
1424
|
-
const [tableElem, trElem, tdElem, thElem, captionElem, theadElem, tbodyElem, tfootElem] = tableElems; // colgroupElem, colElem
|
|
1425
|
-
|
|
1426
|
-
const [checkedFields, checkedFieldIndexes, allInterlinearColIndexes] = checkedAndInterlinearFieldInfo;
|
|
1427
|
-
const tableWithFixedHeaderAndFooter = $pRaw('headerfooterfixed') === 'yes';
|
|
1428
|
-
|
|
1429
|
-
const tableWrap = children => {
|
|
1430
|
-
return tableWithFixedHeaderAndFooter ? ['div', {
|
|
1431
|
-
class: 'table-responsive anchor-table-header zupa'
|
|
1432
|
-
}, [['div', {
|
|
1433
|
-
class: 'table-responsive anchor-table-body'
|
|
1434
|
-
}, children]]] : ['div', {
|
|
1435
|
-
class: 'table-responsive'
|
|
1436
|
-
}, children];
|
|
1437
|
-
};
|
|
1438
|
-
|
|
1439
|
-
const addChildren = (el, children) => {
|
|
1440
|
-
el = JSON.parse(JSON.stringify(el));
|
|
1441
|
-
el.push(children);
|
|
1442
|
-
return el;
|
|
1443
|
-
};
|
|
1444
|
-
|
|
1445
|
-
const addAtts = ([el, atts], newAtts) => [el, _objectSpread2(_objectSpread2({}, atts), newAtts)];
|
|
1446
|
-
|
|
1447
|
-
const foundState = {
|
|
1448
|
-
start: false,
|
|
1449
|
-
end: false
|
|
1450
|
-
};
|
|
1451
|
-
const outArr = [];
|
|
1452
|
-
const {
|
|
1453
|
-
showEmptyInterlinear,
|
|
1454
|
-
showTitleOnSingleInterlinear
|
|
1455
|
-
} = this;
|
|
1456
|
-
|
|
1457
|
-
const checkEmpty = (tdVal, htmlEscaped) => {
|
|
1458
|
-
if (!showEmptyInterlinear) {
|
|
1459
|
-
if (!htmlEscaped) {
|
|
1460
|
-
tdVal = new DOMParser().parseFromString(tdVal, 'text/html').body;
|
|
1461
|
-
[...tdVal.querySelectorAll('br')].forEach(br => {
|
|
1462
|
-
br.remove();
|
|
1463
|
-
});
|
|
1464
|
-
tdVal = tdVal.innerHTML;
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
if (!tdVal.trim()) {
|
|
1468
|
-
return true;
|
|
1469
|
-
}
|
|
1470
|
-
}
|
|
1471
|
-
};
|
|
1472
|
-
|
|
1473
|
-
tableData.some((tr, i) => {
|
|
1474
|
-
const rowID = determineEnd({
|
|
1475
|
-
tr,
|
|
1476
|
-
foundState
|
|
1477
|
-
});
|
|
1478
|
-
|
|
1479
|
-
if (typeof rowID === 'boolean') {
|
|
1480
|
-
return rowID;
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
const canonicalID = getCanonicalID({
|
|
1484
|
-
tr
|
|
1485
|
-
});
|
|
1486
|
-
outArr.push(addChildren(addAtts(trElem, {
|
|
1487
|
-
dataset: {
|
|
1488
|
-
row: rowID,
|
|
1489
|
-
'canonical-type': canonicalBrowseFieldSetName,
|
|
1490
|
-
'canonical-id': canonicalID
|
|
1491
|
-
}
|
|
1492
|
-
}), checkedFieldIndexes.map((idx, j) => {
|
|
1493
|
-
const interlinearColIndexes = allInterlinearColIndexes[j];
|
|
1494
|
-
const showInterlins = interlinearColIndexes;
|
|
1495
|
-
const {
|
|
1496
|
-
tdVal,
|
|
1497
|
-
htmlEscaped
|
|
1498
|
-
} = getCellValue({
|
|
1499
|
-
tr,
|
|
1500
|
-
idx
|
|
1501
|
-
});
|
|
1502
|
-
const interlins = showInterlins && interlinearColIndexes.map(idx => {
|
|
1503
|
-
// Need to get a new one
|
|
1504
|
-
const {
|
|
1505
|
-
tdVal,
|
|
1506
|
-
htmlEscaped
|
|
1507
|
-
} = getCellValue({
|
|
1508
|
-
tr,
|
|
1509
|
-
idx
|
|
1510
|
-
});
|
|
1511
|
-
console.log('showEmptyInterlinear', showEmptyInterlinear, htmlEscaped);
|
|
1512
|
-
const isEmpty = checkEmpty(tdVal, htmlEscaped);
|
|
1513
|
-
|
|
1514
|
-
if (isEmpty) {
|
|
1515
|
-
return '';
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
return showInterlins ? Templates.resultsDisplayServerOrClient.interlinearSegment({
|
|
1519
|
-
lang: fieldLangs[idx],
|
|
1520
|
-
dir: fieldDirs[idx],
|
|
1521
|
-
html: (showInterlinTitles ? Templates.resultsDisplayServerOrClient.interlinearTitle({
|
|
1522
|
-
l,
|
|
1523
|
-
val: localizedFieldNames[idx]
|
|
1524
|
-
}) : '') + tdVal
|
|
1525
|
-
}) : tdVal;
|
|
1526
|
-
}).filter(cell => cell !== '');
|
|
1527
|
-
return addAtts(tdElem, {
|
|
1528
|
-
// We could remove these (and add to <col>) for optimizing delivery,
|
|
1529
|
-
// but non-table output couldn't use unless on a hidden element
|
|
1530
|
-
// Can't have unique IDs if user duplicates a column
|
|
1531
|
-
id: 'row' + (i + 1) + 'col' + (j + 1),
|
|
1532
|
-
lang: fieldLangs[idx],
|
|
1533
|
-
dir: fieldDirs[idx],
|
|
1534
|
-
dataset: {
|
|
1535
|
-
col: localizedFieldNames[idx]
|
|
1536
|
-
},
|
|
1537
|
-
innerHTML: (showInterlins && !checkEmpty(tdVal, htmlEscaped) && (showTitleOnSingleInterlinear || interlins.length) ? Templates.resultsDisplayServerOrClient.interlinearSegment({
|
|
1538
|
-
lang: fieldLangs[idx],
|
|
1539
|
-
html: (showInterlinTitles ? Templates.resultsDisplayServerOrClient.interlinearTitle({
|
|
1540
|
-
l,
|
|
1541
|
-
val: localizedFieldNames[idx]
|
|
1542
|
-
}) : '') + tdVal
|
|
1543
|
-
}) : tdVal) + (interlinearColIndexes && interlins.length ? interlinearSeparator + interlins.join(interlinearSeparator) : '')
|
|
1544
|
-
});
|
|
1545
|
-
})));
|
|
1546
|
-
return false;
|
|
1547
|
-
});
|
|
1548
|
-
return ['div', [Templates.resultsDisplayServerOrClient.styles({
|
|
1549
|
-
$p,
|
|
1550
|
-
$pRaw,
|
|
1551
|
-
$pRawEsc,
|
|
1552
|
-
$pEscArbitrary,
|
|
1553
|
-
escapeQuotedCSS,
|
|
1554
|
-
escapeCSS,
|
|
1555
|
-
tableWithFixedHeaderAndFooter,
|
|
1556
|
-
checkedFieldIndexes,
|
|
1557
|
-
hasCaption
|
|
1558
|
-
}), tableWrap([addChildren(tableElem, [caption ? addChildren(captionElem, [caption, tableWithFixedHeaderAndFooter ? ['div', {
|
|
1559
|
-
class: 'zupa1'
|
|
1560
|
-
}, [['div', {
|
|
1561
|
-
class: 'inner-caption'
|
|
1562
|
-
}, [['span', [caption]]]]]] : '']) : '',
|
|
1563
|
-
/*
|
|
1564
|
-
// Works but quirky, e.g., `color` doesn't work (as also
|
|
1565
|
-
// confirmed per https://quirksmode.org/css/css2/columns.html)
|
|
1566
|
-
addChildren(colgroupElem,
|
|
1567
|
-
checkedFieldIndexes.map((idx, i) =>
|
|
1568
|
-
addAtts(colElem, {style: $pRaw('css' + (idx + 1))})
|
|
1569
|
-
)
|
|
1570
|
-
),
|
|
1571
|
-
*/
|
|
1572
|
-
$pRaw('header') !== '0' ? addChildren(theadElem, [addChildren(trElem, checkedFields.map((cf, i) => {
|
|
1573
|
-
const interlinearColIndexes = allInterlinearColIndexes[i];
|
|
1574
|
-
cf = escapeHTML(cf) + (interlinearColIndexes ? l('comma-space') + interlinearColIndexes.map(idx => localizedFieldNames[idx]).join(l('comma-space')) : '');
|
|
1575
|
-
return addChildren(thElem, [cf, tableWithFixedHeaderAndFooter ? ['div', {
|
|
1576
|
-
class: 'zupa1'
|
|
1577
|
-
}, [['div', {
|
|
1578
|
-
class: 'th-inner'
|
|
1579
|
-
}, [['span', [cf]]]]]] : '']);
|
|
1580
|
-
}))]) : '', $pRaw('footer') && $pRaw('footer') !== '0' ? addChildren(tfootElem, [addChildren(trElem, checkedFields.map((cf, i) => {
|
|
1581
|
-
const interlinearColIndexes = allInterlinearColIndexes[i];
|
|
1582
|
-
cf = escapeHTML(cf) + (interlinearColIndexes ? l('comma-space') + interlinearColIndexes.map(idx => localizedFieldNames[idx]).join(l('comma-space')) : '');
|
|
1583
|
-
return addChildren(thElem, [cf, tableWithFixedHeaderAndFooter ? ['div', {
|
|
1584
|
-
class: 'zupa1'
|
|
1585
|
-
}, [['div', {
|
|
1586
|
-
class: 'th-inner'
|
|
1587
|
-
}, [['span', [cf]]]]]] : '']);
|
|
1588
|
-
}))]) : '', addChildren(tbodyElem, outArr)])])]];
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1591
|
-
};
|
|
1592
|
-
|
|
1593
|
-
const $ = sel => document.querySelector(sel);
|
|
1594
|
-
|
|
1595
|
-
const $e = (el, descendentsSel) => {
|
|
1596
|
-
el = typeof el === 'string' ? $(el) : el;
|
|
1597
|
-
return el.querySelector(descendentsSel);
|
|
1598
|
-
};
|
|
1599
|
-
|
|
1600
|
-
const _excluded = ["submit", "submitClass"],
|
|
1601
|
-
_excluded2 = ["submit", "cancel", "cancelClass", "submitClass"],
|
|
1602
|
-
_excluded3 = ["message", "submit"];
|
|
1603
|
-
const defaultLocale = 'en';
|
|
1604
|
-
const localeStrings = {
|
|
1605
|
-
en: {
|
|
1606
|
-
submit: 'Submit',
|
|
1607
|
-
cancel: 'Cancel',
|
|
1608
|
-
ok: 'Ok'
|
|
1609
|
-
}
|
|
1610
|
-
};
|
|
1611
|
-
|
|
1612
|
-
class Dialog {
|
|
1613
|
-
constructor({
|
|
1614
|
-
locale,
|
|
1615
|
-
localeObject
|
|
1616
|
-
} = {}) {
|
|
1617
|
-
this.setLocale({
|
|
1618
|
-
locale,
|
|
1619
|
-
localeObject
|
|
1620
|
-
});
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
|
-
setLocale({
|
|
1624
|
-
locale = {},
|
|
1625
|
-
localeObject = {}
|
|
1626
|
-
}) {
|
|
1627
|
-
this.localeStrings = _objectSpread2(_objectSpread2(_objectSpread2({}, localeStrings[defaultLocale]), localeStrings[locale]), localeObject);
|
|
1628
|
-
}
|
|
1629
|
-
|
|
1630
|
-
makeDialog({
|
|
1631
|
-
atts = {},
|
|
1632
|
-
children = [],
|
|
1633
|
-
close,
|
|
1634
|
-
remove = true
|
|
1635
|
-
}) {
|
|
1636
|
-
if (close) {
|
|
1637
|
-
if (!atts.$on) {
|
|
1638
|
-
atts.$on = {};
|
|
1639
|
-
}
|
|
1640
|
-
|
|
1641
|
-
if (!atts.$on.close) {
|
|
1642
|
-
atts.$on.close = close;
|
|
1643
|
-
}
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
const dialog = jamilih.jml('dialog', atts, children, jamilih.$('#main'));
|
|
1647
|
-
dialog.showModal();
|
|
1648
|
-
|
|
1649
|
-
if (remove) {
|
|
1650
|
-
dialog.addEventListener('close', () => {
|
|
1651
|
-
dialog.remove();
|
|
1652
|
-
});
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
|
-
return dialog;
|
|
1656
|
-
}
|
|
1657
|
-
|
|
1658
|
-
makeSubmitDialog(_ref) {
|
|
1659
|
-
let {
|
|
1660
|
-
submit,
|
|
1661
|
-
// Don't pass this on to `args` if present
|
|
1662
|
-
submitClass = 'submit'
|
|
1663
|
-
} = _ref,
|
|
1664
|
-
args = _objectWithoutProperties(_ref, _excluded);
|
|
1665
|
-
|
|
1666
|
-
const dialog = this.makeCancelDialog(args);
|
|
1667
|
-
$e(dialog, `button.${args.cancelClass || 'cancel'}`).before(jamilih.jml('button', {
|
|
1668
|
-
class: submitClass,
|
|
1669
|
-
$on: {
|
|
1670
|
-
click(e) {
|
|
1671
|
-
if (submit) {
|
|
1672
|
-
submit.call(this, {
|
|
1673
|
-
e,
|
|
1674
|
-
dialog
|
|
1675
|
-
});
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
}
|
|
1680
|
-
}, [this.localeStrings.submit]), jamilih.nbsp.repeat(2));
|
|
1681
|
-
return dialog;
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
makeCancelDialog(_ref2) {
|
|
1685
|
-
let {
|
|
1686
|
-
submit,
|
|
1687
|
-
// Don't pass this on to `args` if present
|
|
1688
|
-
cancel,
|
|
1689
|
-
cancelClass = 'cancel',
|
|
1690
|
-
submitClass = 'submit'
|
|
1691
|
-
} = _ref2,
|
|
1692
|
-
args = _objectWithoutProperties(_ref2, _excluded2);
|
|
1693
|
-
|
|
1694
|
-
const dialog = this.makeDialog(args);
|
|
1695
|
-
jamilih.jml('div', {
|
|
1696
|
-
class: submitClass
|
|
1697
|
-
}, [['br'], ['br'], ['button', {
|
|
1698
|
-
class: cancelClass,
|
|
1699
|
-
$on: {
|
|
1700
|
-
click(e) {
|
|
1701
|
-
e.preventDefault();
|
|
1702
|
-
|
|
1703
|
-
if (cancel) {
|
|
1704
|
-
if (cancel.call(this, {
|
|
1705
|
-
e,
|
|
1706
|
-
dialog
|
|
1707
|
-
}) === false) {
|
|
1708
|
-
return;
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
|
|
1712
|
-
dialog.close();
|
|
1713
|
-
}
|
|
1714
|
-
|
|
1715
|
-
}
|
|
1716
|
-
}, [this.localeStrings.cancel]]], dialog);
|
|
1717
|
-
return dialog;
|
|
1718
|
-
}
|
|
1719
|
-
|
|
1720
|
-
alert(message, ok) {
|
|
1721
|
-
message = typeof message === 'string' ? {
|
|
1722
|
-
message
|
|
1723
|
-
} : message;
|
|
1724
|
-
const {
|
|
1725
|
-
ok: includeOk = typeof ok === 'object' ? ok.ok !== false : ok !== false,
|
|
1726
|
-
message: msg,
|
|
1727
|
-
submitClass = 'submit'
|
|
1728
|
-
} = message;
|
|
1729
|
-
return new Promise((resolve, reject) => {
|
|
1730
|
-
const dialog = jamilih.jml('dialog', [msg, ...(includeOk ? [['br'], ['br'], ['div', {
|
|
1731
|
-
class: submitClass
|
|
1732
|
-
}, [['button', {
|
|
1733
|
-
$on: {
|
|
1734
|
-
click() {
|
|
1735
|
-
dialog.close();
|
|
1736
|
-
resolve();
|
|
1737
|
-
}
|
|
1738
|
-
|
|
1739
|
-
}
|
|
1740
|
-
}, [this.localeStrings.ok]]]]] : [])], jamilih.$('#main'));
|
|
1741
|
-
dialog.showModal();
|
|
1742
|
-
});
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1745
|
-
prompt(message) {
|
|
1746
|
-
message = typeof message === 'string' ? {
|
|
1747
|
-
message
|
|
1748
|
-
} : message;
|
|
1749
|
-
|
|
1750
|
-
const {
|
|
1751
|
-
message: msg,
|
|
1752
|
-
submit: userSubmit
|
|
1753
|
-
} = message,
|
|
1754
|
-
submitArgs = _objectWithoutProperties(message, _excluded3);
|
|
1755
|
-
|
|
1756
|
-
return new Promise((resolve, reject) => {
|
|
1757
|
-
const submit = function ({
|
|
1758
|
-
e,
|
|
1759
|
-
dialog
|
|
1760
|
-
}) {
|
|
1761
|
-
if (userSubmit) {
|
|
1762
|
-
userSubmit.call(this, {
|
|
1763
|
-
e,
|
|
1764
|
-
dialog
|
|
1765
|
-
});
|
|
1766
|
-
}
|
|
1767
|
-
|
|
1768
|
-
dialog.close();
|
|
1769
|
-
resolve($e(dialog, 'input').value);
|
|
1770
|
-
};
|
|
1771
|
-
/* const dialog = */
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
this.makeSubmitDialog(_objectSpread2(_objectSpread2({}, submitArgs), {}, {
|
|
1775
|
-
submit,
|
|
1776
|
-
|
|
1777
|
-
cancel() {
|
|
1778
|
-
reject(new Error('cancelled'));
|
|
1779
|
-
},
|
|
1780
|
-
|
|
1781
|
-
children: [['label', [msg, jamilih.nbsp.repeat(3), ['input']]]]
|
|
1782
|
-
}));
|
|
1783
|
-
});
|
|
1784
|
-
}
|
|
1785
|
-
|
|
1786
|
-
confirm(message) {
|
|
1787
|
-
message = typeof message === 'string' ? {
|
|
1788
|
-
message
|
|
1789
|
-
} : message;
|
|
1790
|
-
const {
|
|
1791
|
-
message: msg,
|
|
1792
|
-
submitClass = 'submit'
|
|
1793
|
-
} = message;
|
|
1794
|
-
return new Promise((resolve, reject) => {
|
|
1795
|
-
const dialog = jamilih.jml('dialog', [msg, ['br'], ['br'], ['div', {
|
|
1796
|
-
class: submitClass
|
|
1797
|
-
}, [['button', {
|
|
1798
|
-
$on: {
|
|
1799
|
-
click() {
|
|
1800
|
-
dialog.close();
|
|
1801
|
-
resolve();
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
}
|
|
1805
|
-
}, [this.localeStrings.ok]], jamilih.nbsp.repeat(2), ['button', {
|
|
1806
|
-
$on: {
|
|
1807
|
-
click() {
|
|
1808
|
-
dialog.close();
|
|
1809
|
-
reject(new Error('cancelled'));
|
|
1810
|
-
}
|
|
1811
|
-
|
|
1812
|
-
}
|
|
1813
|
-
}, [this.localeStrings.cancel]]]]], jamilih.$('#main'));
|
|
1814
|
-
dialog.showModal();
|
|
1815
|
-
});
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
const dialogs = new Dialog();
|
|
1821
|
-
|
|
1822
|
-
var resultsDisplayClient = {
|
|
1823
|
-
anchorRowCol({
|
|
1824
|
-
anchorRowCol
|
|
1825
|
-
}) {
|
|
1826
|
-
return jamilih.$('#' + anchorRowCol);
|
|
1827
|
-
},
|
|
1828
|
-
|
|
1829
|
-
anchors({
|
|
1830
|
-
escapedRow,
|
|
1831
|
-
escapedCol
|
|
1832
|
-
}) {
|
|
1833
|
-
const sel = 'tr[data-row="' + escapedRow + '"]' + (escapedCol ? '> td[data-col="' + escapedCol + '"]' : '');
|
|
1834
|
-
return jamilih.$(sel);
|
|
1835
|
-
},
|
|
1836
|
-
|
|
1837
|
-
main(...args) {
|
|
1838
|
-
let html;
|
|
1839
|
-
|
|
1840
|
-
try {
|
|
1841
|
-
html = Templates.resultsDisplayServerOrClient.main(...args);
|
|
1842
|
-
} catch (err) {
|
|
1843
|
-
if (err.message === 'JSON support is currently not available') {
|
|
1844
|
-
dialogs.alert(err.message);
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1847
|
-
|
|
1848
|
-
jamilih.jml(...html, jamilih.$('#main'));
|
|
1849
|
-
}
|
|
1850
|
-
|
|
1851
|
-
};
|
|
1852
|
-
|
|
1853
|
-
/* eslint-env browser */
|
|
1854
|
-
const Templates = {
|
|
1855
|
-
languageSelect,
|
|
1856
|
-
workSelect,
|
|
1857
|
-
workDisplay,
|
|
1858
|
-
resultsDisplayServerOrClient: resultsDisplayServerOrClient$1,
|
|
1859
|
-
resultsDisplayClient,
|
|
1860
|
-
|
|
1861
|
-
defaultBody() {
|
|
1862
|
-
// We empty rather than `replaceWith` as our Jamilih `body`
|
|
1863
|
-
// aliases expect the old instance
|
|
1864
|
-
while (jamilih.body.hasChildNodes()) {
|
|
1865
|
-
jamilih.body.firstChild.remove();
|
|
1866
|
-
}
|
|
1867
|
-
|
|
1868
|
-
return jamilih.jml('div', {
|
|
1869
|
-
id: 'main',
|
|
1870
|
-
role: 'main'
|
|
1871
|
-
}, jamilih.body);
|
|
1872
|
-
}
|
|
1873
|
-
|
|
1874
|
-
};
|
|
1875
|
-
Templates.permissions = {
|
|
1876
|
-
versionChange() {
|
|
1877
|
-
jamilih.$('#versionChange').showModal();
|
|
1878
|
-
},
|
|
1879
|
-
|
|
1880
|
-
addLogEntry({
|
|
1881
|
-
text
|
|
1882
|
-
}) {
|
|
1883
|
-
const installationDialog = jamilih.$('#installationLogContainer');
|
|
1884
|
-
|
|
1885
|
-
try {
|
|
1886
|
-
installationDialog.showModal();
|
|
1887
|
-
const container = jamilih.$('#dialogContainer');
|
|
1888
|
-
container.hidden = false;
|
|
1889
|
-
} catch (err) {// May already be open
|
|
1890
|
-
}
|
|
1891
|
-
|
|
1892
|
-
jamilih.$('#installationLog').append(text + '\n');
|
|
1893
|
-
},
|
|
1894
|
-
|
|
1895
|
-
exitDialogs() {
|
|
1896
|
-
const container = jamilih.$('#dialogContainer');
|
|
1897
|
-
|
|
1898
|
-
if (container) {
|
|
1899
|
-
container.hidden = true;
|
|
1900
|
-
}
|
|
1901
|
-
},
|
|
1902
|
-
|
|
1903
|
-
dbError({
|
|
1904
|
-
type,
|
|
1905
|
-
escapedErrorMessage
|
|
1906
|
-
}) {
|
|
1907
|
-
if (type) {
|
|
1908
|
-
jamilih.jml('span', [type, ' ', escapedErrorMessage], jamilih.$('#dbError'));
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
jamilih.$('#dbError').showModal();
|
|
1912
|
-
},
|
|
1913
|
-
|
|
1914
|
-
errorRegistering(escapedErrorMessage) {
|
|
1915
|
-
if (escapedErrorMessage) {
|
|
1916
|
-
jamilih.jml('span', [escapedErrorMessage], jamilih.$('#errorRegistering'));
|
|
1917
|
-
}
|
|
1918
|
-
|
|
1919
|
-
jamilih.$('#errorRegistering').showModal();
|
|
1920
|
-
},
|
|
1921
|
-
|
|
1922
|
-
browserNotGrantingPersistence() {
|
|
1923
|
-
jamilih.$('#browserNotGrantingPersistence').showModal();
|
|
1924
|
-
},
|
|
1925
|
-
|
|
1926
|
-
main({
|
|
1927
|
-
l,
|
|
1928
|
-
ok,
|
|
1929
|
-
refuse,
|
|
1930
|
-
close,
|
|
1931
|
-
closeBrowserNotGranting
|
|
1932
|
-
}) {
|
|
1933
|
-
const installationDialog = jamilih.jml('dialog', {
|
|
1934
|
-
style: 'text-align: center; height: 100%',
|
|
1935
|
-
id: 'installationLogContainer',
|
|
1936
|
-
class: 'focus'
|
|
1937
|
-
}, [['p', [l('wait-installing')]], ['div', {
|
|
1938
|
-
style: 'height: 80%; overflow: auto;'
|
|
1939
|
-
}, [['pre', {
|
|
1940
|
-
id: 'installationLog'
|
|
1941
|
-
}, []]]] // ['textarea', {readonly: true, style: 'width: 80%; height: 80%;'}]
|
|
1942
|
-
]);
|
|
1943
|
-
let requestPermissionsDialog = '';
|
|
1944
|
-
|
|
1945
|
-
if (ok) {
|
|
1946
|
-
requestPermissionsDialog = jamilih.jml('dialog', {
|
|
1947
|
-
id: 'willRequestStoragePermissions',
|
|
1948
|
-
$on: {
|
|
1949
|
-
close
|
|
1950
|
-
}
|
|
1951
|
-
}, [['section', [l('will-request-storage-permissions')]], ['br'], ['div', {
|
|
1952
|
-
class: 'focus'
|
|
1953
|
-
}, [['button', {
|
|
1954
|
-
$on: {
|
|
1955
|
-
click: ok
|
|
1956
|
-
}
|
|
1957
|
-
}, [l('OK')]], ['br'], ['br'], ['button', {
|
|
1958
|
-
$on: {
|
|
1959
|
-
click: refuse
|
|
1960
|
-
}
|
|
1961
|
-
}, [l('refuse-request-storage-permissions')]]]]]);
|
|
1962
|
-
}
|
|
1963
|
-
|
|
1964
|
-
const errorRegisteringNotice = jamilih.jml('dialog', {
|
|
1965
|
-
id: 'errorRegistering'
|
|
1966
|
-
}, [['section', [l('errorRegistering')]]]);
|
|
1967
|
-
let browserNotGrantingPersistenceAlert = '';
|
|
1968
|
-
|
|
1969
|
-
if (closeBrowserNotGranting) {
|
|
1970
|
-
browserNotGrantingPersistenceAlert = jamilih.jml('dialog', {
|
|
1971
|
-
id: 'browserNotGrantingPersistence'
|
|
1972
|
-
}, [['section', [l('browser-not-granting-persistence')]], ['br'], ['div', {
|
|
1973
|
-
class: 'focus'
|
|
1974
|
-
}, [['button', {
|
|
1975
|
-
$on: {
|
|
1976
|
-
click: closeBrowserNotGranting
|
|
1977
|
-
}
|
|
1978
|
-
}, [l('OK')]]]]]);
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
|
-
const versionChangeNotice = jamilih.jml('dialog', {
|
|
1982
|
-
id: 'versionChange'
|
|
1983
|
-
}, [['section', [l('versionChange')]]]);
|
|
1984
|
-
const dbErrorNotice = jamilih.jml('dialog', {
|
|
1985
|
-
id: 'dbError'
|
|
1986
|
-
}, [['section', [l('dbError')]]]);
|
|
1987
|
-
jamilih.jml('div', {
|
|
1988
|
-
id: 'dialogContainer',
|
|
1989
|
-
style: 'height: 100%'
|
|
1990
|
-
}, [installationDialog, requestPermissionsDialog, browserNotGrantingPersistenceAlert, errorRegisteringNotice, versionChangeNotice, dbErrorNotice], jamilih.$('#main'));
|
|
1991
|
-
return [installationDialog, requestPermissionsDialog, browserNotGrantingPersistenceAlert, errorRegisteringNotice, versionChangeNotice, dbErrorNotice];
|
|
1992
|
-
}
|
|
1993
|
-
|
|
1994
|
-
};
|
|
1995
|
-
|
|
1996
|
-
const escapeHTML = s => {
|
|
1997
|
-
return !s ? '' : s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1998
|
-
};
|
|
1999
|
-
|
|
2000
|
-
/* eslint-env browser */
|
|
2001
|
-
// Todo: remember this locales choice by cookie?
|
|
2002
|
-
const getPreferredLanguages = ({
|
|
2003
|
-
namespace,
|
|
2004
|
-
preferredLocale
|
|
2005
|
-
}) => {
|
|
2006
|
-
// Todo: Add to this optionally with one-off tag input box
|
|
2007
|
-
// Todo: Switch to fallbackLanguages so can default to
|
|
2008
|
-
// navigator.languages?
|
|
2009
|
-
const langCodes = localStorage.getItem(namespace + '-langCodes');
|
|
2010
|
-
const lngs = langCodes && JSON.parse(langCodes) || [preferredLocale];
|
|
2011
|
-
const langArr = [];
|
|
2012
|
-
lngs.forEach(lng => {
|
|
2013
|
-
// Todo: Check for multiple separate hyphenated
|
|
2014
|
-
// groupings (for each supplied language)
|
|
2015
|
-
const higherLocale = lng.replace(/-.*$/, '');
|
|
2016
|
-
|
|
2017
|
-
if (higherLocale === lng) {
|
|
2018
|
-
langArr.push(lng);
|
|
2019
|
-
} else {
|
|
2020
|
-
langArr.push(lng, higherLocale);
|
|
2021
|
-
}
|
|
2022
|
-
});
|
|
2023
|
-
return langArr;
|
|
2024
|
-
};
|
|
2025
|
-
class Languages {
|
|
2026
|
-
constructor({
|
|
2027
|
-
langData
|
|
2028
|
-
}) {
|
|
2029
|
-
this.langData = langData;
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
localeFromLangData(lan) {
|
|
2033
|
-
return this.langData['localization-strings'][lan];
|
|
2034
|
-
}
|
|
2035
|
-
|
|
2036
|
-
getLanguageFromCode(code) {
|
|
2037
|
-
return this.localeFromLangData(code).languages[code];
|
|
2038
|
-
}
|
|
2039
|
-
|
|
2040
|
-
getFieldNameFromPluginNameAndLocales({
|
|
2041
|
-
pluginName,
|
|
2042
|
-
locales,
|
|
2043
|
-
lf,
|
|
2044
|
-
targetLanguage,
|
|
2045
|
-
applicableFieldI18N,
|
|
2046
|
-
meta,
|
|
2047
|
-
metaApplicableField
|
|
2048
|
-
}) {
|
|
2049
|
-
return lf(['plugins', pluginName, 'fieldname'], _objectSpread2(_objectSpread2(_objectSpread2({}, meta), metaApplicableField), {}, {
|
|
2050
|
-
applicableField: applicableFieldI18N,
|
|
2051
|
-
targetLanguage: targetLanguage ? this.getLanguageFromCode(targetLanguage) : ''
|
|
2052
|
-
}));
|
|
2053
|
-
}
|
|
2054
|
-
|
|
2055
|
-
getLanguageInfo({
|
|
2056
|
-
$p
|
|
2057
|
-
}) {
|
|
2058
|
-
const langs = this.langData.languages;
|
|
2059
|
-
|
|
2060
|
-
const localePass = lcl => {
|
|
2061
|
-
return langs.some(({
|
|
2062
|
-
code
|
|
2063
|
-
}) => code === lcl) ? lcl : false;
|
|
2064
|
-
};
|
|
2065
|
-
|
|
2066
|
-
const languageParam = $p.get('lang', true); // Todo: We could (unless overridden by another button) assume the
|
|
2067
|
-
// browser language based on fallbackLanguages instead
|
|
2068
|
-
// of giving a choice
|
|
2069
|
-
|
|
2070
|
-
const navLangs = navigator.languages.filter(localePass);
|
|
2071
|
-
const fallbackLanguages = navLangs.length ? navLangs : [localePass(navigator.language) || 'en-US']; // We need a default to display a default title
|
|
2072
|
-
|
|
2073
|
-
const language = languageParam || fallbackLanguages[0];
|
|
2074
|
-
const preferredLangs = language.split('.');
|
|
2075
|
-
const lang = [...preferredLangs, ...fallbackLanguages];
|
|
2076
|
-
return {
|
|
2077
|
-
lang,
|
|
2078
|
-
langs,
|
|
2079
|
-
languageParam,
|
|
2080
|
-
fallbackLanguages
|
|
2081
|
-
};
|
|
2082
|
-
}
|
|
2083
|
-
|
|
2084
|
-
}
|
|
2085
|
-
|
|
2086
|
-
/* eslint-env browser */
|
|
2087
|
-
|
|
2088
|
-
const JsonRefs$1 = require('json-refs'); // eslint-disable-line import/order
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
const getCurrDir = () => window.location.href.replace(/(index\.html)?#.*$/, '');
|
|
2092
|
-
|
|
2093
|
-
const getMetaProp = function getMetaProp(lang, metadataObj, properties, allowObjects) {
|
|
2094
|
-
let prop;
|
|
2095
|
-
properties = typeof properties === 'string' ? [properties] : properties;
|
|
2096
|
-
lang.some(lan => {
|
|
2097
|
-
const p = [...properties];
|
|
2098
|
-
let strings = metadataObj['localization-strings'][lan];
|
|
2099
|
-
|
|
2100
|
-
while (strings && p.length) {
|
|
2101
|
-
strings = strings[p.shift()];
|
|
2102
|
-
} // Todo: Fix this allowance for allowObjects (as it does not properly
|
|
2103
|
-
// fallback if an object is returned from a language because
|
|
2104
|
-
// that language is missing content and is only thus returning
|
|
2105
|
-
// an object)
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
prop = allowObjects || typeof strings === 'string' ? strings : undefined;
|
|
2109
|
-
return prop;
|
|
2110
|
-
});
|
|
2111
|
-
return prop;
|
|
2112
|
-
}; // Use the following to dynamically add specific file schema in place of
|
|
2113
|
-
// generic table schema if validating against files.jsonschema
|
|
2114
|
-
// filesSchema.properties.groups.items.properties.files.items.properties.
|
|
2115
|
-
// file.anyOf.splice(1, 1, {$ref: schemaFile});
|
|
2116
|
-
// Todo: Allow use of dbs and fileGroup together in base directories?
|
|
2117
|
-
|
|
2118
|
-
const getMetadata = async (file, property, basePath) => {
|
|
2119
|
-
const url = new URL(basePath || getCurrDir());
|
|
2120
|
-
url.search = ''; // Clear out query string, e.g., `?fbclid` from Facebook
|
|
2121
|
-
|
|
2122
|
-
url.pathname = file;
|
|
2123
|
-
url.hash = property ? '#/' + property : '';
|
|
2124
|
-
return (await JsonRefs$1.resolveRefsAt(url.toString(), {
|
|
2125
|
-
loaderOptions: {
|
|
2126
|
-
processContent(res, callback) {
|
|
2127
|
-
callback(undefined, JSON.parse(res.text || // `.metadata` not a recognized extension, so
|
|
2128
|
-
// convert to string for JSON in Node
|
|
2129
|
-
res.body.toString()));
|
|
2130
|
-
}
|
|
2131
|
-
|
|
2132
|
-
}
|
|
2133
|
-
})).resolved;
|
|
2134
|
-
};
|
|
2135
|
-
const getFieldNameAndValueAliases = function ({
|
|
2136
|
-
field,
|
|
2137
|
-
schemaItems,
|
|
2138
|
-
metadataObj,
|
|
2139
|
-
getFieldAliasOrName,
|
|
2140
|
-
lang
|
|
2141
|
-
}) {
|
|
2142
|
-
const fieldSchemaIndex = schemaItems.findIndex(item => item.title === field);
|
|
2143
|
-
const fieldSchema = schemaItems[fieldSchemaIndex];
|
|
2144
|
-
const ret = {
|
|
2145
|
-
// field,
|
|
2146
|
-
fieldName: getFieldAliasOrName(field)
|
|
2147
|
-
};
|
|
2148
|
-
const fieldInfo = metadataObj.fields[field];
|
|
2149
|
-
let fieldValueAliasMap = fieldInfo && fieldInfo['fieldvalue-aliases'];
|
|
2150
|
-
|
|
2151
|
-
if (fieldValueAliasMap) {
|
|
2152
|
-
if (fieldValueAliasMap.localeKey) {
|
|
2153
|
-
fieldValueAliasMap = getMetaProp(lang, metadataObj, fieldValueAliasMap.localeKey.split('/'), true);
|
|
2154
|
-
}
|
|
2155
|
-
|
|
2156
|
-
ret.rawFieldValueAliasMap = JSON.parse(JSON.stringify(fieldValueAliasMap));
|
|
2157
|
-
ret.aliases = []; // Todo: We could use `prefer_alias` but algorithm below may cover
|
|
2158
|
-
// needed cases
|
|
2159
|
-
|
|
2160
|
-
if (fieldSchema.enum && fieldSchema.enum.length) {
|
|
2161
|
-
fieldSchema.enum.forEach(enm => {
|
|
2162
|
-
ret.aliases.push(getMetaProp(lang, metadataObj, ['fieldvalue', field, enm], true));
|
|
2163
|
-
|
|
2164
|
-
if (enm in fieldValueAliasMap && // Todo: We could allow numbers here too, but crowds
|
|
2165
|
-
// pull-down
|
|
2166
|
-
typeof fieldValueAliasMap[enm] === 'string') {
|
|
2167
|
-
ret.aliases.push(...fieldValueAliasMap[enm]);
|
|
2168
|
-
}
|
|
2169
|
-
});
|
|
2170
|
-
} else {
|
|
2171
|
-
// Todo: We might iterate over all values (in case some not
|
|
2172
|
-
// included in fv map)
|
|
2173
|
-
// Todo: Check `fieldSchema` for integer or string type
|
|
2174
|
-
Object.entries(fieldValueAliasMap).forEach(([key, aliases]) => {
|
|
2175
|
-
// We'll preserve the numbers since probably more useful if
|
|
2176
|
-
// stored with data (as opposed to enums)
|
|
2177
|
-
if (!Array.isArray(aliases)) {
|
|
2178
|
-
aliases = Object.values(aliases);
|
|
2179
|
-
} // We'll assume the longest version is best for auto-complete
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
ret.aliases.push(...aliases.filter(v => aliases.every(x => x === v || !x.toLowerCase().startsWith(v.toLowerCase()))).map(v => v + ' (' + key + ')') // Todo: i18nize
|
|
2183
|
-
);
|
|
2184
|
-
});
|
|
2185
|
-
}
|
|
2186
|
-
|
|
2187
|
-
ret.fieldValueAliasMap = JSON.parse(JSON.stringify(fieldValueAliasMap)); // ret.aliases.sort();
|
|
2188
|
-
}
|
|
2189
|
-
|
|
2190
|
-
ret.fieldSchema = fieldSchema;
|
|
2191
|
-
ret.fieldSchemaIndex = fieldSchemaIndex;
|
|
2192
|
-
ret.preferAlias = fieldInfo.prefer_alias;
|
|
2193
|
-
ret.lang = fieldInfo.lang;
|
|
2194
|
-
return ret;
|
|
2195
|
-
};
|
|
2196
|
-
const getBrowseFieldData = function ({
|
|
2197
|
-
metadataObj,
|
|
2198
|
-
schemaItems,
|
|
2199
|
-
getFieldAliasOrName,
|
|
2200
|
-
lang,
|
|
2201
|
-
callback
|
|
2202
|
-
}) {
|
|
2203
|
-
metadataObj.table.browse_fields.forEach((browseFieldSetObject, i) => {
|
|
2204
|
-
if (typeof browseFieldSetObject === 'string') {
|
|
2205
|
-
browseFieldSetObject = {
|
|
2206
|
-
set: [browseFieldSetObject]
|
|
2207
|
-
};
|
|
2208
|
-
}
|
|
2209
|
-
|
|
2210
|
-
if (!browseFieldSetObject.name) {
|
|
2211
|
-
browseFieldSetObject.name = browseFieldSetObject.set.join(',');
|
|
2212
|
-
}
|
|
2213
|
-
|
|
2214
|
-
const setName = browseFieldSetObject.name;
|
|
2215
|
-
const fieldSets = browseFieldSetObject.set;
|
|
2216
|
-
const {
|
|
2217
|
-
presort
|
|
2218
|
-
} = browseFieldSetObject; // Todo: Deal with ['td', [['h3', [ld(browseFieldObject.name)]]]]
|
|
2219
|
-
// as kind of fieldset
|
|
2220
|
-
|
|
2221
|
-
const browseFields = fieldSets.map(field => getFieldNameAndValueAliases({
|
|
2222
|
-
lang,
|
|
2223
|
-
field,
|
|
2224
|
-
schemaItems,
|
|
2225
|
-
metadataObj,
|
|
2226
|
-
getFieldAliasOrName
|
|
2227
|
-
}));
|
|
2228
|
-
callback({
|
|
2229
|
-
setName,
|
|
2230
|
-
browseFields,
|
|
2231
|
-
i,
|
|
2232
|
-
presort
|
|
2233
|
-
}); // eslint-disable-line node/no-callback-literal
|
|
2234
|
-
});
|
|
2235
|
-
}; // Todo: Incorporate other methods into this class
|
|
2236
|
-
|
|
2237
|
-
class Metadata {
|
|
2238
|
-
constructor({
|
|
2239
|
-
metadataObj
|
|
2240
|
-
}) {
|
|
2241
|
-
this.metadataObj = metadataObj;
|
|
2242
|
-
}
|
|
2243
|
-
|
|
2244
|
-
getFieldLang(field) {
|
|
2245
|
-
const {
|
|
2246
|
-
metadataObj
|
|
2247
|
-
} = this;
|
|
2248
|
-
const fields = metadataObj && metadataObj.fields;
|
|
2249
|
-
return fields && fields[field] && fields[field].lang;
|
|
2250
|
-
}
|
|
2251
|
-
|
|
2252
|
-
getFieldMatchesLocale({
|
|
2253
|
-
namespace,
|
|
2254
|
-
preferredLocale,
|
|
2255
|
-
schemaItems,
|
|
2256
|
-
pluginsForWork
|
|
2257
|
-
}) {
|
|
2258
|
-
const {
|
|
2259
|
-
metadataObj
|
|
2260
|
-
} = this;
|
|
2261
|
-
return field => {
|
|
2262
|
-
const preferredLanguages = getPreferredLanguages({
|
|
2263
|
-
namespace,
|
|
2264
|
-
preferredLocale
|
|
2265
|
-
});
|
|
2266
|
-
|
|
2267
|
-
if (pluginsForWork.isPluginField({
|
|
2268
|
-
namespace,
|
|
2269
|
-
field
|
|
2270
|
-
})) {
|
|
2271
|
-
let [,, targetLanguage] = pluginsForWork.getPluginFieldParts({
|
|
2272
|
-
namespace,
|
|
2273
|
-
field
|
|
2274
|
-
});
|
|
2275
|
-
|
|
2276
|
-
if (targetLanguage === '{locale}') {
|
|
2277
|
-
targetLanguage = preferredLocale;
|
|
2278
|
-
}
|
|
2279
|
-
|
|
2280
|
-
return !targetLanguage || preferredLanguages.includes(targetLanguage);
|
|
2281
|
-
}
|
|
2282
|
-
|
|
2283
|
-
const metaLang = this.getFieldLang(field);
|
|
2284
|
-
const localeStrings = metadataObj && metadataObj['localization-strings']; // If this is a localized field (e.g., enum), we don't want
|
|
2285
|
-
// to avoid as may be translated (should check though)
|
|
2286
|
-
|
|
2287
|
-
const hasFieldValue = localeStrings && Object.keys(localeStrings).some(lng => {
|
|
2288
|
-
const fv = localeStrings[lng] && localeStrings[lng].fieldvalue;
|
|
2289
|
-
return fv && fv[field];
|
|
2290
|
-
});
|
|
2291
|
-
return hasFieldValue || metaLang && preferredLanguages.includes(metaLang) || schemaItems.some(item => item.title === field && item.type !== 'string');
|
|
2292
|
-
};
|
|
2293
|
-
}
|
|
2294
|
-
|
|
2295
|
-
}
|
|
2296
|
-
|
|
2297
|
-
const escapePluginComponent = pluginName => {
|
|
2298
|
-
return pluginName.replace(/\^/g, '^^') // Escape our escape
|
|
2299
|
-
.replace(/-/g, '^0');
|
|
2300
|
-
};
|
|
2301
|
-
const unescapePluginComponent = pluginName => {
|
|
2302
|
-
if (!pluginName) {
|
|
2303
|
-
return pluginName;
|
|
2304
|
-
}
|
|
2305
|
-
|
|
2306
|
-
return pluginName.replace(/(\^+)0/g, (n0, esc) => {
|
|
2307
|
-
return esc.length % 2 ? esc.slice(1) + '-' : n0;
|
|
2308
|
-
}).replace(/\^\^/g, '^');
|
|
2309
|
-
};
|
|
2310
|
-
const escapePlugin = ({
|
|
2311
|
-
pluginName,
|
|
2312
|
-
applicableField,
|
|
2313
|
-
targetLanguage
|
|
2314
|
-
}) => {
|
|
2315
|
-
return escapePluginComponent(pluginName) + (applicableField ? '-' + escapePluginComponent(applicableField) : '-') + (targetLanguage ? '-' + escapePluginComponent(targetLanguage) : '');
|
|
2316
|
-
};
|
|
2317
|
-
class PluginsForWork {
|
|
2318
|
-
constructor({
|
|
2319
|
-
pluginsInWork,
|
|
2320
|
-
pluginFieldMappings,
|
|
2321
|
-
pluginObjects
|
|
2322
|
-
}) {
|
|
2323
|
-
this.pluginsInWork = pluginsInWork;
|
|
2324
|
-
this.pluginFieldMappings = pluginFieldMappings;
|
|
2325
|
-
this.pluginObjects = pluginObjects;
|
|
2326
|
-
}
|
|
2327
|
-
|
|
2328
|
-
getPluginObject(pluginName) {
|
|
2329
|
-
const idx = this.pluginsInWork.findIndex(([name]) => {
|
|
2330
|
-
return name === pluginName;
|
|
2331
|
-
});
|
|
2332
|
-
const plugin = this.pluginObjects[idx];
|
|
2333
|
-
return plugin;
|
|
2334
|
-
}
|
|
2335
|
-
|
|
2336
|
-
iterateMappings(cb) {
|
|
2337
|
-
this.pluginFieldMappings.forEach(({
|
|
2338
|
-
placement,
|
|
2339
|
-
|
|
2340
|
-
/*
|
|
2341
|
-
{fieldXYZ: {
|
|
2342
|
-
targetLanguage: "en"|["en"], // E.g., translating from Persian to English
|
|
2343
|
-
onByDefault: true // Overrides plugin default
|
|
2344
|
-
}}
|
|
2345
|
-
*/
|
|
2346
|
-
'applicable-fields': applicableFields
|
|
2347
|
-
}, i) => {
|
|
2348
|
-
const [pluginName, {
|
|
2349
|
-
onByDefault: onByDefaultDefault,
|
|
2350
|
-
lang: pluginLang,
|
|
2351
|
-
meta
|
|
2352
|
-
}] = this.pluginsInWork[i];
|
|
2353
|
-
const plugin = this.getPluginObject(pluginName);
|
|
2354
|
-
cb({
|
|
2355
|
-
// eslint-disable-line node/no-callback-literal
|
|
2356
|
-
plugin,
|
|
2357
|
-
placement,
|
|
2358
|
-
applicableFields,
|
|
2359
|
-
pluginName,
|
|
2360
|
-
pluginLang,
|
|
2361
|
-
onByDefaultDefault,
|
|
2362
|
-
meta
|
|
2363
|
-
});
|
|
2364
|
-
});
|
|
2365
|
-
}
|
|
2366
|
-
|
|
2367
|
-
processTargetLanguages(applicableFields, cb) {
|
|
2368
|
-
if (!applicableFields) {
|
|
2369
|
-
return false;
|
|
2370
|
-
}
|
|
2371
|
-
|
|
2372
|
-
Object.entries(applicableFields).forEach(([applicableField, {
|
|
2373
|
-
targetLanguage,
|
|
2374
|
-
onByDefault,
|
|
2375
|
-
meta: metaApplicableField
|
|
2376
|
-
}]) => {
|
|
2377
|
-
if (Array.isArray(targetLanguage)) {
|
|
2378
|
-
targetLanguage.forEach(targetLanguage => {
|
|
2379
|
-
cb({
|
|
2380
|
-
applicableField,
|
|
2381
|
-
targetLanguage,
|
|
2382
|
-
onByDefault,
|
|
2383
|
-
metaApplicableField
|
|
2384
|
-
}); // eslint-disable-line node/no-callback-literal
|
|
2385
|
-
});
|
|
2386
|
-
} else {
|
|
2387
|
-
// eslint-disable-next-line node/callback-return
|
|
2388
|
-
cb({
|
|
2389
|
-
applicableField,
|
|
2390
|
-
targetLanguage,
|
|
2391
|
-
onByDefault,
|
|
2392
|
-
metaApplicableField
|
|
2393
|
-
}); // eslint-disable-line node/no-callback-literal
|
|
2394
|
-
}
|
|
2395
|
-
});
|
|
2396
|
-
return true;
|
|
2397
|
-
}
|
|
2398
|
-
|
|
2399
|
-
isPluginField({
|
|
2400
|
-
namespace,
|
|
2401
|
-
field
|
|
2402
|
-
}) {
|
|
2403
|
-
return field.startsWith(`${namespace}-plugin-`);
|
|
2404
|
-
}
|
|
2405
|
-
|
|
2406
|
-
getPluginFieldParts({
|
|
2407
|
-
namespace,
|
|
2408
|
-
field
|
|
2409
|
-
}) {
|
|
2410
|
-
field = field.replace(`${namespace}-plugin-`, '');
|
|
2411
|
-
let pluginName, applicableField, targetLanguage;
|
|
2412
|
-
|
|
2413
|
-
if (field.includes('-')) {
|
|
2414
|
-
[pluginName, applicableField, targetLanguage] = field.split('-');
|
|
2415
|
-
} else {
|
|
2416
|
-
pluginName = field;
|
|
2417
|
-
}
|
|
2418
|
-
|
|
2419
|
-
return [pluginName, applicableField, targetLanguage].map(unescapePluginComponent);
|
|
2420
|
-
}
|
|
2421
|
-
|
|
2422
|
-
}
|
|
2423
|
-
|
|
2424
|
-
let path, babelRegister;
|
|
2425
|
-
|
|
2426
|
-
if (typeof process !== 'undefined') {
|
|
2427
|
-
/* eslint-disable node/global-require */
|
|
2428
|
-
path = require('path');
|
|
2429
|
-
babelRegister = require('@babel/register');
|
|
2430
|
-
/* eslint-enable node/global-require */
|
|
2431
|
-
}
|
|
2432
|
-
const getFilePaths = function getFilePaths(filesObj, fileGroup, fileData) {
|
|
2433
|
-
const baseDir = (filesObj.baseDirectory || '') + (fileGroup.baseDirectory || '') + '/';
|
|
2434
|
-
const schemaBaseDir = (filesObj.schemaBaseDirectory || '') + (fileGroup.schemaBaseDirectory || '') + '/';
|
|
2435
|
-
const metadataBaseDir = (filesObj.metadataBaseDirectory || '') + (fileGroup.metadataBaseDirectory || '') + '/';
|
|
2436
|
-
const file = baseDir + fileData.file.$ref;
|
|
2437
|
-
const schemaFile = fileData.schemaFile ? schemaBaseDir + fileData.schemaFile : '';
|
|
2438
|
-
const metadataFile = fileData.metadataFile ? metadataBaseDir + fileData.metadataFile : '';
|
|
2439
|
-
return {
|
|
2440
|
-
file,
|
|
2441
|
-
schemaFile,
|
|
2442
|
-
metadataFile
|
|
2443
|
-
};
|
|
2444
|
-
};
|
|
2445
|
-
const getWorkData = async function ({
|
|
2446
|
-
lang,
|
|
2447
|
-
fallbackLanguages,
|
|
2448
|
-
work,
|
|
2449
|
-
files,
|
|
2450
|
-
allowPlugins,
|
|
2451
|
-
basePath,
|
|
2452
|
-
languages,
|
|
2453
|
-
preferredLocale
|
|
2454
|
-
}) {
|
|
2455
|
-
const filesObj = await simpleGetJson.getJSON(files);
|
|
2456
|
-
|
|
2457
|
-
const localeFromFileData = lan => filesObj['localization-strings'][lan];
|
|
2458
|
-
|
|
2459
|
-
const imfFile = imf.IMF({
|
|
2460
|
-
locales: lang.map(localeFromFileData),
|
|
2461
|
-
fallbackLocales: fallbackLanguages.map(localeFromFileData)
|
|
2462
|
-
});
|
|
2463
|
-
const lf = imfFile.getFormatter();
|
|
2464
|
-
let fileData;
|
|
2465
|
-
const fileGroup = filesObj.groups.find(fg => {
|
|
2466
|
-
fileData = fg.files.find(file => work === lf(['workNames', fg.id, file.name]));
|
|
2467
|
-
return Boolean(fileData);
|
|
2468
|
-
}); // This is not specific to the work, but we export it anyways
|
|
2469
|
-
|
|
2470
|
-
const groupsToWorks = filesObj.groups.map(fg => {
|
|
2471
|
-
return {
|
|
2472
|
-
name: lf({
|
|
2473
|
-
key: fg.name.localeKey,
|
|
2474
|
-
fallback: true
|
|
2475
|
-
}),
|
|
2476
|
-
workNames: fg.files.map(file => {
|
|
2477
|
-
return lf(['workNames', fg.id, file.name]);
|
|
2478
|
-
}),
|
|
2479
|
-
shortcuts: fg.files.map(file => file.shortcut)
|
|
2480
|
-
};
|
|
2481
|
-
});
|
|
2482
|
-
const fp = getFilePaths(filesObj, fileGroup, fileData);
|
|
2483
|
-
const {
|
|
2484
|
-
file
|
|
2485
|
-
} = fp;
|
|
2486
|
-
let {
|
|
2487
|
-
schemaFile,
|
|
2488
|
-
metadataFile
|
|
2489
|
-
} = fp;
|
|
2490
|
-
let schemaProperty = '',
|
|
2491
|
-
metadataProperty = '';
|
|
2492
|
-
|
|
2493
|
-
if (!schemaFile) {
|
|
2494
|
-
schemaFile = file;
|
|
2495
|
-
schemaProperty = 'schema';
|
|
2496
|
-
}
|
|
2497
|
-
|
|
2498
|
-
if (!metadataFile) {
|
|
2499
|
-
metadataFile = file;
|
|
2500
|
-
metadataProperty = 'metadata';
|
|
2501
|
-
}
|
|
2502
|
-
|
|
2503
|
-
let getPlugins,
|
|
2504
|
-
pluginsInWork,
|
|
2505
|
-
pluginFieldsForWork,
|
|
2506
|
-
pluginPaths,
|
|
2507
|
-
pluginFieldMappingForWork = [];
|
|
2508
|
-
|
|
2509
|
-
if (allowPlugins) {
|
|
2510
|
-
const pluginFieldMapping = filesObj['plugin-field-mapping'];
|
|
2511
|
-
const pluginFieldMappingID = pluginFieldMapping[fileGroup.id];
|
|
2512
|
-
const possiblePluginFieldMappingForWork = pluginFieldMappingID[fileData.name];
|
|
2513
|
-
|
|
2514
|
-
if (possiblePluginFieldMappingForWork) {
|
|
2515
|
-
pluginFieldsForWork = Object.keys(possiblePluginFieldMappingForWork);
|
|
2516
|
-
pluginsInWork = Object.entries(filesObj.plugins).filter(([p]) => {
|
|
2517
|
-
return pluginFieldsForWork.includes(p);
|
|
2518
|
-
});
|
|
2519
|
-
pluginFieldMappingForWork = pluginsInWork.map(([p]) => {
|
|
2520
|
-
return possiblePluginFieldMappingForWork[p];
|
|
2521
|
-
});
|
|
2522
|
-
pluginPaths = pluginsInWork.map(([, pluginObj]) => pluginObj.path);
|
|
2523
|
-
getPlugins = pluginsInWork;
|
|
2524
|
-
}
|
|
2525
|
-
}
|
|
2526
|
-
|
|
2527
|
-
const metadataObj = await getMetadata(metadataFile, metadataProperty, basePath);
|
|
2528
|
-
|
|
2529
|
-
const getFieldAliasOrName = function getFieldAliasOrName(field) {
|
|
2530
|
-
const fieldObj = metadataObj.fields && metadataObj.fields[field];
|
|
2531
|
-
let fieldName;
|
|
2532
|
-
let fieldAlias;
|
|
2533
|
-
|
|
2534
|
-
if (fieldObj) {
|
|
2535
|
-
fieldAlias = fieldObj.alias;
|
|
2536
|
-
}
|
|
2537
|
-
|
|
2538
|
-
if (fieldAlias) {
|
|
2539
|
-
if (typeof fieldAlias === 'string') {
|
|
2540
|
-
fieldName = fieldAlias;
|
|
2541
|
-
} else {
|
|
2542
|
-
fieldAlias = fieldAlias.localeKey;
|
|
2543
|
-
fieldName = getMetaProp(lang, metadataObj, fieldAlias.split('/'));
|
|
2544
|
-
}
|
|
2545
|
-
} else {
|
|
2546
|
-
// No alias
|
|
2547
|
-
fieldName = fieldObj.name;
|
|
2548
|
-
|
|
2549
|
-
if (typeof fieldName === 'object') {
|
|
2550
|
-
fieldName = fieldName.localeKey;
|
|
2551
|
-
fieldName = getMetaProp(lang, metadataObj, fieldName.split('/'));
|
|
2552
|
-
}
|
|
2553
|
-
}
|
|
2554
|
-
|
|
2555
|
-
return fieldName;
|
|
2556
|
-
};
|
|
2557
|
-
|
|
2558
|
-
const pluginFieldMappings = pluginFieldMappingForWork;
|
|
2559
|
-
const [schemaObj, pluginObjects] = await Promise.all([getMetadata(schemaFile, schemaProperty, basePath), getPlugins ? Promise.all(pluginPaths.map(pluginPath => {
|
|
2560
|
-
if (typeof process !== 'undefined') {
|
|
2561
|
-
pluginPath = path.resolve(path.join(process.cwd(), 'node_modules/textbrowser/server', pluginPath));
|
|
2562
|
-
babelRegister({
|
|
2563
|
-
presets: ['@babel/env']
|
|
2564
|
-
});
|
|
2565
|
-
return Promise.resolve().then(() => {
|
|
2566
|
-
return require(pluginPath); // eslint-disable-line node/global-require, import/no-dynamic-require
|
|
2567
|
-
}).catch(err => {
|
|
2568
|
-
// E.g., with tooltips plugin
|
|
2569
|
-
console.log('err', err);
|
|
2570
|
-
});
|
|
2571
|
-
} // eslint-disable-next-line no-unsanitized/method
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
return Promise.resolve(`${pluginPath}`).then(s => _interopRequireWildcard(require(s)));
|
|
2575
|
-
})) : null]);
|
|
2576
|
-
const pluginsForWork = new PluginsForWork({
|
|
2577
|
-
pluginsInWork,
|
|
2578
|
-
pluginFieldMappings,
|
|
2579
|
-
pluginObjects
|
|
2580
|
-
});
|
|
2581
|
-
const schemaItems = schemaObj.items.items;
|
|
2582
|
-
const fieldInfo = schemaItems.map(({
|
|
2583
|
-
title: field
|
|
2584
|
-
}) => {
|
|
2585
|
-
return {
|
|
2586
|
-
field,
|
|
2587
|
-
fieldAliasOrName: getFieldAliasOrName(field) || field
|
|
2588
|
-
};
|
|
2589
|
-
});
|
|
2590
|
-
const metadata = new Metadata({
|
|
2591
|
-
metadataObj
|
|
2592
|
-
});
|
|
2593
|
-
|
|
2594
|
-
if (languages && // Avoid all this processing if this is not the specific call requiring
|
|
2595
|
-
pluginsForWork) {
|
|
2596
|
-
console.log('pluginsForWork', pluginsForWork);
|
|
2597
|
-
const {
|
|
2598
|
-
lang,
|
|
2599
|
-
namespace
|
|
2600
|
-
} = this; // array with first item as preferred
|
|
2601
|
-
|
|
2602
|
-
pluginsForWork.iterateMappings(({
|
|
2603
|
-
plugin,
|
|
2604
|
-
pluginName,
|
|
2605
|
-
pluginLang,
|
|
2606
|
-
onByDefaultDefault,
|
|
2607
|
-
placement,
|
|
2608
|
-
applicableFields,
|
|
2609
|
-
meta
|
|
2610
|
-
}) => {
|
|
2611
|
-
const processField = ({
|
|
2612
|
-
applicableField,
|
|
2613
|
-
targetLanguage,
|
|
2614
|
-
onByDefault,
|
|
2615
|
-
metaApplicableField
|
|
2616
|
-
} = {}) => {
|
|
2617
|
-
const plugin = pluginsForWork.getPluginObject(pluginName) || {};
|
|
2618
|
-
const applicableFieldLang = metadata.getFieldLang(applicableField);
|
|
2619
|
-
|
|
2620
|
-
if (plugin.getTargetLanguage) {
|
|
2621
|
-
targetLanguage = plugin.getTargetLanguage({
|
|
2622
|
-
applicableField,
|
|
2623
|
-
targetLanguage,
|
|
2624
|
-
// Default lang for plug-in (from files.json)
|
|
2625
|
-
pluginLang,
|
|
2626
|
-
// Default lang when no target language or
|
|
2627
|
-
// plugin lang; using the lang of the applicable
|
|
2628
|
-
// field
|
|
2629
|
-
applicableFieldLang
|
|
2630
|
-
});
|
|
2631
|
-
}
|
|
2632
|
-
|
|
2633
|
-
const field = escapePlugin({
|
|
2634
|
-
pluginName,
|
|
2635
|
-
applicableField,
|
|
2636
|
-
targetLanguage: targetLanguage || pluginLang || applicableFieldLang
|
|
2637
|
-
});
|
|
2638
|
-
|
|
2639
|
-
if (targetLanguage === '{locale}') {
|
|
2640
|
-
targetLanguage = preferredLocale;
|
|
2641
|
-
}
|
|
2642
|
-
|
|
2643
|
-
const applicableFieldI18N = getMetaProp(lang, metadataObj, ['fieldnames', applicableField]);
|
|
2644
|
-
const fieldAliasOrName = plugin.getFieldAliasOrName ? plugin.getFieldAliasOrName({
|
|
2645
|
-
locales: lang,
|
|
2646
|
-
lf,
|
|
2647
|
-
targetLanguage,
|
|
2648
|
-
applicableField,
|
|
2649
|
-
applicableFieldI18N,
|
|
2650
|
-
meta,
|
|
2651
|
-
metaApplicableField,
|
|
2652
|
-
targetLanguageI18N: languages.getLanguageFromCode(targetLanguage)
|
|
2653
|
-
}) : languages.getFieldNameFromPluginNameAndLocales({
|
|
2654
|
-
pluginName,
|
|
2655
|
-
locales: lang,
|
|
2656
|
-
lf,
|
|
2657
|
-
targetLanguage,
|
|
2658
|
-
applicableFieldI18N,
|
|
2659
|
-
// Todo: Should have formal way to i18nize meta
|
|
2660
|
-
meta,
|
|
2661
|
-
metaApplicableField
|
|
2662
|
-
});
|
|
2663
|
-
fieldInfo.splice( // Todo: Allow default placement overriding for
|
|
2664
|
-
// non-plugins
|
|
2665
|
-
placement === 'end' ? Number.POSITIVE_INFINITY // push
|
|
2666
|
-
: placement, 0, {
|
|
2667
|
-
field: `${namespace}-plugin-${field}`,
|
|
2668
|
-
fieldAliasOrName,
|
|
2669
|
-
// Plug-in specific (todo: allow specifying
|
|
2670
|
-
// for non-plugins)
|
|
2671
|
-
onByDefault: typeof onByDefault === 'boolean' ? onByDefault : onByDefaultDefault || false,
|
|
2672
|
-
// Three conventions for use by plug-ins but
|
|
2673
|
-
// textbrowser only passes on (might
|
|
2674
|
-
// not need here)
|
|
2675
|
-
applicableField,
|
|
2676
|
-
metaApplicableField,
|
|
2677
|
-
fieldLang: targetLanguage
|
|
2678
|
-
});
|
|
2679
|
-
};
|
|
2680
|
-
|
|
2681
|
-
if (!pluginsForWork.processTargetLanguages(applicableFields, processField)) {
|
|
2682
|
-
processField();
|
|
2683
|
-
}
|
|
2684
|
-
});
|
|
2685
|
-
}
|
|
2686
|
-
|
|
2687
|
-
return {
|
|
2688
|
-
fileData,
|
|
2689
|
-
lf,
|
|
2690
|
-
getFieldAliasOrName,
|
|
2691
|
-
metadataObj,
|
|
2692
|
-
schemaObj,
|
|
2693
|
-
schemaItems,
|
|
2694
|
-
fieldInfo,
|
|
2695
|
-
pluginsForWork,
|
|
2696
|
-
groupsToWorks,
|
|
2697
|
-
metadata
|
|
2698
|
-
};
|
|
2699
|
-
};
|
|
2700
|
-
|
|
2701
|
-
const JsonRefs = require('json-refs');
|
|
2702
|
-
|
|
2703
|
-
const fieldValueAliasRegex = /^.* \((.*?)\)$/;
|
|
2704
|
-
|
|
2705
|
-
const getRawFieldValue = v => {
|
|
2706
|
-
return typeof v === 'string' ? v.replace(fieldValueAliasRegex, '$1') : v;
|
|
2707
|
-
};
|
|
2708
|
-
const resultsDisplayServer = async function resultsDisplayServer(args) {
|
|
2709
|
-
const {
|
|
2710
|
-
templateArgs
|
|
2711
|
-
} = await resultsDisplayServerOrClient.call(this, _objectSpread2({}, args)); // Todo: Should really reconcile this with client-side output options
|
|
2712
|
-
// (as should also have option there to get JSON, Jamilih, etc.
|
|
2713
|
-
// output)
|
|
2714
|
-
|
|
2715
|
-
switch (args.serverOutput) {
|
|
2716
|
-
case 'jamilih':
|
|
2717
|
-
return Templates.resultsDisplayServerOrClient.main(templateArgs);
|
|
2718
|
-
|
|
2719
|
-
case 'html':
|
|
2720
|
-
{
|
|
2721
|
-
const jamilih$1 = Templates.resultsDisplayServerOrClient.main(templateArgs);
|
|
2722
|
-
return jamilih.jml.toHTML(...jamilih$1);
|
|
2723
|
-
}
|
|
2724
|
-
|
|
2725
|
-
case 'json':
|
|
2726
|
-
default:
|
|
2727
|
-
return templateArgs.tableData;
|
|
2728
|
-
}
|
|
2729
|
-
};
|
|
2730
|
-
const resultsDisplayServerOrClient = async function resultsDisplayServerOrClient({
|
|
2731
|
-
l,
|
|
2732
|
-
lang,
|
|
2733
|
-
fallbackLanguages,
|
|
2734
|
-
imfLocales,
|
|
2735
|
-
$p,
|
|
2736
|
-
skipIndexedDB,
|
|
2737
|
-
noIndexedDB,
|
|
2738
|
-
prefI18n,
|
|
2739
|
-
files,
|
|
2740
|
-
allowPlugins,
|
|
2741
|
-
langData,
|
|
2742
|
-
basePath = '',
|
|
2743
|
-
dynamicBasePath = ''
|
|
2744
|
-
}) {
|
|
2745
|
-
const languages = new Languages({
|
|
2746
|
-
langData
|
|
2747
|
-
});
|
|
2748
|
-
|
|
2749
|
-
const getCellValue = ({
|
|
2750
|
-
fieldValueAliasMapPreferred,
|
|
2751
|
-
escapeColumnIndexes
|
|
2752
|
-
}) => ({
|
|
2753
|
-
tr,
|
|
2754
|
-
idx
|
|
2755
|
-
}) => {
|
|
2756
|
-
let tdVal = fieldValueAliasMapPreferred[idx] !== undefined ? fieldValueAliasMapPreferred[idx][tr[idx]] : tr[idx];
|
|
2757
|
-
|
|
2758
|
-
if (tdVal && typeof tdVal === 'object') {
|
|
2759
|
-
tdVal = Object.values(tdVal);
|
|
2760
|
-
}
|
|
2761
|
-
|
|
2762
|
-
if (Array.isArray(tdVal)) {
|
|
2763
|
-
tdVal = tdVal.join(l('comma-space'));
|
|
2764
|
-
}
|
|
2765
|
-
|
|
2766
|
-
return (escapeColumnIndexes[idx] || !this.trustFormatHTML) && typeof tdVal === 'string' ? {
|
|
2767
|
-
tdVal: escapeHTML(tdVal),
|
|
2768
|
-
htmlEscaped: true
|
|
2769
|
-
} : {
|
|
2770
|
-
tdVal
|
|
2771
|
-
};
|
|
2772
|
-
};
|
|
2773
|
-
|
|
2774
|
-
const getCanonicalID = ({
|
|
2775
|
-
fieldValueAliasMap,
|
|
2776
|
-
fieldValueAliasMapPreferred,
|
|
2777
|
-
localizedFieldNames,
|
|
2778
|
-
canonicalBrowseFieldNames
|
|
2779
|
-
}) => ({
|
|
2780
|
-
tr,
|
|
2781
|
-
foundState
|
|
2782
|
-
}) => {
|
|
2783
|
-
return canonicalBrowseFieldNames.map(fieldName => {
|
|
2784
|
-
const idx = localizedFieldNames.indexOf(fieldName); // This works to put alias in anchor but this includes
|
|
2785
|
-
// our ending parenthetical, the alias may be harder
|
|
2786
|
-
// to remember and/or automated than original (e.g.,
|
|
2787
|
-
// for a number representing a book); we may wish to
|
|
2788
|
-
// switch this (and also for other browse field-based
|
|
2789
|
-
// items).
|
|
2790
|
-
|
|
2791
|
-
if (fieldValueAliasMap[idx] !== undefined) {
|
|
2792
|
-
return fieldValueAliasMapPreferred[idx][tr[idx]];
|
|
2793
|
-
}
|
|
2794
|
-
|
|
2795
|
-
return tr[idx];
|
|
2796
|
-
}).join('-'); // rowID;
|
|
2797
|
-
};
|
|
2798
|
-
|
|
2799
|
-
const determineEnd = ({
|
|
2800
|
-
fieldValueAliasMap,
|
|
2801
|
-
fieldValueAliasMapPreferred,
|
|
2802
|
-
localizedFieldNames,
|
|
2803
|
-
applicableBrowseFieldNames,
|
|
2804
|
-
startsRaw,
|
|
2805
|
-
endsRaw
|
|
2806
|
-
}) => ({
|
|
2807
|
-
tr,
|
|
2808
|
-
foundState
|
|
2809
|
-
}) => {
|
|
2810
|
-
const rowIDPartsPreferred = [];
|
|
2811
|
-
const rowIDParts = applicableBrowseFieldNames.map(fieldName => {
|
|
2812
|
-
const idx = localizedFieldNames.indexOf(fieldName); // This works to put alias in anchor but this includes
|
|
2813
|
-
// our ending parenthetical, the alias may be harder
|
|
2814
|
-
// to remember and/or automated than original (e.g.,
|
|
2815
|
-
// for a number representing a book), and there could
|
|
2816
|
-
// be multiple aliases for a value; we may wish to
|
|
2817
|
-
// switch this (and also for other browse field-based
|
|
2818
|
-
// items).
|
|
2819
|
-
|
|
2820
|
-
if (fieldValueAliasMap[idx] !== undefined) {
|
|
2821
|
-
rowIDPartsPreferred.push(fieldValueAliasMapPreferred[idx][tr[idx]]);
|
|
2822
|
-
} else {
|
|
2823
|
-
rowIDPartsPreferred.push(tr[idx]);
|
|
2824
|
-
}
|
|
2825
|
-
|
|
2826
|
-
return tr[idx];
|
|
2827
|
-
}); // Todo: Use schema to determine field type and use `Number.parseInt`
|
|
2828
|
-
// on other value instead of `String` conversions
|
|
2829
|
-
|
|
2830
|
-
if (!foundState.start) {
|
|
2831
|
-
if (startsRaw.some((part, i) => {
|
|
2832
|
-
const rowIDPart = rowIDParts[i];
|
|
2833
|
-
return part !== rowIDPart;
|
|
2834
|
-
})) {
|
|
2835
|
-
// Trigger skip of this row
|
|
2836
|
-
return false;
|
|
2837
|
-
}
|
|
2838
|
-
|
|
2839
|
-
foundState.start = true;
|
|
2840
|
-
} // This doesn't go in an `else` for the above in case the start is the end
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
if (endsRaw.every((part, i) => {
|
|
2844
|
-
const rowIDPart = rowIDParts[i];
|
|
2845
|
-
return part === rowIDPart;
|
|
2846
|
-
})) {
|
|
2847
|
-
foundState.end = true;
|
|
2848
|
-
} else if (foundState.end) {
|
|
2849
|
-
// If no longer matching, trigger end of the table
|
|
2850
|
-
return true;
|
|
2851
|
-
}
|
|
2852
|
-
|
|
2853
|
-
return rowIDPartsPreferred.join('-'); // rowID;
|
|
2854
|
-
};
|
|
2855
|
-
|
|
2856
|
-
const getCheckedAndInterlinearFieldInfo = ({
|
|
2857
|
-
localizedFieldNames
|
|
2858
|
-
}) => {
|
|
2859
|
-
let i = 1;
|
|
2860
|
-
let field, checked;
|
|
2861
|
-
let checkedFields = [];
|
|
2862
|
-
|
|
2863
|
-
do {
|
|
2864
|
-
field = $p.get('field' + i, true);
|
|
2865
|
-
checked = $p.get('checked' + i, true);
|
|
2866
|
-
i++;
|
|
2867
|
-
|
|
2868
|
-
if (field && (checked === l('yes') || checked === null // Default to "on"
|
|
2869
|
-
)) {
|
|
2870
|
-
checkedFields.push(field);
|
|
2871
|
-
}
|
|
2872
|
-
} while (field);
|
|
2873
|
-
|
|
2874
|
-
checkedFields = checkedFields.filter(cf => localizedFieldNames.includes(cf));
|
|
2875
|
-
const checkedFieldIndexes = checkedFields.map(cf => localizedFieldNames.indexOf(cf));
|
|
2876
|
-
const allInterlinearColIndexes = checkedFieldIndexes.map((cfi, i) => {
|
|
2877
|
-
const interlin = $p.get('interlin' + (cfi + 1), true);
|
|
2878
|
-
return interlin && interlin.split(/\s*,\s*/).map(col => // Todo: Avoid this when known to be integer or if string, though allow
|
|
2879
|
-
// string to be treated as number if config is set.
|
|
2880
|
-
Number.parseInt(col) - 1).filter(n => !Number.isNaN(n));
|
|
2881
|
-
});
|
|
2882
|
-
return [checkedFields, checkedFieldIndexes, allInterlinearColIndexes];
|
|
2883
|
-
};
|
|
2884
|
-
|
|
2885
|
-
const getCaption = ({
|
|
2886
|
-
starts,
|
|
2887
|
-
ends,
|
|
2888
|
-
applicableBrowseFieldNames,
|
|
2889
|
-
heading
|
|
2890
|
-
}) => {
|
|
2891
|
-
let caption;
|
|
2892
|
-
const hasCaption = $pRaw('caption') !== '0';
|
|
2893
|
-
|
|
2894
|
-
if (hasCaption) {
|
|
2895
|
-
/*
|
|
2896
|
-
// Works but displays in parentheses browse fields which
|
|
2897
|
-
// may be non-applicable
|
|
2898
|
-
const buildRangePoint = (startOrEnd) => escapeHTML(
|
|
2899
|
-
browseFieldSets.reduce((txt, bfs, i) =>
|
|
2900
|
-
(txt ? txt + ' (' : '') + bfs.map((bf, j) =>
|
|
2901
|
-
(j > 0 ? l('comma-space') : '') + bf + ' ' +
|
|
2902
|
-
$pRaw(startOrEnd + (i + 1) + '-' + (j + 1))
|
|
2903
|
-
).join('') + (txt ? ')' : ''), '')
|
|
2904
|
-
);
|
|
2905
|
-
*/
|
|
2906
|
-
|
|
2907
|
-
/*
|
|
2908
|
-
// Works but overly long
|
|
2909
|
-
const buildRangePoint = (startOrEnd) => escapeHTML(
|
|
2910
|
-
applicableBrowseFieldSet.map((bf, j) =>
|
|
2911
|
-
(j > 0 ? l('comma-space') : '') + bf + ' ' +
|
|
2912
|
-
$pRaw(startOrEnd + (browseFieldSetIdx + 1) + '-' + (j + 1))
|
|
2913
|
-
).join('')
|
|
2914
|
-
);
|
|
2915
|
-
*/
|
|
2916
|
-
const startSep = Templates.resultsDisplayServerOrClient.startSeparator({
|
|
2917
|
-
l
|
|
2918
|
-
});
|
|
2919
|
-
const innerBrowseFieldSeparator = Templates.resultsDisplayServerOrClient.innerBrowseFieldSeparator({
|
|
2920
|
-
l
|
|
2921
|
-
});
|
|
2922
|
-
|
|
2923
|
-
const buildRanges = () => {
|
|
2924
|
-
const endVals = [];
|
|
2925
|
-
const startRange = starts.reduce((str, startFieldValue, i) => {
|
|
2926
|
-
const ret = str + startFieldValue;
|
|
2927
|
-
|
|
2928
|
-
if (startFieldValue === ends[i]) {
|
|
2929
|
-
// We abbreviate as start/end share same Book, etc.
|
|
2930
|
-
return ret + (i > 0 ? innerBrowseFieldSeparator // e.g., for "Genesis 7, 5-8"
|
|
2931
|
-
: ' ' // e.g., for 2nd space in "Surah 2 5-8"
|
|
2932
|
-
);
|
|
2933
|
-
}
|
|
2934
|
-
|
|
2935
|
-
endVals.push(ends[i]);
|
|
2936
|
-
return ret + startSep;
|
|
2937
|
-
}, '').slice(0, -startSep.length);
|
|
2938
|
-
const rangeNames = applicableBrowseFieldNames.join(innerBrowseFieldSeparator);
|
|
2939
|
-
return escapeHTML(Templates.resultsDisplayServerOrClient.ranges({
|
|
2940
|
-
l,
|
|
2941
|
-
startRange,
|
|
2942
|
-
endVals,
|
|
2943
|
-
rangeNames
|
|
2944
|
-
}));
|
|
2945
|
-
};
|
|
2946
|
-
|
|
2947
|
-
const ranges = buildRanges();
|
|
2948
|
-
caption = Templates.resultsDisplayServerOrClient.caption({
|
|
2949
|
-
heading,
|
|
2950
|
-
ranges
|
|
2951
|
-
});
|
|
2952
|
-
}
|
|
2953
|
-
|
|
2954
|
-
return [hasCaption, caption];
|
|
2955
|
-
};
|
|
2956
|
-
|
|
2957
|
-
const runPresort = ({
|
|
2958
|
-
presort,
|
|
2959
|
-
tableData,
|
|
2960
|
-
applicableBrowseFieldNames,
|
|
2961
|
-
localizedFieldNames
|
|
2962
|
-
}) => {
|
|
2963
|
-
// Todo: Ought to be checking against an aliased table
|
|
2964
|
-
if (presort) {
|
|
2965
|
-
tableData.sort((rowA, rowB) => {
|
|
2966
|
-
let precedence;
|
|
2967
|
-
applicableBrowseFieldNames.some(fieldName => {
|
|
2968
|
-
const idx = localizedFieldNames.indexOf(fieldName);
|
|
2969
|
-
const rowAFirst = rowA[idx] < rowB[idx];
|
|
2970
|
-
const rowBFirst = rowA[idx] > rowB[idx];
|
|
2971
|
-
precedence = rowBFirst ? 1 : -1;
|
|
2972
|
-
return rowAFirst || rowBFirst; // Keep going if 0
|
|
2973
|
-
});
|
|
2974
|
-
return precedence;
|
|
2975
|
-
});
|
|
2976
|
-
}
|
|
2977
|
-
};
|
|
2978
|
-
|
|
2979
|
-
const getFieldValueAliasMap = ({
|
|
2980
|
-
schemaItems,
|
|
2981
|
-
fieldInfo,
|
|
2982
|
-
metadataObj,
|
|
2983
|
-
getFieldAliasOrName,
|
|
2984
|
-
usePreferAlias
|
|
2985
|
-
}) => {
|
|
2986
|
-
return fieldInfo.map(({
|
|
2987
|
-
field,
|
|
2988
|
-
plugin
|
|
2989
|
-
}) => {
|
|
2990
|
-
if (plugin) {
|
|
2991
|
-
return undefined;
|
|
2992
|
-
}
|
|
2993
|
-
|
|
2994
|
-
const {
|
|
2995
|
-
preferAlias,
|
|
2996
|
-
fieldValueAliasMap
|
|
2997
|
-
} = getFieldNameAndValueAliases({
|
|
2998
|
-
field,
|
|
2999
|
-
schemaItems,
|
|
3000
|
-
metadataObj,
|
|
3001
|
-
getFieldAliasOrName,
|
|
3002
|
-
lang
|
|
3003
|
-
});
|
|
3004
|
-
|
|
3005
|
-
if (!usePreferAlias) {
|
|
3006
|
-
return preferAlias !== false ? fieldValueAliasMap : undefined;
|
|
3007
|
-
}
|
|
3008
|
-
|
|
3009
|
-
if (fieldValueAliasMap) {
|
|
3010
|
-
Object.entries(fieldValueAliasMap).forEach(([key, val]) => {
|
|
3011
|
-
if (Array.isArray(val)) {
|
|
3012
|
-
fieldValueAliasMap[key] = val.map(value => Templates.resultsDisplayServerOrClient.fieldValueAlias({
|
|
3013
|
-
key,
|
|
3014
|
-
value
|
|
3015
|
-
}));
|
|
3016
|
-
return;
|
|
3017
|
-
}
|
|
3018
|
-
|
|
3019
|
-
if (val && typeof val === 'object') {
|
|
3020
|
-
if (typeof preferAlias === 'string') {
|
|
3021
|
-
fieldValueAliasMap[key] = Templates.resultsDisplayServerOrClient.fieldValueAlias({
|
|
3022
|
-
key,
|
|
3023
|
-
value: val[preferAlias]
|
|
3024
|
-
});
|
|
3025
|
-
} else {
|
|
3026
|
-
Object.entries(val).forEach(([k, value]) => {
|
|
3027
|
-
fieldValueAliasMap[key][k] = Templates.resultsDisplayServerOrClient.fieldValueAlias({
|
|
3028
|
-
key,
|
|
3029
|
-
value
|
|
3030
|
-
});
|
|
3031
|
-
});
|
|
3032
|
-
}
|
|
3033
|
-
|
|
3034
|
-
return;
|
|
3035
|
-
}
|
|
3036
|
-
|
|
3037
|
-
fieldValueAliasMap[key] = Templates.resultsDisplayServerOrClient.fieldValueAlias({
|
|
3038
|
-
key,
|
|
3039
|
-
value: val
|
|
3040
|
-
});
|
|
3041
|
-
});
|
|
3042
|
-
return preferAlias !== false ? fieldValueAliasMap : undefined;
|
|
3043
|
-
}
|
|
3044
|
-
|
|
3045
|
-
return undefined;
|
|
3046
|
-
});
|
|
3047
|
-
};
|
|
3048
|
-
|
|
3049
|
-
const $pRaw = (param, avoidLog) => {
|
|
3050
|
-
// Todo: Should work with i18n=true (if names i18nized, need reverse look-up)
|
|
3051
|
-
let key;
|
|
3052
|
-
const p = $p.get(param, true);
|
|
3053
|
-
/**
|
|
3054
|
-
*
|
|
3055
|
-
* @param {GenericArray|PlainObject} locale
|
|
3056
|
-
* @returns {boolean}
|
|
3057
|
-
*/
|
|
3058
|
-
|
|
3059
|
-
function reverseLocaleLookup(locale) {
|
|
3060
|
-
if (Array.isArray(locale)) {
|
|
3061
|
-
return locale.some(reverseLocaleLookup);
|
|
3062
|
-
}
|
|
3063
|
-
|
|
3064
|
-
const localeValues = Object.values(locale);
|
|
3065
|
-
return localeValues.some((val, i) => {
|
|
3066
|
-
if (typeof val !== 'string') {
|
|
3067
|
-
return reverseLocaleLookup(val);
|
|
3068
|
-
}
|
|
3069
|
-
|
|
3070
|
-
if (val === p) {
|
|
3071
|
-
key = Object.keys(locale)[i];
|
|
3072
|
-
return true;
|
|
3073
|
-
}
|
|
3074
|
-
|
|
3075
|
-
return false;
|
|
3076
|
-
});
|
|
3077
|
-
}
|
|
3078
|
-
|
|
3079
|
-
reverseLocaleLookup(imfLocales);
|
|
3080
|
-
|
|
3081
|
-
if (!key && !avoidLog) {
|
|
3082
|
-
console.log('Bad param/value', param, '::', p);
|
|
3083
|
-
}
|
|
3084
|
-
|
|
3085
|
-
return key; // || p; // $p.get(param, true);
|
|
3086
|
-
};
|
|
3087
|
-
|
|
3088
|
-
const escapeCSS = escapeHTML;
|
|
3089
|
-
|
|
3090
|
-
const $pRawEsc = param => escapeHTML($pRaw(param));
|
|
3091
|
-
|
|
3092
|
-
const $pEscArbitrary = param => escapeHTML($p.get(param, true)); // Not currently in use
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
const escapeQuotedCSS = s => s.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
3096
|
-
|
|
3097
|
-
const {
|
|
3098
|
-
fileData,
|
|
3099
|
-
lf,
|
|
3100
|
-
getFieldAliasOrName,
|
|
3101
|
-
schemaObj,
|
|
3102
|
-
metadataObj,
|
|
3103
|
-
pluginsForWork
|
|
3104
|
-
} = await getWorkData({
|
|
3105
|
-
files: files || this.files,
|
|
3106
|
-
allowPlugins: typeof allowPlugins === 'boolean' ? allowPlugins : this.allowPlugins,
|
|
3107
|
-
lang,
|
|
3108
|
-
fallbackLanguages,
|
|
3109
|
-
work: $p.get('work'),
|
|
3110
|
-
basePath
|
|
3111
|
-
});
|
|
3112
|
-
console.log('pluginsForWork', pluginsForWork);
|
|
3113
|
-
const heading = getMetaProp(lang, metadataObj, 'heading');
|
|
3114
|
-
const schemaItems = schemaObj.items.items;
|
|
3115
|
-
const setNames = [];
|
|
3116
|
-
const presorts = [];
|
|
3117
|
-
const browseFieldSets = [];
|
|
3118
|
-
getBrowseFieldData({
|
|
3119
|
-
metadataObj,
|
|
3120
|
-
schemaItems,
|
|
3121
|
-
getFieldAliasOrName,
|
|
3122
|
-
lang,
|
|
3123
|
-
|
|
3124
|
-
callback({
|
|
3125
|
-
setName,
|
|
3126
|
-
browseFields,
|
|
3127
|
-
presort
|
|
3128
|
-
}) {
|
|
3129
|
-
setNames.push(setName);
|
|
3130
|
-
presorts.push(presort);
|
|
3131
|
-
browseFieldSets.push(browseFields);
|
|
3132
|
-
}
|
|
3133
|
-
|
|
3134
|
-
});
|
|
3135
|
-
const fieldInfo = schemaItems.map(({
|
|
3136
|
-
title: field,
|
|
3137
|
-
format
|
|
3138
|
-
}) => {
|
|
3139
|
-
return {
|
|
3140
|
-
field,
|
|
3141
|
-
fieldAliasOrName: getFieldAliasOrName(field) || field,
|
|
3142
|
-
escapeColumn: format !== 'html',
|
|
3143
|
-
fieldLang: metadataObj.fields[field].lang
|
|
3144
|
-
};
|
|
3145
|
-
});
|
|
3146
|
-
const [preferredLocale] = lang;
|
|
3147
|
-
const metadata = new Metadata({
|
|
3148
|
-
metadataObj
|
|
3149
|
-
});
|
|
3150
|
-
|
|
3151
|
-
if (pluginsForWork) {
|
|
3152
|
-
console.log('pluginsForWork', pluginsForWork);
|
|
3153
|
-
const {
|
|
3154
|
-
lang
|
|
3155
|
-
} = this; // array with first item as preferred
|
|
3156
|
-
|
|
3157
|
-
pluginsForWork.iterateMappings(({
|
|
3158
|
-
plugin,
|
|
3159
|
-
pluginName,
|
|
3160
|
-
pluginLang,
|
|
3161
|
-
onByDefaultDefault,
|
|
3162
|
-
placement,
|
|
3163
|
-
applicableFields,
|
|
3164
|
-
meta
|
|
3165
|
-
}) => {
|
|
3166
|
-
placement = placement === 'end' ? Number.POSITIVE_INFINITY // push
|
|
3167
|
-
: placement;
|
|
3168
|
-
|
|
3169
|
-
const processField = ({
|
|
3170
|
-
applicableField,
|
|
3171
|
-
targetLanguage,
|
|
3172
|
-
onByDefault,
|
|
3173
|
-
metaApplicableField
|
|
3174
|
-
} = {}) => {
|
|
3175
|
-
const plugin = pluginsForWork.getPluginObject(pluginName) || {};
|
|
3176
|
-
const applicableFieldLang = metadata.getFieldLang(applicableField);
|
|
3177
|
-
|
|
3178
|
-
if (plugin.getTargetLanguage) {
|
|
3179
|
-
targetLanguage = plugin.getTargetLanguage({
|
|
3180
|
-
applicableField,
|
|
3181
|
-
targetLanguage,
|
|
3182
|
-
// Default lang for plug-in (from files.json)
|
|
3183
|
-
pluginLang,
|
|
3184
|
-
// Default lang when no target language or
|
|
3185
|
-
// plugin lang; using the lang of the applicable
|
|
3186
|
-
// field
|
|
3187
|
-
applicableFieldLang
|
|
3188
|
-
});
|
|
3189
|
-
}
|
|
3190
|
-
|
|
3191
|
-
if (targetLanguage === '{locale}') {
|
|
3192
|
-
targetLanguage = preferredLocale;
|
|
3193
|
-
}
|
|
3194
|
-
|
|
3195
|
-
const applicableFieldI18N = getMetaProp(lang, metadataObj, ['fieldnames', applicableField]);
|
|
3196
|
-
const fieldAliasOrName = plugin.getFieldAliasOrName ? plugin.getFieldAliasOrName({
|
|
3197
|
-
locales: lang,
|
|
3198
|
-
lf,
|
|
3199
|
-
targetLanguage,
|
|
3200
|
-
applicableField,
|
|
3201
|
-
applicableFieldI18N,
|
|
3202
|
-
meta,
|
|
3203
|
-
metaApplicableField,
|
|
3204
|
-
targetLanguageI18N: languages.getLanguageFromCode(targetLanguage)
|
|
3205
|
-
}) : languages.getFieldNameFromPluginNameAndLocales({
|
|
3206
|
-
pluginName,
|
|
3207
|
-
locales: lang,
|
|
3208
|
-
lf,
|
|
3209
|
-
targetLanguage,
|
|
3210
|
-
applicableFieldI18N,
|
|
3211
|
-
// Todo: Should have formal way to i18nize meta
|
|
3212
|
-
meta,
|
|
3213
|
-
metaApplicableField
|
|
3214
|
-
});
|
|
3215
|
-
fieldInfo.splice( // Todo: Allow default placement overriding for
|
|
3216
|
-
// non-plugins
|
|
3217
|
-
placement, 0, {
|
|
3218
|
-
plugin,
|
|
3219
|
-
meta,
|
|
3220
|
-
placement,
|
|
3221
|
-
// field: `${this.namespace}-plugin-${field}`,
|
|
3222
|
-
fieldAliasOrName,
|
|
3223
|
-
escapeColumn: plugin.escapeColumn !== false,
|
|
3224
|
-
// Plug-in specific (todo: allow specifying
|
|
3225
|
-
// for non-plugins)
|
|
3226
|
-
onByDefault: typeof onByDefault === 'boolean' ? onByDefault : onByDefaultDefault || false,
|
|
3227
|
-
// Three conventions for use by plug-ins but
|
|
3228
|
-
// textbrowser only passes on (might
|
|
3229
|
-
// not need here)
|
|
3230
|
-
applicableField,
|
|
3231
|
-
metaApplicableField,
|
|
3232
|
-
fieldLang: targetLanguage
|
|
3233
|
-
});
|
|
3234
|
-
};
|
|
3235
|
-
|
|
3236
|
-
if (!pluginsForWork.processTargetLanguages(applicableFields, processField)) {
|
|
3237
|
-
processField();
|
|
3238
|
-
}
|
|
3239
|
-
});
|
|
3240
|
-
}
|
|
3241
|
-
|
|
3242
|
-
const localizedFieldNames = fieldInfo.map(fi => fi.fieldAliasOrName);
|
|
3243
|
-
const escapeColumnIndexes = fieldInfo.map(fi => fi.escapeColumn);
|
|
3244
|
-
const fieldLangs = fieldInfo.map(({
|
|
3245
|
-
fieldLang
|
|
3246
|
-
}) => {
|
|
3247
|
-
return fieldLang !== preferredLocale ? fieldLang : null;
|
|
3248
|
-
});
|
|
3249
|
-
const fieldValueAliasMap = getFieldValueAliasMap({
|
|
3250
|
-
schemaItems,
|
|
3251
|
-
fieldInfo,
|
|
3252
|
-
metadataObj,
|
|
3253
|
-
getFieldAliasOrName,
|
|
3254
|
-
usePreferAlias: false
|
|
3255
|
-
});
|
|
3256
|
-
const fieldValueAliasMapPreferred = getFieldValueAliasMap({
|
|
3257
|
-
schemaItems,
|
|
3258
|
-
fieldInfo,
|
|
3259
|
-
metadataObj,
|
|
3260
|
-
getFieldAliasOrName,
|
|
3261
|
-
usePreferAlias: true
|
|
3262
|
-
}); // Todo: Repeats some code in workDisplay; probably need to reuse
|
|
3263
|
-
// these functions more in `Templates.resultsDisplayServerOrClient` too
|
|
3264
|
-
|
|
3265
|
-
const localizeParamNames = $p.localizeParamNames = $p.has('i18n', true) ? $p.get('i18n', true) === '1' : prefI18n === 'true' || prefI18n !== 'false' && this.localizeParamNames;
|
|
3266
|
-
const il = localizeParamNames ? key => l(['params', key]) : key => key;
|
|
3267
|
-
const iil = localizeParamNames ? key => l(['params', 'indexed', key]) : key => key;
|
|
3268
|
-
const ilRaw = localizeParamNames ? (key, suffix = '') => $p.get(il(key) + suffix, true) : (key, suffix = '') => $p.get(key + suffix, true);
|
|
3269
|
-
const iilRaw = localizeParamNames ? (key, suffix = '') => $p.get($p.get('work') + '-' + iil(key) + suffix, true) : (key, suffix = '') => $p.get($p.get('work') + '-' + key + suffix, true); // Now that we know `browseFieldSets`, we can parse `startEnd`
|
|
3270
|
-
|
|
3271
|
-
const browseFieldSetStartEndIdx = browseFieldSets.findIndex((item, i) => iilRaw('startEnd', i + 1));
|
|
3272
|
-
|
|
3273
|
-
if (browseFieldSetStartEndIdx !== -1) {
|
|
3274
|
-
// Todo: i18nize (by work and/or by whole app?)
|
|
3275
|
-
const rangeSep = '-';
|
|
3276
|
-
const partSep = ':'; // Search box functionality (Todo: not yet in UI); should first
|
|
3277
|
-
// avoid numeric startEnd and even work across book
|
|
3278
|
-
// Todo: At least avoid need for book text AND book number in Bible
|
|
3279
|
-
// Todo: Change query beginning at 0 to 1 if none present?
|
|
3280
|
-
// Todo: Support i18nized or canonical aliases (but don't
|
|
3281
|
-
// over-trim in such cases)
|
|
3282
|
-
|
|
3283
|
-
const rawSearch = (iilRaw('startEnd', browseFieldSetStartEndIdx + 1) || '').trim();
|
|
3284
|
-
const [startFull, endFull] = rawSearch.split(rangeSep);
|
|
3285
|
-
|
|
3286
|
-
if (endFull !== undefined) {
|
|
3287
|
-
const startPartVals = startFull.split(partSep);
|
|
3288
|
-
const endPartVals = endFull.split(partSep);
|
|
3289
|
-
const startEndDiff = startPartVals.length - endPartVals.length;
|
|
3290
|
-
|
|
3291
|
-
if (startEndDiff > 0) {
|
|
3292
|
-
// e.g., 5:42:7 - 8 only gets verses 7-8
|
|
3293
|
-
endPartVals.unshift(...startPartVals.slice(0, startEndDiff));
|
|
3294
|
-
} else if (startEndDiff < 0) {
|
|
3295
|
-
// e.g., 5 - 6:2:1 gets all of book 5 to 6:2:1
|
|
3296
|
-
// Todo: We should fill with '0' but since that often
|
|
3297
|
-
// doesn't find anything, we default for now to '1'.
|
|
3298
|
-
startPartVals.push(...Array.from({
|
|
3299
|
-
length: -startEndDiff
|
|
3300
|
-
}).fill('1'));
|
|
3301
|
-
}
|
|
3302
|
-
|
|
3303
|
-
console.log('startPartVals', startPartVals);
|
|
3304
|
-
console.log('endPartVals', endPartVals);
|
|
3305
|
-
startPartVals.forEach((startPartVal, i) => {
|
|
3306
|
-
const endPartVal = endPartVals[i];
|
|
3307
|
-
$p.set(`${$p.get('work')}-start${browseFieldSetStartEndIdx + 1}-${i + 1}`, startPartVal, true);
|
|
3308
|
-
$p.set(`${$p.get('work')}-end${browseFieldSetStartEndIdx + 1}-${i + 1}`, endPartVal, true);
|
|
3309
|
-
});
|
|
3310
|
-
}
|
|
3311
|
-
}
|
|
3312
|
-
|
|
3313
|
-
const browseFieldSetIdx = browseFieldSets.findIndex((item, i) => iilRaw('start', i + 1 + '-1'));
|
|
3314
|
-
const applicableBrowseFieldSet = browseFieldSets[browseFieldSetIdx];
|
|
3315
|
-
const applicableBrowseFieldSetName = setNames[browseFieldSetIdx];
|
|
3316
|
-
const applicableBrowseFieldNames = applicableBrowseFieldSet.map(abfs => abfs.fieldName);
|
|
3317
|
-
const canonicalBrowseFieldSet = browseFieldSets[0];
|
|
3318
|
-
const canonicalBrowseFieldSetName = setNames[0];
|
|
3319
|
-
const canonicalBrowseFieldNames = canonicalBrowseFieldSet.map(abfs => abfs.fieldName);
|
|
3320
|
-
const fieldSchemaTypes = applicableBrowseFieldSet.map(abfs => abfs.fieldSchema.type);
|
|
3321
|
-
|
|
3322
|
-
const buildRangePoint = startOrEnd => applicableBrowseFieldNames.map((bfn, j) => $p.get($p.get('work') + '-' + startOrEnd + (browseFieldSetIdx + 1) + '-' + (j + 1), true));
|
|
3323
|
-
|
|
3324
|
-
const starts = buildRangePoint('start');
|
|
3325
|
-
const ends = buildRangePoint('end');
|
|
3326
|
-
const [hasCaption, caption] = getCaption({
|
|
3327
|
-
starts,
|
|
3328
|
-
ends,
|
|
3329
|
-
applicableBrowseFieldNames,
|
|
3330
|
-
heading
|
|
3331
|
-
});
|
|
3332
|
-
const showInterlinTitles = $pRaw('interlintitle') === '1';
|
|
3333
|
-
console.log('rand', ilRaw('rand') === 'yes');
|
|
3334
|
-
|
|
3335
|
-
const stripToRawFieldValue = (v, i) => {
|
|
3336
|
-
let val;
|
|
3337
|
-
|
|
3338
|
-
if (/^\d+$/.test(v) || fieldValueAliasRegex.test(v)) {
|
|
3339
|
-
val = getRawFieldValue(v);
|
|
3340
|
-
} else {
|
|
3341
|
-
const {
|
|
3342
|
-
rawFieldValueAliasMap
|
|
3343
|
-
} = applicableBrowseFieldSet[i];
|
|
3344
|
-
let dealiased;
|
|
3345
|
-
|
|
3346
|
-
if (rawFieldValueAliasMap) {
|
|
3347
|
-
// Look to dealias
|
|
3348
|
-
const fvEntries = Object.entries(rawFieldValueAliasMap);
|
|
3349
|
-
|
|
3350
|
-
if (Array.isArray(fvEntries[0][1])) {
|
|
3351
|
-
fvEntries.some(([key, arr]) => {
|
|
3352
|
-
if (arr.includes(v)) {
|
|
3353
|
-
dealiased = key;
|
|
3354
|
-
return true;
|
|
3355
|
-
}
|
|
3356
|
-
|
|
3357
|
-
return false;
|
|
3358
|
-
});
|
|
3359
|
-
} else {
|
|
3360
|
-
fvEntries.some(([key, obj]) => {
|
|
3361
|
-
const arr = Object.values(obj);
|
|
3362
|
-
|
|
3363
|
-
if (arr.includes(v)) {
|
|
3364
|
-
dealiased = key;
|
|
3365
|
-
return true;
|
|
3366
|
-
}
|
|
3367
|
-
|
|
3368
|
-
return false;
|
|
3369
|
-
});
|
|
3370
|
-
}
|
|
3371
|
-
}
|
|
3372
|
-
|
|
3373
|
-
val = dealiased === undefined ? v : dealiased;
|
|
3374
|
-
}
|
|
3375
|
-
|
|
3376
|
-
return fieldSchemaTypes[i] === 'integer' ? Number.parseInt(val) : val;
|
|
3377
|
-
};
|
|
3378
|
-
|
|
3379
|
-
const unlocalizedWorkName = fileData.name;
|
|
3380
|
-
const startsRaw = starts.map(stripToRawFieldValue);
|
|
3381
|
-
const endsRaw = ends.map(stripToRawFieldValue);
|
|
3382
|
-
let tableData,
|
|
3383
|
-
usingServerData = false; // Site owner may have configured to skip (e.g., testing)
|
|
3384
|
-
|
|
3385
|
-
if (!skipIndexedDB && // User may have refused, not yet agreed, or are visiting the
|
|
3386
|
-
// results page directly where we don't ask for the permissions
|
|
3387
|
-
// needed for persistent IndexedDB currently so that people can
|
|
3388
|
-
// be brought to a results page without needing to agree to persist
|
|
3389
|
-
// through notifications (or however)
|
|
3390
|
-
!noIndexedDB) {
|
|
3391
|
-
tableData = await new Promise((resolve, reject) => {
|
|
3392
|
-
// Todo: Fetch the work in code based on the non-localized `datafileName`
|
|
3393
|
-
const dbName = this.namespace + '-textbrowser-cache-data';
|
|
3394
|
-
const req = indexedDB.open(dbName);
|
|
3395
|
-
|
|
3396
|
-
req.onsuccess = ({
|
|
3397
|
-
target: {
|
|
3398
|
-
result: db
|
|
3399
|
-
}
|
|
3400
|
-
}) => {
|
|
3401
|
-
const storeName = 'files-to-cache-' + unlocalizedWorkName;
|
|
3402
|
-
const trans = db.transaction(storeName);
|
|
3403
|
-
const store = trans.objectStore(storeName); // Get among browse field sets by index number within URL params
|
|
3404
|
-
|
|
3405
|
-
const index = store.index('browseFields-' + applicableBrowseFieldSetName); // console.log('dbName', dbName);
|
|
3406
|
-
// console.log('storeName', storeName);
|
|
3407
|
-
// console.log('applicableBrowseFieldSetName', 'browseFields-' + applicableBrowseFieldSetName);
|
|
3408
|
-
|
|
3409
|
-
const r = index.getAll(IDBKeyRange.bound(startsRaw, endsRaw));
|
|
3410
|
-
|
|
3411
|
-
r.onsuccess = ({
|
|
3412
|
-
target: {
|
|
3413
|
-
result
|
|
3414
|
-
}
|
|
3415
|
-
}) => {
|
|
3416
|
-
const converted = result.map(r => r.value);
|
|
3417
|
-
resolve(converted);
|
|
3418
|
-
};
|
|
3419
|
-
};
|
|
3420
|
-
});
|
|
3421
|
-
} else {
|
|
3422
|
-
// No need for presorting in indexedDB, given indexes
|
|
3423
|
-
const presort = presorts[browseFieldSetIdx]; // Given that we are not currently wishing to add complexity to
|
|
3424
|
-
// our PHP code (though it is not a problem with Node.js),
|
|
3425
|
-
// we retrieve the whole file and then sort where presorting is
|
|
3426
|
-
// needed
|
|
3427
|
-
// if (presort || this.noDynamic) {
|
|
3428
|
-
|
|
3429
|
-
if (this.noDynamic) {
|
|
3430
|
-
({
|
|
3431
|
-
resolved: {
|
|
3432
|
-
data: tableData
|
|
3433
|
-
}
|
|
3434
|
-
} = await JsonRefs.resolveRefs(fileData.file));
|
|
3435
|
-
runPresort({
|
|
3436
|
-
presort,
|
|
3437
|
-
tableData,
|
|
3438
|
-
applicableBrowseFieldNames,
|
|
3439
|
-
localizedFieldNames
|
|
3440
|
-
});
|
|
3441
|
-
} else {
|
|
3442
|
-
/*
|
|
3443
|
-
const jsonURL = Object.entries({
|
|
3444
|
-
prefI18n, unlocalizedWorkName, startsRaw, endsRaw
|
|
3445
|
-
}).reduce((url, [arg, argVal]) => {
|
|
3446
|
-
return url + '&' + arg + '=' + encodeURIComponent((argVal));
|
|
3447
|
-
}, `${dynamicBasePath}textbrowser?`);
|
|
3448
|
-
*/
|
|
3449
|
-
const jsonURL = `${dynamicBasePath}textbrowser?${$p.toString()}`;
|
|
3450
|
-
tableData = await (await fetch(jsonURL)).json();
|
|
3451
|
-
usingServerData = true;
|
|
3452
|
-
}
|
|
3453
|
-
}
|
|
3454
|
-
|
|
3455
|
-
if (!usingServerData && pluginsForWork) {
|
|
3456
|
-
fieldInfo.forEach(({
|
|
3457
|
-
plugin,
|
|
3458
|
-
placement
|
|
3459
|
-
}, j) => {
|
|
3460
|
-
if (!plugin) {
|
|
3461
|
-
return;
|
|
3462
|
-
}
|
|
3463
|
-
|
|
3464
|
-
tableData.forEach((tr, i) => {
|
|
3465
|
-
// Todo: We should pass on other arguments (like `meta` but on `applicableFields`)
|
|
3466
|
-
tr.splice(placement, 0, null // `${i}-${j}`);
|
|
3467
|
-
);
|
|
3468
|
-
});
|
|
3469
|
-
});
|
|
3470
|
-
fieldInfo.forEach(({
|
|
3471
|
-
plugin,
|
|
3472
|
-
applicableField,
|
|
3473
|
-
fieldLang,
|
|
3474
|
-
meta,
|
|
3475
|
-
metaApplicableField
|
|
3476
|
-
}, j) => {
|
|
3477
|
-
if (!plugin) {
|
|
3478
|
-
return;
|
|
3479
|
-
}
|
|
3480
|
-
|
|
3481
|
-
const applicableFieldIdx = fieldInfo.findIndex(({
|
|
3482
|
-
field
|
|
3483
|
-
}) => {
|
|
3484
|
-
return field === applicableField;
|
|
3485
|
-
}); // Now safe to pass (and set) `j` value as tr array expanded
|
|
3486
|
-
|
|
3487
|
-
tableData.forEach((tr, i) => {
|
|
3488
|
-
const applicableFieldText = tr[applicableFieldIdx];
|
|
3489
|
-
tr[j] = plugin.getCellData && plugin.getCellData({
|
|
3490
|
-
tr,
|
|
3491
|
-
tableData,
|
|
3492
|
-
i,
|
|
3493
|
-
j,
|
|
3494
|
-
applicableField,
|
|
3495
|
-
fieldInfo,
|
|
3496
|
-
applicableFieldIdx,
|
|
3497
|
-
applicableFieldText,
|
|
3498
|
-
fieldLang,
|
|
3499
|
-
getLangDir: rtlDetect.getLangDir,
|
|
3500
|
-
meta,
|
|
3501
|
-
metaApplicableField,
|
|
3502
|
-
$p,
|
|
3503
|
-
thisObj: this
|
|
3504
|
-
}) || applicableFieldText;
|
|
3505
|
-
});
|
|
3506
|
-
console.log('applicableFieldIdx', applicableFieldIdx);
|
|
3507
|
-
});
|
|
3508
|
-
}
|
|
3509
|
-
|
|
3510
|
-
const localeDir = rtlDetect.getLangDir(preferredLocale);
|
|
3511
|
-
const fieldDirs = fieldLangs.map(langCode => {
|
|
3512
|
-
if (!langCode) {
|
|
3513
|
-
return null;
|
|
3514
|
-
}
|
|
3515
|
-
|
|
3516
|
-
const langDir = rtlDetect.getLangDir(langCode);
|
|
3517
|
-
return langDir !== localeDir ? langDir : null;
|
|
3518
|
-
});
|
|
3519
|
-
const templateArgs = {
|
|
3520
|
-
tableData,
|
|
3521
|
-
$p,
|
|
3522
|
-
$pRaw,
|
|
3523
|
-
$pRawEsc,
|
|
3524
|
-
$pEscArbitrary,
|
|
3525
|
-
escapeQuotedCSS,
|
|
3526
|
-
escapeCSS,
|
|
3527
|
-
escapeHTML,
|
|
3528
|
-
l,
|
|
3529
|
-
localizedFieldNames,
|
|
3530
|
-
fieldLangs,
|
|
3531
|
-
fieldDirs,
|
|
3532
|
-
caption,
|
|
3533
|
-
hasCaption,
|
|
3534
|
-
showInterlinTitles,
|
|
3535
|
-
determineEnd: determineEnd({
|
|
3536
|
-
applicableBrowseFieldNames,
|
|
3537
|
-
fieldValueAliasMap,
|
|
3538
|
-
fieldValueAliasMapPreferred,
|
|
3539
|
-
localizedFieldNames,
|
|
3540
|
-
startsRaw,
|
|
3541
|
-
endsRaw
|
|
3542
|
-
}),
|
|
3543
|
-
canonicalBrowseFieldSetName,
|
|
3544
|
-
getCanonicalID: getCanonicalID({
|
|
3545
|
-
canonicalBrowseFieldNames,
|
|
3546
|
-
fieldValueAliasMap,
|
|
3547
|
-
fieldValueAliasMapPreferred,
|
|
3548
|
-
localizedFieldNames
|
|
3549
|
-
}),
|
|
3550
|
-
getCellValue: getCellValue({
|
|
3551
|
-
fieldValueAliasMapPreferred,
|
|
3552
|
-
escapeColumnIndexes,
|
|
3553
|
-
escapeHTML
|
|
3554
|
-
}),
|
|
3555
|
-
checkedAndInterlinearFieldInfo: getCheckedAndInterlinearFieldInfo({
|
|
3556
|
-
localizedFieldNames
|
|
3557
|
-
}),
|
|
3558
|
-
interlinearSeparator: this.interlinearSeparator
|
|
3559
|
-
};
|
|
3560
|
-
return {
|
|
3561
|
-
fieldInfo,
|
|
3562
|
-
$p,
|
|
3563
|
-
applicableBrowseFieldSet,
|
|
3564
|
-
fieldValueAliasMapPreferred,
|
|
3565
|
-
lf,
|
|
3566
|
-
iil,
|
|
3567
|
-
ilRaw,
|
|
3568
|
-
browseFieldSets,
|
|
3569
|
-
lang,
|
|
3570
|
-
metadataObj,
|
|
3571
|
-
fileData,
|
|
3572
|
-
templateArgs
|
|
3573
|
-
};
|
|
3574
|
-
};
|
|
3575
|
-
|
|
3576
|
-
function getIMFFallbackResults({
|
|
3577
|
-
$p,
|
|
3578
|
-
lang,
|
|
3579
|
-
langs,
|
|
3580
|
-
langData,
|
|
3581
|
-
fallbackLanguages,
|
|
3582
|
-
resultsDisplay,
|
|
3583
|
-
basePath = '',
|
|
3584
|
-
localeCallback = false
|
|
3585
|
-
}) {
|
|
3586
|
-
return new Promise((resolve, reject) => {
|
|
3587
|
-
const resultsCallback = (...args) => {
|
|
3588
|
-
const [l10n] = args;
|
|
3589
|
-
|
|
3590
|
-
if (!$p.l10n) {
|
|
3591
|
-
$p.l10n = l10n;
|
|
3592
|
-
}
|
|
3593
|
-
|
|
3594
|
-
return resultsDisplay({
|
|
3595
|
-
l: l10n,
|
|
3596
|
-
lang,
|
|
3597
|
-
fallbackLanguages,
|
|
3598
|
-
imfLocales: imf$1.locales,
|
|
3599
|
-
$p,
|
|
3600
|
-
basePath
|
|
3601
|
-
}, ...args);
|
|
3602
|
-
};
|
|
3603
|
-
|
|
3604
|
-
const imf$1 = imf.IMF({
|
|
3605
|
-
languages: lang,
|
|
3606
|
-
fallbackLanguages,
|
|
3607
|
-
|
|
3608
|
-
localeFileResolver(code) {
|
|
3609
|
-
// Todo: For editing of locales, we might instead resolve all
|
|
3610
|
-
// `$ref` (as with <https://github.com/whitlockjc/json-refs>) and
|
|
3611
|
-
// replace IMF() loadLocales behavior with our own now resolved
|
|
3612
|
-
// locales; see https://github.com/jdorn/json-editor/issues/132
|
|
3613
|
-
return basePath + langData.localeFileBasePath + langs.find(l => l.code === code).locale.$ref;
|
|
3614
|
-
},
|
|
3615
|
-
|
|
3616
|
-
async callback(...args) {
|
|
3617
|
-
if (localeCallback && localeCallback(...args)) {
|
|
3618
|
-
resolve();
|
|
3619
|
-
return;
|
|
3620
|
-
}
|
|
3621
|
-
|
|
3622
|
-
await resultsCallback(...args);
|
|
3623
|
-
resolve();
|
|
3624
|
-
}
|
|
3625
|
-
|
|
3626
|
-
});
|
|
3627
|
-
});
|
|
3628
|
-
}
|
|
3629
|
-
|
|
3630
|
-
/* globals console, location, URL */
|
|
3631
|
-
const setServiceWorkerDefaults = (target, source) => {
|
|
3632
|
-
target.userJSON = source.userJSON || 'resources/user.json';
|
|
3633
|
-
target.languages = source.languages || new URL('../appdata/languages.json', // Todo: Substitute with `import.meta.url`
|
|
3634
|
-
new URL('node_modules/textbrowser/resources/index.js', location)).href;
|
|
3635
|
-
target.serviceWorkerPath = source.serviceWorkerPath || `sw.js?pathToUserJSON=${encodeURIComponent(target.userJSON)}&stylesheets=${encodeURIComponent(JSON.stringify(target.stylesheets || []))}`;
|
|
3636
|
-
target.files = source.files || 'files.json';
|
|
3637
|
-
target.namespace = source.namespace || 'textbrowser';
|
|
3638
|
-
return target;
|
|
3639
|
-
}; // (Unless skipped in code, will wait between install
|
|
3640
|
-
|
|
3641
|
-
/* eslint-env worker */
|
|
3642
|
-
const {
|
|
3643
|
-
ceil
|
|
3644
|
-
} = Math;
|
|
3645
|
-
|
|
3646
|
-
const arrayChunk = (arr, size) => {
|
|
3647
|
-
return [...Array.from({
|
|
3648
|
-
length: ceil(arr.length / size)
|
|
3649
|
-
})].map((_, i) => {
|
|
3650
|
-
const offset = i * size;
|
|
3651
|
-
return arr.slice(offset, offset + size);
|
|
3652
|
-
});
|
|
3653
|
-
}; // Todo: If fetching fails here (or in install), e.g., because activate event
|
|
3654
|
-
// never completed
|
|
3655
|
-
// to cache, de-register and re-register (?); how to detect if all
|
|
3656
|
-
// files in cache?
|
|
3657
|
-
// Todo: Check `oldVersion` and run this first if still too old
|
|
3658
|
-
|
|
3659
|
-
/**
|
|
3660
|
-
* @callback Logger
|
|
3661
|
-
* @param {...any} args
|
|
3662
|
-
* @returns {void}
|
|
3663
|
-
*/
|
|
3664
|
-
|
|
3665
|
-
/**
|
|
3666
|
-
* @param {PlainObject} cfg
|
|
3667
|
-
* @param {string} cfg.namespace
|
|
3668
|
-
* @param {string[]} cfg.files
|
|
3669
|
-
* @param {Logger} cfg.log
|
|
3670
|
-
* @param {string} [cfg.basePath=""]
|
|
3671
|
-
* @returns {Promise<void>}
|
|
3672
|
-
*/
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
async function activateCallback({
|
|
3676
|
-
namespace,
|
|
3677
|
-
files,
|
|
3678
|
-
log,
|
|
3679
|
-
basePath = ''
|
|
3680
|
-
}) {
|
|
3681
|
-
// Now we know we have the files cached, we can postpone
|
|
3682
|
-
// the `indexedDB` processing (which will work offline
|
|
3683
|
-
// anyways); also important to avoid conflicts with
|
|
3684
|
-
// already-running versions upon future sw updates
|
|
3685
|
-
log('Activate: Callback called');
|
|
3686
|
-
const r = await fetch(files);
|
|
3687
|
-
const {
|
|
3688
|
-
groups
|
|
3689
|
-
} = await r.json();
|
|
3690
|
-
|
|
3691
|
-
const addJSONFetch = (arr, path) => {
|
|
3692
|
-
arr.push((async () => (await fetch(basePath + path)).json())());
|
|
3693
|
-
};
|
|
3694
|
-
|
|
3695
|
-
const dataFileNames = [];
|
|
3696
|
-
const dataFiles = [];
|
|
3697
|
-
const schemaFiles = [];
|
|
3698
|
-
const metadataFiles = [];
|
|
3699
|
-
groups.forEach(({
|
|
3700
|
-
files: fileObjs,
|
|
3701
|
-
metadataBaseDirectory,
|
|
3702
|
-
schemaBaseDirectory
|
|
3703
|
-
}) => {
|
|
3704
|
-
fileObjs.forEach(({
|
|
3705
|
-
file: {
|
|
3706
|
-
$ref: filePath
|
|
3707
|
-
},
|
|
3708
|
-
metadataFile,
|
|
3709
|
-
schemaFile,
|
|
3710
|
-
name
|
|
3711
|
-
}) => {
|
|
3712
|
-
// We don't i18nize the name here
|
|
3713
|
-
dataFileNames.push(name);
|
|
3714
|
-
addJSONFetch(dataFiles, filePath);
|
|
3715
|
-
addJSONFetch(metadataFiles, metadataBaseDirectory + '/' + metadataFile);
|
|
3716
|
-
addJSONFetch(schemaFiles, schemaBaseDirectory + '/' + schemaFile);
|
|
3717
|
-
});
|
|
3718
|
-
});
|
|
3719
|
-
const promises = await Promise.all([...dataFiles, ...schemaFiles, ...metadataFiles]);
|
|
3720
|
-
const chunked = arrayChunk(promises, dataFiles.length);
|
|
3721
|
-
const [dataFileResponses, schemaFileResponses, metadataFileResponses] = chunked;
|
|
3722
|
-
log('Activate: Files fetched');
|
|
3723
|
-
const dbName = namespace + '-textbrowser-cache-data';
|
|
3724
|
-
indexedDB.deleteDatabase(dbName);
|
|
3725
|
-
return new Promise((resolve, reject) => {
|
|
3726
|
-
const req = indexedDB.open(dbName);
|
|
3727
|
-
req.addEventListener('upgradeneeded', ({
|
|
3728
|
-
target: {
|
|
3729
|
-
result: db
|
|
3730
|
-
}
|
|
3731
|
-
}) => {
|
|
3732
|
-
db.onversionchange = () => {
|
|
3733
|
-
db.close();
|
|
3734
|
-
const err = new Error('versionchange');
|
|
3735
|
-
err.type = 'versionchange';
|
|
3736
|
-
reject(err);
|
|
3737
|
-
};
|
|
3738
|
-
|
|
3739
|
-
dataFileResponses.forEach(({
|
|
3740
|
-
data: tableRows
|
|
3741
|
-
}, i) => {
|
|
3742
|
-
const dataFileName = dataFileNames[i];
|
|
3743
|
-
const store = db.createObjectStore('files-to-cache-' + dataFileName);
|
|
3744
|
-
const schemaFileResponse = schemaFileResponses[i];
|
|
3745
|
-
const metadataFileResponse = metadataFileResponses[i];
|
|
3746
|
-
const fieldItems = schemaFileResponse.items.items;
|
|
3747
|
-
let browseFields = metadataFileResponse.table.browse_fields;
|
|
3748
|
-
browseFields = Array.isArray(browseFields) ? browseFields : [browseFields];
|
|
3749
|
-
const columnIndexes = [];
|
|
3750
|
-
browseFields.forEach(browseFieldSetObj => {
|
|
3751
|
-
if (typeof browseFieldSetObj === 'string') {
|
|
3752
|
-
browseFieldSetObj = {
|
|
3753
|
-
set: [browseFieldSetObj]
|
|
3754
|
-
};
|
|
3755
|
-
}
|
|
3756
|
-
|
|
3757
|
-
if (!browseFieldSetObj.name) {
|
|
3758
|
-
browseFieldSetObj.name = browseFieldSetObj.set.join(',');
|
|
3759
|
-
}
|
|
3760
|
-
|
|
3761
|
-
const browseFieldSetName = browseFieldSetObj.name;
|
|
3762
|
-
const browseFieldSetIndexes = browseFieldSetObj.set.map(browseField => {
|
|
3763
|
-
// Need to convert to columns for numbers
|
|
3764
|
-
// to become valid key paths
|
|
3765
|
-
return 'c' + fieldItems.findIndex(item => item.title === browseField);
|
|
3766
|
-
});
|
|
3767
|
-
columnIndexes.push(...browseFieldSetIndexes);
|
|
3768
|
-
log('Activate: Creating index:', dataFileName, 'browseFields-' + browseFieldSetName, browseFieldSetIndexes); // No need for using `presort` as our index will sort anyways
|
|
3769
|
-
|
|
3770
|
-
store.createIndex('browseFields-' + browseFieldSetName, browseFieldSetIndexes);
|
|
3771
|
-
});
|
|
3772
|
-
const uniqueColumnIndexes = [...new Set(columnIndexes)];
|
|
3773
|
-
tableRows.forEach((tableRow, j) => {
|
|
3774
|
-
// Todo: Optionally send notice when complete
|
|
3775
|
-
// To take advantage of indexes on our arrays, we
|
|
3776
|
-
// need to transform them to objects! See https://github.com/w3c/IndexedDB/issues/209
|
|
3777
|
-
const objRow = {
|
|
3778
|
-
value: tableRow
|
|
3779
|
-
};
|
|
3780
|
-
uniqueColumnIndexes.forEach(colIdx => {
|
|
3781
|
-
objRow[colIdx] = tableRow[colIdx.slice(1)];
|
|
3782
|
-
}); // log('objRow', objRow);
|
|
3783
|
-
|
|
3784
|
-
store.put(objRow, j);
|
|
3785
|
-
});
|
|
3786
|
-
});
|
|
3787
|
-
});
|
|
3788
|
-
req.addEventListener('success', ({
|
|
3789
|
-
target: {
|
|
3790
|
-
result: db
|
|
3791
|
-
}
|
|
3792
|
-
}) => {
|
|
3793
|
-
log('Activate: Database set-up complete', db); // Todo: Replace this with `ready()` check
|
|
3794
|
-
// in calling code?
|
|
3795
|
-
|
|
3796
|
-
resolve();
|
|
3797
|
-
});
|
|
3798
|
-
|
|
3799
|
-
const onerr = ({
|
|
3800
|
-
error = new Error('dbError')
|
|
3801
|
-
}) => {
|
|
3802
|
-
error.type = 'dbError';
|
|
3803
|
-
reject(error);
|
|
3804
|
-
};
|
|
3805
|
-
|
|
3806
|
-
req.addEventListener('blocked', onerr);
|
|
3807
|
-
req.addEventListener('error', onerr);
|
|
3808
|
-
});
|
|
3809
|
-
}
|
|
3810
|
-
|
|
3811
|
-
/* eslint-disable import/no-commonjs */
|
|
3812
|
-
|
|
3813
|
-
const http = require('http');
|
|
3814
|
-
|
|
3815
|
-
const fetch$1 = require('node-fetch'); // Problems as `import` since 2.1.2
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
const setGlobalVars = require('indexeddbshim/dist/indexeddbshim-UnicodeIdentifiers-node.js'); // Todo (low): See
|
|
3819
|
-
// https://gist.github.com/brettz9/0993fbde6f7352b2bb05f38078cefb29
|
|
3820
|
-
// on idea for adapting node-serviceworker to consume entire service-worker
|
|
3821
|
-
// server-side and apply fetch listener as Node middleware to retrieve a
|
|
3822
|
-
// locally-saved copy of pre-fetched (cached) files during install event
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
const optionDefinitions = [// Node-server-specific
|
|
3826
|
-
{
|
|
3827
|
-
name: 'nodeActivate',
|
|
3828
|
-
type: Boolean
|
|
3829
|
-
}, {
|
|
3830
|
-
name: 'port',
|
|
3831
|
-
type: Number
|
|
3832
|
-
}, {
|
|
3833
|
-
name: 'domain',
|
|
3834
|
-
type: String
|
|
3835
|
-
}, {
|
|
3836
|
-
name: 'basePath',
|
|
3837
|
-
type: String
|
|
3838
|
-
}, // Results display (main)
|
|
3839
|
-
// `namespace`: (but set below)
|
|
3840
|
-
// `skipIndexedDB`: set to `false` below (and the default anyways)
|
|
3841
|
-
// `noDynamic`: not used
|
|
3842
|
-
// `prefI18n`: will instead be set dynamically per user query
|
|
3843
|
-
// `files`: Already set below for service worker
|
|
3844
|
-
{
|
|
3845
|
-
name: 'interlinearSeparator',
|
|
3846
|
-
type: String
|
|
3847
|
-
}, {
|
|
3848
|
-
name: 'localizeParamNames',
|
|
3849
|
-
type: Boolean
|
|
3850
|
-
}, {
|
|
3851
|
-
name: 'trustFormatHTML',
|
|
3852
|
-
type: Boolean
|
|
3853
|
-
}, {
|
|
3854
|
-
name: 'allowPlugins',
|
|
3855
|
-
type: Boolean
|
|
3856
|
-
}, // Results display (template)
|
|
3857
|
-
{
|
|
3858
|
-
name: 'showEmptyInterlinear',
|
|
3859
|
-
type: Boolean
|
|
3860
|
-
}, {
|
|
3861
|
-
name: 'showTitleOnSingleInterlinear',
|
|
3862
|
-
type: Boolean
|
|
3863
|
-
}, // Service worker
|
|
3864
|
-
{
|
|
3865
|
-
name: 'serviceWorkerPath',
|
|
3866
|
-
type: String,
|
|
3867
|
-
defaultOption: true
|
|
3868
|
-
}, {
|
|
3869
|
-
name: 'userJSON',
|
|
3870
|
-
type: String
|
|
3871
|
-
}, {
|
|
3872
|
-
name: 'languages',
|
|
3873
|
-
type: String
|
|
3874
|
-
}, {
|
|
3875
|
-
name: 'files',
|
|
3876
|
-
type: String
|
|
3877
|
-
}, {
|
|
3878
|
-
name: 'namespace',
|
|
3879
|
-
type: String
|
|
3880
|
-
}, {
|
|
3881
|
-
name: 'expressServer',
|
|
3882
|
-
type: String
|
|
3883
|
-
}];
|
|
3884
|
-
|
|
3885
|
-
const userParams = require('command-line-args')(optionDefinitions);
|
|
3886
|
-
|
|
3887
|
-
const port = 'port' in userParams ? userParams.port : 8000;
|
|
3888
|
-
const domain = userParams.domain || `localhost`;
|
|
3889
|
-
const basePath = userParams.basePath || `http://${domain}${port ? ':' + port : ''}/`;
|
|
3890
|
-
|
|
3891
|
-
const userParamsWithDefaults = _objectSpread2(_objectSpread2(_objectSpread2({}, setServiceWorkerDefaults({}, {
|
|
3892
|
-
namespace: 'textbrowser',
|
|
3893
|
-
files: `${basePath}files.json`,
|
|
3894
|
-
// `files` must be absolute path for node-fetch
|
|
3895
|
-
languages: `${basePath}node_modules/textbrowser/appdata/languages.json`,
|
|
3896
|
-
serviceWorkerPath: `${basePath}sw.js?pathToUserJSON=${encodeURIComponent(userParams.userJSON || '')}`
|
|
3897
|
-
})), userParams), {}, {
|
|
3898
|
-
log(...args) {
|
|
3899
|
-
console.log(...args);
|
|
3900
|
-
},
|
|
3901
|
-
|
|
3902
|
-
nodeActivate: undefined,
|
|
3903
|
-
port: undefined,
|
|
3904
|
-
skipIndexedDB: false,
|
|
3905
|
-
// Not relevant here
|
|
3906
|
-
noDynamic: false // Not relevant here
|
|
3907
|
-
|
|
3908
|
-
/*
|
|
3909
|
-
Not in use:
|
|
3910
|
-
logger: {
|
|
3911
|
-
addLogEntry ({text}) {
|
|
3912
|
-
console.log(`Log: ${text}`);
|
|
3913
|
-
},
|
|
3914
|
-
dbError ({
|
|
3915
|
-
type,
|
|
3916
|
-
escapedErrorMessage
|
|
3917
|
-
}) {
|
|
3918
|
-
throw new Error(`Worker aborted: error type ${type}; ${escapedErrorMessage}`);
|
|
3919
|
-
}
|
|
3920
|
-
} */
|
|
3921
|
-
|
|
3922
|
-
});
|
|
3923
|
-
|
|
3924
|
-
console.log('userParamsWithDefaults', userParamsWithDefaults);
|
|
3925
|
-
setGlobalVars(null, {
|
|
3926
|
-
checkOrigin: false
|
|
3927
|
-
}); // Adds `indexedDB` and `IDBKeyRange` to global in Node
|
|
3928
|
-
|
|
3929
|
-
if (userParams.nodeActivate) {
|
|
3930
|
-
global.fetch = fetch$1;
|
|
3931
|
-
|
|
3932
|
-
(async () => {
|
|
3933
|
-
await activateCallback(_objectSpread2(_objectSpread2({}, userParamsWithDefaults), {}, {
|
|
3934
|
-
basePath
|
|
3935
|
-
}));
|
|
3936
|
-
console.log('Activated');
|
|
3937
|
-
})();
|
|
3938
|
-
}
|
|
3939
|
-
|
|
3940
|
-
console.log('past activate check');
|
|
3941
|
-
global.DOMParser = require('dom-parser'); // potentially used within resultsDisplay.js
|
|
3942
|
-
|
|
3943
|
-
const statik = require('@brettz9/node-static');
|
|
3944
|
-
/* eslint-enable import/no-commonjs */
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
const fileServer = new statik.Server(); // Pass path; otherwise uses current directory
|
|
3948
|
-
|
|
3949
|
-
let langData, languagesInstance;
|
|
3950
|
-
const srv = http.createServer(async (req, res) => {
|
|
3951
|
-
var _req$headers$acceptL, _req$headers$acceptL2;
|
|
3952
|
-
|
|
3953
|
-
// console.log('URL::', new URL(req.url));
|
|
3954
|
-
const {
|
|
3955
|
-
pathname,
|
|
3956
|
-
search
|
|
3957
|
-
} = new URL(req.url, basePath);
|
|
3958
|
-
|
|
3959
|
-
if (pathname !== '/textbrowser' || !search) {
|
|
3960
|
-
const staticServer = () => {
|
|
3961
|
-
if (pathname.includes('.git')) {
|
|
3962
|
-
req.url = '/index.html';
|
|
3963
|
-
}
|
|
3964
|
-
|
|
3965
|
-
fileServer.serve(req, res);
|
|
3966
|
-
};
|
|
3967
|
-
|
|
3968
|
-
if (userParams.expressServer) {
|
|
3969
|
-
// eslint-disable-next-line node/global-require, import/no-dynamic-require
|
|
3970
|
-
const server = require(userParams.expressServer)();
|
|
3971
|
-
|
|
3972
|
-
server.get('*', staticServer);
|
|
3973
|
-
server(req, res);
|
|
3974
|
-
return;
|
|
3975
|
-
}
|
|
3976
|
-
|
|
3977
|
-
req.addListener('end', staticServer).resume();
|
|
3978
|
-
/*
|
|
3979
|
-
res.writeHead(404, {'Content-Type': 'text/html'});
|
|
3980
|
-
res.end('<h1>File not found</h1>');
|
|
3981
|
-
*/
|
|
3982
|
-
|
|
3983
|
-
return;
|
|
3984
|
-
}
|
|
3985
|
-
|
|
3986
|
-
const languages = ((_req$headers$acceptL = (_req$headers$acceptL2 = req.headers['accept-language']) === null || _req$headers$acceptL2 === void 0 ? void 0 : _req$headers$acceptL2.replace(/;q=.*?$/, '')) !== null && _req$headers$acceptL !== void 0 ? _req$headers$acceptL : 'en-US').split(',');
|
|
3987
|
-
global.navigator = {
|
|
3988
|
-
language: languages[0],
|
|
3989
|
-
languages
|
|
3990
|
-
};
|
|
3991
|
-
const $p = new IntlURLSearchParams({
|
|
3992
|
-
params: search
|
|
3993
|
-
});
|
|
3994
|
-
|
|
3995
|
-
if (!langData || !languagesInstance) {
|
|
3996
|
-
langData = await simpleGetJson.getJSON(userParamsWithDefaults.languages);
|
|
3997
|
-
languagesInstance = new Languages({
|
|
3998
|
-
langData
|
|
3999
|
-
});
|
|
4000
|
-
}
|
|
4001
|
-
|
|
4002
|
-
const {
|
|
4003
|
-
lang,
|
|
4004
|
-
langs,
|
|
4005
|
-
fallbackLanguages
|
|
4006
|
-
} = languagesInstance.getLanguageInfo({
|
|
4007
|
-
$p
|
|
4008
|
-
});
|
|
4009
|
-
getIMFFallbackResults({
|
|
4010
|
-
$p,
|
|
4011
|
-
basePath,
|
|
4012
|
-
lang,
|
|
4013
|
-
langs,
|
|
4014
|
-
fallbackLanguages,
|
|
4015
|
-
langData,
|
|
4016
|
-
|
|
4017
|
-
async resultsDisplay(resultsArgs, ...args) {
|
|
4018
|
-
const serverOutput = $p.get('serverOutput', true);
|
|
4019
|
-
let allowPlugins = $p.get('allowPlugins', true);
|
|
4020
|
-
|
|
4021
|
-
if (allowPlugins === '0') {
|
|
4022
|
-
allowPlugins = false;
|
|
4023
|
-
}
|
|
4024
|
-
|
|
4025
|
-
const isHTML = serverOutput === 'html';
|
|
4026
|
-
res.writeHead(200, {
|
|
4027
|
-
'Content-Type': isHTML ? 'text/html;charset=utf8' : 'application/json;charset=utf8'
|
|
4028
|
-
});
|
|
4029
|
-
resultsArgs = _objectSpread2(_objectSpread2({}, resultsArgs), {}, {
|
|
4030
|
-
skipIndexedDB: false,
|
|
4031
|
-
allowPlugins,
|
|
4032
|
-
serverOutput,
|
|
4033
|
-
langData,
|
|
4034
|
-
prefI18n: $p.get('prefI18n', true)
|
|
4035
|
-
}); // Todo: Move sw-sample.js to bahaiwritings and test
|
|
4036
|
-
|
|
4037
|
-
const result = await resultsDisplayServer.call(_objectSpread2(_objectSpread2({}, userParamsWithDefaults), {}, {
|
|
4038
|
-
lang,
|
|
4039
|
-
langs,
|
|
4040
|
-
fallbackLanguages
|
|
4041
|
-
}), resultsArgs, ...args);
|
|
4042
|
-
res.end(isHTML ? result : JSON.stringify(result));
|
|
4043
|
-
}
|
|
4044
|
-
|
|
4045
|
-
});
|
|
4046
|
-
});
|
|
4047
|
-
|
|
4048
|
-
if (!userParams.domain) {
|
|
4049
|
-
srv.listen(port);
|
|
4050
|
-
} else {
|
|
4051
|
-
srv.listen(port, userParams.domain);
|
|
4052
|
-
}
|