@entryscape/rdforms 10.14.0 → 10.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4027 @@
1
+ /******/ (() => { // webpackBootstrap
2
+ /******/ "use strict";
3
+ /******/ var __webpack_modules__ = ({
4
+
5
+ /***/ 229:
6
+ /***/ ((module) => {
7
+
8
+ module.exports = require("node-fetch");
9
+
10
+ /***/ })
11
+
12
+ /******/ });
13
+ /************************************************************************/
14
+ /******/ // The module cache
15
+ /******/ var __webpack_module_cache__ = {};
16
+ /******/
17
+ /******/ // The require function
18
+ /******/ function __webpack_require__(moduleId) {
19
+ /******/ // Check if module is in cache
20
+ /******/ var cachedModule = __webpack_module_cache__[moduleId];
21
+ /******/ if (cachedModule !== undefined) {
22
+ /******/ return cachedModule.exports;
23
+ /******/ }
24
+ /******/ // Create a new module (and put it into the cache)
25
+ /******/ var module = __webpack_module_cache__[moduleId] = {
26
+ /******/ // no module.id needed
27
+ /******/ // no module.loaded needed
28
+ /******/ exports: {}
29
+ /******/ };
30
+ /******/
31
+ /******/ // Execute the module function
32
+ /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
33
+ /******/
34
+ /******/ // Return the exports of the module
35
+ /******/ return module.exports;
36
+ /******/ }
37
+ /******/
38
+ /************************************************************************/
39
+ /******/ /* webpack/runtime/compat get default export */
40
+ /******/ (() => {
41
+ /******/ // getDefaultExport function for compatibility with non-harmony modules
42
+ /******/ __webpack_require__.n = (module) => {
43
+ /******/ var getter = module && module.__esModule ?
44
+ /******/ () => (module['default']) :
45
+ /******/ () => (module);
46
+ /******/ __webpack_require__.d(getter, { a: getter });
47
+ /******/ return getter;
48
+ /******/ };
49
+ /******/ })();
50
+ /******/
51
+ /******/ /* webpack/runtime/define property getters */
52
+ /******/ (() => {
53
+ /******/ // define getter functions for harmony exports
54
+ /******/ __webpack_require__.d = (exports, definition) => {
55
+ /******/ for(var key in definition) {
56
+ /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
57
+ /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
58
+ /******/ }
59
+ /******/ }
60
+ /******/ };
61
+ /******/ })();
62
+ /******/
63
+ /******/ /* webpack/runtime/hasOwnProperty shorthand */
64
+ /******/ (() => {
65
+ /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
66
+ /******/ })();
67
+ /******/
68
+ /******/ /* webpack/runtime/make namespace object */
69
+ /******/ (() => {
70
+ /******/ // define __esModule on exports
71
+ /******/ __webpack_require__.r = (exports) => {
72
+ /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
73
+ /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
74
+ /******/ }
75
+ /******/ Object.defineProperty(exports, '__esModule', { value: true });
76
+ /******/ };
77
+ /******/ })();
78
+ /******/
79
+ /************************************************************************/
80
+ var __webpack_exports__ = {};
81
+ // ESM COMPAT FLAG
82
+ __webpack_require__.r(__webpack_exports__);
83
+
84
+ // EXPORTS
85
+ __webpack_require__.d(__webpack_exports__, {
86
+ ItemStore: () => (/* reexport */ ItemStore),
87
+ bundleLoader: () => (/* reexport */ bundleLoader),
88
+ engine: () => (/* reexport */ engine),
89
+ system: () => (/* reexport */ system),
90
+ utils: () => (/* reexport */ utils),
91
+ validate: () => (/* reexport */ validate)
92
+ });
93
+
94
+ ;// external "@entryscape/rdfjson"
95
+ const rdfjson_namespaceObject = require("@entryscape/rdfjson");
96
+ ;// external "moment"
97
+ const external_moment_namespaceObject = require("moment");
98
+ var external_moment_default = /*#__PURE__*/__webpack_require__.n(external_moment_namespaceObject);
99
+ ;// external "lodash"
100
+ const external_lodash_namespaceObject = require("lodash");
101
+ ;// ./src/model/system.js
102
+ /* eslint-disable no-unused-vars */
103
+
104
+ const generateUIDNotMoreThan1million = () =>
105
+ // eslint-disable-next-line no-restricted-properties,no-bitwise
106
+ `0000${(Math.random() * Math.pow(36, 4) << 0).toString(36)}`.slice(-4);
107
+ const createURI = (item, parentBinding) => {
108
+ const parentURI = parentBinding.getChildrenRootUri();
109
+ const hash = parentURI.lastIndexOf('#');
110
+ const newURIBase = hash === -1 ? `${parentURI}#` : parentURI.substring(0, hash + 1);
111
+ const graph = parentBinding.getGraph()._graph;
112
+ while (true) {
113
+ const newURI = newURIBase + generateUIDNotMoreThan1million();
114
+ if (graph[newURI] == null) {
115
+ return newURI;
116
+ }
117
+ }
118
+ };
119
+ const getFallbackChoice = (item, value, seeAlso, graph) => {
120
+ if (item.getNodetype() === 'URI' || item.getNodetype() === 'RESOURCE') {
121
+ let lmap = utils.getLocalizedMap(graph, value, item.getURIValueLabelProperties());
122
+ if (!lmap) {
123
+ const lastHash = value.lastIndexOf('#');
124
+ const lastSlash = value.lastIndexOf('/');
125
+ if (lastHash > 0 || lastSlash > 0) {
126
+ lmap = {
127
+ '': decodeURIComponent(value.substring(1 + (lastHash > lastSlash ? lastHash : lastSlash)))
128
+ };
129
+ } else {
130
+ lmap = {
131
+ '': value
132
+ };
133
+ }
134
+ }
135
+ return {
136
+ value,
137
+ label: lmap
138
+ };
139
+ }
140
+ return {
141
+ value,
142
+ label: value
143
+ };
144
+ };
145
+
146
+ /**
147
+ * This method is a default implementation, feel free to override with specific construction of matched choices.
148
+ * Returns a choice object containing a value and a label.
149
+ * Override this function to provide specific loading of a choice.
150
+ * If you need to do this asynchonously provide a "load" method on the returned choice object.
151
+ * To indicate that a matched value is not acceptable anymore,
152
+ * set the flag mismatch to true in the returned choice object.
153
+ *
154
+ * @param item the RDForms template item matched against.
155
+ * @param value the value to match
156
+ * @param seeAlso if provided the value is a URI and a rdfs:seeAlso property has been found in the graph
157
+ * @param graph the RDF graph where the value was matched
158
+ * @returns {Object} an object containing a value, a label (object with language codes as attributes),
159
+ * an optional load callback method and an optional mismatch flag.
160
+ * @see openChoiceSelector
161
+ */
162
+ const getChoice = (item, value, seeAlso, graph) => getFallbackChoice(item, value, seeAlso, graph);
163
+ const labelProperties = ['http://www.w3.org/2000/01/rdf-schema#label', 'http://purl.org/dc/terms/title', 'http://purl.org/dc/elements/1.1/title', 'http://www.w3.org/2004/02/skos/core#prefLabel', 'http://xmlns.com/foaf/0.1/name', 'http://xmlns.com/foaf/name'];
164
+
165
+ /**
166
+ * This method is a fake implementation for launching a dialog for choosing system choices.
167
+ * The method MUST be overridden if the template you use depends on system choices.
168
+ * (System choices in a RDForm template choice items means that there are neither inline choices
169
+ * or an ontology URL given in combination with provided cached choices for the given ontology URL).
170
+ *
171
+ * @param {rdforms.model.Binding} binding the binding where the choice will be given
172
+ * @param {Function} callback a method to call with a choice object when the user has selected an appropriate choice.
173
+ */
174
+ const openChoiceSelector = (binding, callback) => {
175
+ alert('This alert is a placeholder for a search dialog that should be provided as part of the integration of ' + 'RDForms into a wider system.\nSimply override the methods "getChoices" and "openChoiceSelector" in the ' + 'system module.');
176
+ callback({
177
+ value: 'http://example.com/choice1',
178
+ label: {
179
+ en: 'First choice',
180
+ sv: 'Första valet'
181
+ }
182
+ });
183
+ };
184
+
185
+ /** The implementor is expected to provide an application specific override
186
+ * For example:
187
+ * system.attachExternalLinkBehaviour = (node, binding) => node.setAttribute("target", "_blank");
188
+ */
189
+ const attachExternalLinkBehaviour = () => false;
190
+
191
+ /** The implementor is expected to provide an application specific override
192
+ * For example:
193
+ * system.attachLinkBehaviour = (node, binding) => node.setAttribute("target", "_blank");
194
+ */
195
+ const attachLinkBehaviour = (node, binding) => false;
196
+ const hasDnDSupport = binding => false;
197
+ const addDnD = (binding, node, onDrop) => ({});
198
+ /* harmony default export */ const system = ({
199
+ hasDnDSupport,
200
+ addDnD,
201
+ attachExternalLinkBehaviour,
202
+ attachLinkBehaviour,
203
+ openChoiceSelector,
204
+ getChoice,
205
+ getFallbackChoice,
206
+ labelProperties,
207
+ createURI
208
+ });
209
+ ;// ./src/utils.js
210
+
211
+
212
+
213
+ const getLocalizedValue = (hash, locale) => {
214
+ const _locale = locale || external_moment_default().locale();
215
+ if (hash == null) {
216
+ return {
217
+ precision: 'none'
218
+ };
219
+ } else if (typeof hash === 'string') {
220
+ return {
221
+ value: hash,
222
+ precision: 'nolang',
223
+ lang: ''
224
+ };
225
+ } else if (hash.hasOwnProperty(_locale)) {
226
+ return {
227
+ value: hash[_locale],
228
+ precision: 'exact',
229
+ lang: _locale
230
+ };
231
+ }
232
+ const pos = _locale.indexOf('_');
233
+ if (pos > -1 && hash.hasOwnProperty(_locale.substr(0, 2))) {
234
+ return {
235
+ value: hash[_locale.substr(0, 2)],
236
+ precision: 'coarsen',
237
+ lang: _locale.substr(0, 2)
238
+ };
239
+ } else if (hash.hasOwnProperty('en')) {
240
+ return {
241
+ value: hash.en,
242
+ precision: 'default',
243
+ lang: 'en'
244
+ };
245
+ } else if (hash.hasOwnProperty('')) {
246
+ return {
247
+ value: hash[''],
248
+ precision: 'nolang',
249
+ lang: ''
250
+ };
251
+ }
252
+ const allLangs = Object.keys(hash);
253
+ if (allLangs.length > 0) {
254
+ return {
255
+ value: hash[allLangs[0]],
256
+ precision: 'any',
257
+ lang: allLangs[0]
258
+ };
259
+ }
260
+ return {
261
+ precision: 'none'
262
+ };
263
+ };
264
+ const f = (graph, subject, prop) => {
265
+ const stmts = graph.find(subject, prop);
266
+ if (stmts.length > 0) {
267
+ const obj = {};
268
+ for (let s = 0; s < stmts.length; s++) {
269
+ obj[stmts[s].getLanguage() || ''] = stmts[s].getValue();
270
+ }
271
+ return obj;
272
+ }
273
+ return undefined;
274
+ };
275
+ const getLocalizedMap = (graphOrBinding, subject, propArr) => {
276
+ let graph;
277
+ let _subject = subject;
278
+ let _propArr = propArr;
279
+ if (graphOrBinding.getItem) {
280
+ // graphOrBinding is a Binding
281
+ graph = graphOrBinding.getGraph();
282
+ _subject = graphOrBinding.getValue();
283
+ _propArr = graphOrBinding.getItem().getURIValueLabelProperties();
284
+ } else {
285
+ graph = graphOrBinding;
286
+ }
287
+ if (_propArr == null || _propArr.length === 0) {
288
+ _propArr = system.labelProperties;
289
+ }
290
+ for (let i = 0; i < _propArr.length; i++) {
291
+ const props = _propArr[i];
292
+ if (Array.isArray(props)) {
293
+ const valueArr = [];
294
+ for (let j = 0; j < props.length; j++) {
295
+ const value = f(graph, _subject, props[j]);
296
+ if (value) {
297
+ valueArr.push(getLocalizedValue(value).value);
298
+ }
299
+ }
300
+ if (valueArr.length > 0) {
301
+ return {
302
+ '': valueArr.join(' ')
303
+ };
304
+ }
305
+ } else {
306
+ const value = f(graph, _subject, props);
307
+ if (value) {
308
+ return value;
309
+ }
310
+ }
311
+ }
312
+ return undefined;
313
+ };
314
+ const cloneArrayWithLabels = (objects, noSort) => {
315
+ const itemsArray = [];
316
+ for (let i = 0; i < objects.length; i++) {
317
+ const o = objects[i];
318
+ const currentLabel = getLocalizedValue(o.label);
319
+ const obj = {
320
+ value: o.value,
321
+ label: currentLabel.value || o.value || ''
322
+ };
323
+ if (o.top === true) {
324
+ obj.top = true;
325
+ }
326
+ if (o.children != null) {
327
+ obj.children = (0,external_lodash_namespaceObject.cloneDeep)(o.children);
328
+ }
329
+ if (o.selectable === false) {
330
+ obj.selectable = false;
331
+ } else {
332
+ obj.selectable = true;
333
+ }
334
+ itemsArray.push(obj);
335
+ }
336
+ if (noSort !== true) {
337
+ itemsArray.sort((o1, o2) => o1.label > o2.label ? 1 : -1);
338
+ }
339
+ return itemsArray;
340
+ };
341
+ const extractGist = (str, template) => {
342
+ let _template = template;
343
+ if (_template) {
344
+ if (_template.indexOf('$1') === -1) {
345
+ _template += '$1';
346
+ }
347
+ const r = `${_template}`.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1').replace('\\$1', '(.*)');
348
+ const e = new RegExp(r).exec(str);
349
+ if (e != null) {
350
+ return e[1];
351
+ }
352
+ }
353
+ return str;
354
+ };
355
+ const findFirstValue = (engine, graph, uri, template) => {
356
+ const fvb = engine.findFirstValueBinding(engine.match(graph, uri, template), false);
357
+ if (!fvb) {
358
+ return undefined;
359
+ }
360
+ if (fvb.getChoice) {
361
+ return getLocalizedValue(fvb.getChoice().label).value;
362
+ }
363
+ return fvb.getGist();
364
+ };
365
+ const generateUUID = () => {
366
+ // Public Domain/MIT
367
+ let d = new Date().getTime();
368
+ if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
369
+ d += performance.now(); // use high-precision timer if available
370
+ }
371
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
372
+ // eslint-disable-next-line no-mixed-operators,no-bitwise
373
+ const r = (d + Math.random() * 16) % 16 | 0;
374
+ d = Math.floor(d / 16);
375
+ // eslint-disable-next-line no-bitwise,no-mixed-operators
376
+ return (c === 'x' ? r : r & 0x3 | 0x8).toString(16);
377
+ });
378
+ };
379
+ /* harmony default export */ const utils = ({
380
+ getLocalizedValue,
381
+ getLocalizedMap,
382
+ cloneArrayWithLabels,
383
+ extractGist,
384
+ findFirstValue,
385
+ generateUUID
386
+ });
387
+ ;// ./src/template/Item.js
388
+
389
+
390
+ let itemCount = 0;
391
+ const setObjAttr = (obj, attr, value) => {
392
+ if (value === null || typeof value === 'undefined' || value === '' || Array.isArray(value) && value.length === 0) {
393
+ delete obj[attr];
394
+ } else {
395
+ obj[attr] = value;
396
+ }
397
+ };
398
+ class Item {
399
+ /**
400
+ * Base functionality of Text, Group and Choice item classes.
401
+ */
402
+ constructor({
403
+ source = {},
404
+ bundle,
405
+ itemStore
406
+ }) {
407
+ this._itemStore = itemStore;
408
+ this._source = source;
409
+ this._bundle = bundle;
410
+ itemCount += 1;
411
+ this._internalId = itemCount;
412
+ this._styles = ['heading', 'invisible', 'invisibleGroup', 'stars', 'commentOn', 'multiline', 'horizontalRadioButtons', 'verticalRadioButtons', 'horizontalCheckBoxes', 'verticalCheckBoxes', 'nonEditable', 'expandable', 'compact', 'nonCompact', 'dropDown', 'table', 'firstcolumnfixedtable', 'tree', 'externalLink', 'internalLink', 'noLink', 'image', 'label', 'tooltip', 'strictmatch', 'relaxedDatatypeMatch', 'viewAllTranslations', 'filterTranslations', 'email', 'atLeastOneChild', 'atMostOneChild', 'exactlyOneChild', 'noLabelInPresent', 'autoInitDate', 'autoUpdateDate', 'autoUUID', 'autoValue', 'showURI', 'showLink', 'showValue', 'showDescriptionInPresent', 'showDescriptionInEdit', 'textAsChoice', 'preserveOrderOfChoices', 'linkWithLabel', 'deprecated', 'inline', 'truncate', 'noTruncate', 'card', 'cardInPresent', 'cardInEdit'];
413
+ this._getLocalizedValue = utils.getLocalizedValue;
414
+ }
415
+
416
+ //= ==================================================
417
+ // Public API
418
+ //= ==================================================
419
+ getId() {
420
+ const s = this.getSource(true);
421
+ return s.id || s['@id'];
422
+ }
423
+ setId(id) {
424
+ setObjAttr(this.getSource(true), 'id', id);
425
+ delete s['@id'];
426
+ }
427
+ getType(original) {
428
+ const s = this.getSource(original);
429
+ return s.type || s['@type'];
430
+ }
431
+ setType(typeStr) {
432
+ setObjAttr(this.getSource(true), 'type', typeStr);
433
+ delete s['@type'];
434
+ this.refreshExtends();
435
+ }
436
+ getExtends() {
437
+ return this.getSource(true).extends || '';
438
+ }
439
+ refreshExtends() {
440
+ if (this.isExtention) {
441
+ this.setExtends(this.getExtends());
442
+ }
443
+ }
444
+ setExtends(extendsStr) {
445
+ const s = this.getSource(true);
446
+ const ei = this._itemStore.getItem(extendsStr);
447
+ if (ei == null) {
448
+ this._source = s;
449
+ } else {
450
+ this._source = this._itemStore.createExtendedSource(ei.getSource(), s);
451
+ }
452
+ if (extendsStr === '' || extendsStr == null) {
453
+ delete s.extends;
454
+ } else {
455
+ s.extends = extendsStr;
456
+ }
457
+ }
458
+ isExtention() {
459
+ return this.getExtends() != null;
460
+ }
461
+ _getText(attr, returnDetails, original) {
462
+ const s = this.getSource(original);
463
+ return returnDetails ? utils.getLocalizedValue(s[attr]) : utils.getLocalizedValue(s[attr]).value;
464
+ }
465
+ _setText(attr, value, lang) {
466
+ const s = this.getSource(true);
467
+ s[attr] = this._setLangHash(s[attr], value, lang);
468
+ this.refreshExtends();
469
+ }
470
+ _setTextMap(attr, map) {
471
+ setObjAttr(this.getSource(true), attr, map);
472
+ this.refreshExtends();
473
+ }
474
+ getLabel(returnDetails, original) {
475
+ return this._getText('label', returnDetails, original);
476
+ }
477
+ setLabel(value, lang) {
478
+ this._setText('label', value, lang);
479
+ }
480
+ getLabelMap(original) {
481
+ return this.getSource(original).label;
482
+ }
483
+ setLabelMap(map) {
484
+ this._setTextMap('label', map);
485
+ }
486
+ getEditLabel(returnDetails, original) {
487
+ return this._getText('editlabel', returnDetails, original);
488
+ }
489
+ setEditLabel(value, lang) {
490
+ this._setText('editlabel', value, lang);
491
+ }
492
+ getEditLabelMap(original) {
493
+ return this.getSource(original).editlabel;
494
+ }
495
+ setEditLabelMap(map) {
496
+ this._setTextMap('editlabel', map);
497
+ }
498
+ getDescription(returnDetails, original) {
499
+ return this._getText('description', returnDetails, original);
500
+ }
501
+ setDescription(value, lang) {
502
+ this._setText('description', value, lang);
503
+ }
504
+ getDescriptionMap(original) {
505
+ return this.getSource(original).description;
506
+ }
507
+ setDescriptionMap(map) {
508
+ this._setTextMap('description', map);
509
+ }
510
+ getEditDescription(returnDetails, original) {
511
+ return this._getText('editdescription', returnDetails, original);
512
+ }
513
+ setEditDescription(value, lang) {
514
+ this._setText('editdescription', value, lang);
515
+ }
516
+ getEditDescriptionMap(original) {
517
+ return this.getSource(original).editdescription;
518
+ }
519
+ setEditDescriptionMap(map) {
520
+ this._setTextMap('editdescription', map);
521
+ }
522
+ getHelp(returnDetails, original) {
523
+ return this._getText('help', returnDetails, original);
524
+ }
525
+ setHelp(value, lang) {
526
+ this._setText('help', value, lang);
527
+ }
528
+ getHelpMap(original) {
529
+ return this.getSource(original).help;
530
+ }
531
+ setHelpMap(map) {
532
+ this._setTextMap('help', map);
533
+ }
534
+ getPlaceholder(returnDetails, original) {
535
+ return this._getText('placeholder', returnDetails, original);
536
+ }
537
+ setPlaceholder(value, lang) {
538
+ this._setText('placeholder', value, lang);
539
+ }
540
+ getPlaceholderMap(original) {
541
+ return this.getSource(original).placeholder;
542
+ }
543
+ setPlaceholderMap(map) {
544
+ this._setTextMap('placeholder', map);
545
+ }
546
+ getPurpose(returnDetails, original) {
547
+ return this._getText('purpose', returnDetails, original);
548
+ }
549
+ setPurpose(value, lang) {
550
+ this._setText('purpose', value, lang);
551
+ }
552
+ getPurposeMap(original) {
553
+ return this.getSource(original).purpose;
554
+ }
555
+ setPurposeMap(map) {
556
+ this._setTextMap('purpose', map);
557
+ }
558
+ getSpecification(returnDetails, original) {
559
+ return this._getText('specification', returnDetails, original);
560
+ }
561
+ setSpecification(value, lang) {
562
+ this._setText('specification', value, lang);
563
+ }
564
+ getSpecificationMap(original) {
565
+ return this.getSource(original).specification;
566
+ }
567
+ setSpecificationMap(map) {
568
+ this._setTextMap('specification', map);
569
+ }
570
+ getText(attr, returnDetails, original) {
571
+ const s = this.getSource(original);
572
+ const t = s.text || {};
573
+ return returnDetails ? utils.getLocalizedValue(t[attr]) : utils.getLocalizedValue(t[attr]).value;
574
+ }
575
+ setText(attr, value, lang) {
576
+ const s = this.getSource(true);
577
+ const t = s.text = s.text || {};
578
+ t[attr] = this._setLangHash(t[attr], value, lang);
579
+ this.refreshExtends();
580
+ }
581
+ setTextMap(attr, map) {
582
+ const s = this.getSource(true);
583
+ const t = s.text = s.text || {};
584
+ setObjAttr(t, attr, map);
585
+ this.refreshExtends();
586
+ }
587
+ getEnhanced(attribute) {
588
+ const s = this.getSource(true);
589
+ if (typeof s.enhanced === 'boolean') {
590
+ return s.enhanced;
591
+ }
592
+ return s.enhanced && s.enhanced[attribute] || false;
593
+ }
594
+ setEnhanced(attribute, enhanced) {
595
+ const s = this.getSource(true);
596
+ if (typeof attribute === 'boolean') {
597
+ if (attribute === true) {
598
+ s.enhanced = true;
599
+ } else {
600
+ delete s.enhanced;
601
+ }
602
+ } else {
603
+ s.enhanced = typeof s.enhanced === 'boolean' ? {} : s.enhanced || {};
604
+ if (enhanced) {
605
+ s.enhanced[attribute] = true;
606
+ } else {
607
+ delete s.enhanced[attribute];
608
+ if (Object.keys(s).length === 0) {
609
+ delete s.enhanced;
610
+ }
611
+ }
612
+ }
613
+ // Simple way to refresh this._source which is a cache including potential enhancements
614
+ this.setExtends(this.getExtends());
615
+ }
616
+
617
+ /**
618
+ * @return {String|null} as a URI, may be null for Groups, never null for Text or choice
619
+ * item types.
620
+ */
621
+ getProperty(original) {
622
+ let p = null;
623
+ const source = this.getSource(original);
624
+ if (source) {
625
+ p = source.property;
626
+ if (p != null && p !== '') {
627
+ p = rdfjson_namespaceObject.namespaces.expand(p);
628
+ }
629
+ }
630
+ return p;
631
+ }
632
+ setProperty(prop) {
633
+ setObjAttr(this.getSource(true), 'property', prop);
634
+ this.refreshExtends();
635
+ }
636
+
637
+ /**
638
+ * If the value is a uri, it is not nice to show it directly.
639
+ * Hence, we need to discover (or provide if we are in edit mode)
640
+ * a suitable label for the URI. Labels are typically provided by
641
+ * having triples with the URI as subject, but which predicates do we use?
642
+ *
643
+ * URIValueLabelProperties is an array of properties to be used in this scenario,
644
+ * were each should be tried in turn. In editing mode the first property should be used,
645
+ * alternatively a dropdown can be used to select among the properties.
646
+ *
647
+ * @return {Array} array of properties
648
+ * The property value pairs corresponds to predicate and objects in required tripples.
649
+ */
650
+ getURIValueLabelProperties(original) {
651
+ const arr = this.getSource(original).uriValueLabelProperties;
652
+ if (arr != null) {
653
+ return arr.map(uri => rdfjson_namespaceObject.namespaces.expand(uri));
654
+ }
655
+ return arr;
656
+ }
657
+ setURIValueLabelProperties(props) {
658
+ setObjAttr(this.getSource(true), 'uriValueLabelProperties', props);
659
+ this.refreshExtends();
660
+ }
661
+
662
+ /**
663
+ * @return {Object} never available for Text item type.
664
+ * The property value pairs corresponds to predicate and objects in required tripples.
665
+ */
666
+ getConstraints(original) {
667
+ const constr = this.getSource(original).constraints;
668
+ if (constr != null) {
669
+ const nc = {};
670
+ Object.keys(constr).forEach(key => {
671
+ const val = constr[key];
672
+ if (Array.isArray(val)) {
673
+ nc[rdfjson_namespaceObject.namespaces.expand(key)] = val.map(v => rdfjson_namespaceObject.namespaces.expand(v));
674
+ } else {
675
+ nc[rdfjson_namespaceObject.namespaces.expand(key)] = rdfjson_namespaceObject.namespaces.expand(val);
676
+ }
677
+ });
678
+ return nc;
679
+ }
680
+ return constr;
681
+ }
682
+ setConstraints(constr) {
683
+ setObjAttr(this.getSource(true), 'constraints', constr);
684
+ this.refreshExtends();
685
+ }
686
+
687
+ /**
688
+ * Deps is an array of strings corresponding to predicates, "*" may be used to match
689
+ * anything. The final string in the path may correspond to the object,
690
+ * e.g. a literal or uri.
691
+ * By default, the dependency is given relative to the current items parent.
692
+ * If dependency path should start higher up it can be indicated by providing one or more
693
+ * initial strings with value "..".
694
+ *
695
+ * @return {Object} dependency path that must exist for this item to be visible.
696
+ *
697
+ */
698
+ getDeps(original) {
699
+ const deps = this.getSource(original).deps;
700
+ if (deps != null) {
701
+ return deps.map(d => {
702
+ if (d !== '*' && d !== '..') {
703
+ return rdfjson_namespaceObject.namespaces.expand(d);
704
+ }
705
+ return d;
706
+ });
707
+ }
708
+ return deps;
709
+ }
710
+ setDeps(deps) {
711
+ setObjAttr(this.getSource(true), 'deps', deps);
712
+ this.refreshExtends();
713
+ }
714
+
715
+ /**
716
+ * @return {String} a URI indicating the datatype, for example: "http://www.w3.org/2001/XMLSchema.xsd#date".
717
+ */
718
+ getDatatype(original) {
719
+ const dt = this.getSource(original).datatype;
720
+ if (dt != null && dt !== '') {
721
+ return Array.isArray(dt) ? dt.map(d => rdfjson_namespaceObject.namespaces.expand(d)) : rdfjson_namespaceObject.namespaces.expand(dt);
722
+ }
723
+ return dt;
724
+ }
725
+ setDatatype(dt) {
726
+ setObjAttr(this.getSource(true), 'datatype', dt);
727
+ this.refreshExtends();
728
+ }
729
+ getPattern(original) {
730
+ return this.getSource(original).pattern;
731
+ }
732
+ setPattern(pattern) {
733
+ setObjAttr(this.getSource(true), 'pattern', pattern);
734
+ this.refreshExtends();
735
+ }
736
+
737
+ /**
738
+ * @return {String} a two character language code, only relevant if the item type is Text and the nodetype is
739
+ * a LANGUAGE_LITERAL, indicating that all matching bindings should be set with this language.
740
+ */
741
+ getLanguage(original) {
742
+ return this.getSource(original).language;
743
+ }
744
+ setLanguage(lang) {
745
+ setObjAttr(this.getSource(true), 'language', lang);
746
+ this.refreshExtends();
747
+ }
748
+ getMember(original) {
749
+ return this.getSource(original).member;
750
+ }
751
+ setMember(member) {
752
+ setObjAttr(this.getSource(true), 'member', member);
753
+ this.refreshExtends();
754
+ }
755
+
756
+ /**
757
+ * Allowed values are:
758
+ * LITERAL, RESOURCE, URI, BLANK, PLAIN_LITERAL, ONLY_LITERAL, LANGUAGE_LITERAL, DATATYPE_LITERAL
759
+ */
760
+ getNodetype(original) {
761
+ const s = this.getSource(original);
762
+ return s.nodetype || s.nodeType; // Ugly fix because it is often wrong written in SIRFF.
763
+ }
764
+ setNodetype(nt) {
765
+ setObjAttr(this.getSource(true), 'nodetype', nt);
766
+ this.refreshExtends();
767
+ }
768
+ getValue(original) {
769
+ return this.getSource(original).value;
770
+ }
771
+ setValue(value) {
772
+ setObjAttr(this.getSource(true), 'value', value);
773
+ this.refreshExtends();
774
+ }
775
+ getValueTemplate(original) {
776
+ return this.getSource(original).valueTemplate;
777
+ }
778
+ setValueTemplate(valueTemplate) {
779
+ setObjAttr(this.getSource(true), 'valueTemplate', valueTemplate);
780
+ this.refreshExtends();
781
+ }
782
+
783
+ /**
784
+ * @return {Object} containing max, min, and preferred properties.
785
+ */
786
+ getCardinality(original) {
787
+ if (!this.getProperty() && this.getType() === 'text') {
788
+ return {
789
+ min: 1,
790
+ max: 1
791
+ };
792
+ }
793
+ const source = this.getSource(original);
794
+ if (source && 'cardinality' in source) {
795
+ return source.cardinality;
796
+ }
797
+ if (!this.getProperty()) {
798
+ return {
799
+ max: 1
800
+ };
801
+ }
802
+ return {};
803
+ }
804
+ setCardinality(card) {
805
+ setObjAttr(this.getSource(true), 'cardinality', card);
806
+ this.refreshExtends();
807
+ }
808
+ isEnabled(original) {
809
+ const s = this.getSource(original);
810
+ return s.enabled == null ? true : s.enabled;
811
+ }
812
+ setEnabled(en) {
813
+ const s = this.getSource(true);
814
+ if (en) {
815
+ delete s.enabled;
816
+ } else {
817
+ s.enabled = en;
818
+ }
819
+ this.refreshExtends();
820
+ }
821
+
822
+ /**
823
+ * Classes are exposed in CSS, allows external stylesheets to act on the form.
824
+ * @returns {Array}
825
+ */
826
+ getClasses(original) {
827
+ const source = this.getSource(original);
828
+ if (source && 'cls' in source) {
829
+ return source.cls;
830
+ }
831
+ return [];
832
+ }
833
+ setClasses(arr) {
834
+ setObjAttr(this.getSource(true), 'cls', arr);
835
+ this.refreshExtends();
836
+ }
837
+
838
+ /**
839
+ * @deprecated only provided for backward compatability, use styles, classes
840
+ * @param cls
841
+ * @returns {boolean}
842
+ */
843
+ hasClass(cls, original) {
844
+ const s = this.getSource(original);
845
+ if (this.hasStyle(cls, original)) {
846
+ return true;
847
+ }
848
+ if (s.cls == null) {
849
+ return false;
850
+ }
851
+ return s.cls.some(c => c.toLowerCase() === cls.toLowerCase());
852
+ }
853
+
854
+ /**
855
+ * The available styles, see the _styles variable.
856
+ *
857
+ * @return {Array} that contains strings with all available styles.
858
+ */
859
+ getAvailableStyles() {
860
+ return this._styles;
861
+ }
862
+
863
+ /**
864
+ * @return {Array} that contains strings with the style, if no style is defined an empty array is returned
865
+ */
866
+ getStyles(original) {
867
+ return this.getSource(original).styles || [];
868
+ }
869
+ setStyles(arr) {
870
+ setObjAttr(this.getSource(true), 'styles', arr);
871
+ this.refreshExtends();
872
+ }
873
+ hasStyle(sty, original) {
874
+ const source = this.getSource(original);
875
+ if (!source || !('styles' in source)) {
876
+ return false;
877
+ }
878
+ return source.styles.some(s => s.toLowerCase() === sty.toLowerCase());
879
+ }
880
+ getSource(original, attribute) {
881
+ if (original === true) {
882
+ // Get the original source
883
+ return this._source._extendedSource || this._source;
884
+ } else if (original === false) {
885
+ // Get the extended source
886
+ const entryItem = this._itemStore.getItem(this.getExtends());
887
+ if (entryItem == null) {
888
+ return this._source;
889
+ }
890
+ return entryItem.getSource();
891
+ } // Get the merged source.
892
+ return this._source;
893
+ }
894
+ getBundle() {
895
+ return this._bundle;
896
+ }
897
+ toStringShort() {
898
+ return `'${this.getLabel()}'${this.getId() ? ` (ID: '${this.getId()}')` : ''}`;
899
+ }
900
+ toString() {
901
+ const detailsArr = [];
902
+ if (this.getId()) {
903
+ detailsArr.push(`ID: '${this.getId()}'`);
904
+ }
905
+ detailsArr.push(`TYPE: '${this.getType()}'`);
906
+ if (this.getProperty()) {
907
+ detailsArr.push(`PROPERTY: '${this.getProperty()}'`);
908
+ }
909
+ if (this.getExtends()) {
910
+ detailsArr.push(`EXTENDS: '${this.getExtends()}'`);
911
+ }
912
+ return `'${this.getLabel()}' (${detailsArr.join(', ')})`;
913
+ }
914
+ getHash() {
915
+ return `i_${this._internalId}`;
916
+ }
917
+
918
+ //= ==================================================
919
+ // Inherited methods
920
+ //= ==================================================
921
+
922
+ //= ==================================================
923
+ // Private methods
924
+ //= ==================================================
925
+
926
+ // eslint-disable-next-line class-methods-use-this
927
+ _setLangHash(hash, value, lang) {
928
+ const _hash = hash || {};
929
+ if (typeof value === 'string') {
930
+ if (typeof lang === 'string') {
931
+ _hash[lang] = value;
932
+ } else {
933
+ _hash[''] = value;
934
+ }
935
+ } else if (typeof value === 'object' && value !== null) {
936
+ return value;
937
+ }
938
+ return _hash;
939
+ }
940
+ }
941
+ ;// ./src/template/Group.js
942
+
943
+ const sortItems = items => {
944
+ items.forEach(item => {
945
+ item.__label = (item.getLabel() || '').toLowerCase();
946
+ });
947
+ items.sort((o1, o2) => {
948
+ if (o1._source.priority != null) {
949
+ if (o2._source.priority != null) {
950
+ // eslint-disable-next-line no-nested-ternary
951
+ return o1._source.priority > o2._source.priority ? -1 : o1._source.priority < o2._source.priority ? 1 : 0;
952
+ }
953
+ return o1._source.priority > 0 ? -1 : 1;
954
+ } else if (o2._source.priority != null) {
955
+ return o2._source.priority > 0 ? 1 : -1;
956
+ } else if (o1.__label > o2.__label) {
957
+ return 1;
958
+ } else if (o1.__label < o2.__label) {
959
+ return -1;
960
+ }
961
+ return 0;
962
+ });
963
+ };
964
+ class Group extends Item {
965
+ /**
966
+ * Group extends an Item by having children.
967
+ */
968
+ constructor(params) {
969
+ super(params);
970
+ this._children = params.children;
971
+ if (this._source.content != null) {
972
+ this._source.items = this._source.content;
973
+ delete this._source.content;
974
+ }
975
+ this._forceChildrenClones = false;
976
+ this._parent = null;
977
+ }
978
+
979
+ // ===================================================
980
+ // Public API
981
+ // ===================================================
982
+ getChildren(original) {
983
+ const _original = original && this.isExtention();
984
+ let children = _original ? this._ochildren : this._children;
985
+ if (children == null) {
986
+ children = this._itemStore.getChildren(this, _original);
987
+ if (this.getSource().automatic === true && this._itemStore.automaticSortAllowed) {
988
+ sortItems(children);
989
+ }
990
+ this[`_${_original ? 'o' : ''}children`] = children;
991
+ }
992
+ return children;
993
+ }
994
+ originalChildrenChanged() {
995
+ if (this.isExtention()) {
996
+ delete this._children;
997
+ }
998
+ }
999
+ setExtends(extendsStr) {
1000
+ super.setExtends(extendsStr);
1001
+ delete this._children;
1002
+ delete this._ochildren;
1003
+ }
1004
+
1005
+ // ===================================================
1006
+ // Inherited methods
1007
+ // ===================================================
1008
+
1009
+ getNodetype() {
1010
+ return super.getNodetype() || 'RESOURCE'; // Ugly fix because it is often wrong written in SIRFF.
1011
+ }
1012
+ }
1013
+ ;// ./src/template/PropertyGroup.js
1014
+
1015
+ class PropertyGroup extends Group {
1016
+ /**
1017
+ * A PropertyGroup captures the special case when both the predicate and object of a
1018
+ * tripple should be changable. This is achieved by having a PropertyGroup where the
1019
+ * first child is a Choice item corresponding to the predicate and the second being
1020
+ * an item corresponding to the object in the triple. The second item can be either a
1021
+ * Text, Choice or Group item depending on the kind of object envisioned in the triple.
1022
+ */
1023
+ getChildren(original) {
1024
+ if (this._delegatedChildren == null) {
1025
+ const getCardinality = () => ({
1026
+ min: 1,
1027
+ max: 1,
1028
+ pref: 1
1029
+ });
1030
+ const children = super.getChildren(original) || [];
1031
+ this._delegatedChildren = children.map(child => {
1032
+ const delegate = Object.create(child);
1033
+ delegate.getCardinality = getCardinality;
1034
+ return delegate;
1035
+ });
1036
+ }
1037
+ return this._delegatedChildren;
1038
+ }
1039
+ getPropertyItem() {
1040
+ return this.getChildren()[0];
1041
+ }
1042
+ getObjectItem() {
1043
+ return this.getChildren()[1];
1044
+ }
1045
+ }
1046
+ ;// ./src/template/Text.js
1047
+
1048
+
1049
+ /**
1050
+ * Same functionality as an Item, but separate class to make switching on type possible.
1051
+ */
1052
+ class Text extends Item {
1053
+ getLabelProperties(original) {
1054
+ return this.getSource(original).labelProperties;
1055
+ }
1056
+ }
1057
+ ;// ./src/template/Choice.js
1058
+
1059
+
1060
+ const expandValues = choices => {
1061
+ if (choices == null) {
1062
+ return;
1063
+ }
1064
+ choices.forEach(c => {
1065
+ c.value = rdfjson_namespaceObject.namespaces.expand(c.value);
1066
+ });
1067
+ };
1068
+ class Choice extends Item {
1069
+ /**
1070
+ * A choice item type indicates that the value should be one of a range of predefined choices,
1071
+ * these predefined choices can be defined manually in the template or extracted from an external
1072
+ * ontology (indicated by the ontologyUrl) by means of a query that can be constructed from the constraints.
1073
+ *
1074
+ * TODO:
1075
+ * The choices can also be organized into a hierarchy using the parent and hierarchy properties.
1076
+ */
1077
+ constructor(params) {
1078
+ super(params);
1079
+ this._ontologyStore = params.ontologyStore;
1080
+ }
1081
+
1082
+ // ===================================================
1083
+ // Public API
1084
+ // ===================================================
1085
+ /**
1086
+ * A choice is an object which looks like:
1087
+ * {"value": "http://example.com/choice1",
1088
+ * "label": {"en": "First choice", "sv": "Första valet"}
1089
+ * }
1090
+ *
1091
+ * @return {Array} of choices.
1092
+ */
1093
+ getChoices(original) {
1094
+ return this.getStaticChoices(original) || this.getDynamicChoices(original) || [];
1095
+ }
1096
+
1097
+ /**
1098
+ * @return {Boolean} true if there is an ontology or static choices.
1099
+ */
1100
+ hasChoices(original) {
1101
+ const s = this.getSource(original);
1102
+ return s.ontologyUrl != null || s.choices != null;
1103
+ }
1104
+ hasStaticChoices(original) {
1105
+ const s = this.getSource(original);
1106
+ return s.choices != null;
1107
+ }
1108
+
1109
+ /**
1110
+ * @return {Array} of choices defined manually in the Template.
1111
+ */
1112
+ getStaticChoices(original) {
1113
+ const s = this.getSource(original);
1114
+ if (s.choices) {
1115
+ const isURI = this.getNodetype().indexOf('LITERAL') === -1;
1116
+ if (original && this.isExtention()) {
1117
+ if (!this._origStaticIsSorted) {
1118
+ if (isURI) {
1119
+ expandValues(s.choices);
1120
+ }
1121
+ this._origStaticIsSorted = true;
1122
+ }
1123
+ } else if (!this._staticIsSorted) {
1124
+ if (isURI) expandValues(s.choices);
1125
+ this._staticIsSorted = true;
1126
+ }
1127
+ }
1128
+ return s.choices;
1129
+ }
1130
+ setStaticChoices(choices) {
1131
+ const s = this.getSource(true);
1132
+ if (s.choices === choices) {
1133
+ return;
1134
+ }
1135
+ if (choices != null) {
1136
+ this._origStaticIsSorted = true;
1137
+ }
1138
+ s.choices = choices;
1139
+ this.refreshExtends();
1140
+ }
1141
+ setExtends(extendsStr) {
1142
+ super.setExtends(extendsStr);
1143
+ delete this._staticIsSorted;
1144
+ delete this._origStaticIsSorted;
1145
+ }
1146
+
1147
+ /**
1148
+ * Fetches choices from an external ontology.
1149
+ *
1150
+ * @param {Object} callback will be called asynchronously, if undefined the call is made synchronously.
1151
+ * @return {Array} of choice objects, only provided if method called without callback.
1152
+ */
1153
+ getDynamicChoices(callback) {
1154
+ if (this._dynamicChoices == null) {
1155
+ if (callback == null) {
1156
+ this._dynamicChoices = this._ontologyStore.getChoices(this);
1157
+ return this._dynamicChoices;
1158
+ }
1159
+ this._ontologyStore.getChoices(this, choices => {
1160
+ this._dynamicChoices = choices;
1161
+ if (this._dynamicChoices == null) {
1162
+ console.log(`Failed lookup of choices for ${this.getLabel()}`);
1163
+ console.log(`OntologyUrl is: ${this._source.ontologyUrl}`);
1164
+ }
1165
+ callback(this._dynamicChoices);
1166
+ });
1167
+ } else {
1168
+ if (callback == null) {
1169
+ return this._dynamicChoices;
1170
+ }
1171
+ callback(this._dynamicChoices);
1172
+ }
1173
+ return undefined;
1174
+ }
1175
+ getOntologyUrl(original) {
1176
+ const ou = this.getSource(original).ontologyUrl;
1177
+ if (ou != null && ou !== '') {
1178
+ return rdfjson_namespaceObject.namespaces.expand(ou);
1179
+ }
1180
+ return ou;
1181
+ }
1182
+ setOntologyUrl(url) {
1183
+ const s = this.getSource(true);
1184
+ if (url == null || url === '') {
1185
+ delete s.ontologyUrl;
1186
+ } else {
1187
+ s.ontologyUrl = url;
1188
+ }
1189
+ this.refreshExtends();
1190
+ }
1191
+ getLabelProperties(original) {
1192
+ return this.getSource(original).labelProperties || ['http://www.w3.org/2000/01/rdf-schema#label'];
1193
+ }
1194
+ getParentProperty(original) {
1195
+ const pp = this.getSource(original).parentProperty;
1196
+ if (pp != null && pp !== '') {
1197
+ return rdfjson_namespaceObject.namespaces.expand(pp);
1198
+ }
1199
+ return pp;
1200
+ }
1201
+ setParentProperty(prop) {
1202
+ const s = this.getSource(true);
1203
+ if (prop == null || prop === '') {
1204
+ delete s.parentProperty;
1205
+ } else {
1206
+ s.parentProperty = prop;
1207
+ }
1208
+ this.refreshExtends();
1209
+ }
1210
+ getHierarchyProperty(original) {
1211
+ const hp = this.getSource(original).hierarchyProperty;
1212
+ if (hp != null && hp !== '') {
1213
+ return rdfjson_namespaceObject.namespaces.expand(hp);
1214
+ }
1215
+ return hp;
1216
+ }
1217
+ setHierarchyProperty(prop) {
1218
+ const s = this.getSource(true);
1219
+ if (prop == null || prop === '') {
1220
+ delete s.hierarchyProperty;
1221
+ } else {
1222
+ s.hierarchyProperty = prop;
1223
+ }
1224
+ this.refreshExtends();
1225
+ }
1226
+ isParentPropertyInverted(original) {
1227
+ return this.getSource(original).isParentPropertyInverted === true;
1228
+ }
1229
+ setParentPropertyInverted(inverted) {
1230
+ const s = this.getSource(true);
1231
+ if (inverted === true) {
1232
+ s.isParentPropertyInverted = true;
1233
+ } else {
1234
+ delete s.isParentPropertyInverted;
1235
+ }
1236
+ this.refreshExtends();
1237
+ }
1238
+ isHierarchyPropertyInverted(original) {
1239
+ return this.getSource(original).isHierarchyPropertyInverted === true;
1240
+ }
1241
+ setHierarchyPropertyInverted(inverted) {
1242
+ const s = this.getSource(true);
1243
+ if (inverted) {
1244
+ s.isHierarchyPropertyInverted = true;
1245
+ } else {
1246
+ delete s.isHierarchyPropertyInverted;
1247
+ }
1248
+ this.refreshExtends();
1249
+ }
1250
+ }
1251
+ ;// ./src/template/OntologyStore.js
1252
+
1253
+ class OntologyStore {
1254
+ /**
1255
+ * Simple store of ontologies to allow reuse across templates and items.
1256
+ */
1257
+ constructor() {
1258
+ this._registry = {};
1259
+ }
1260
+
1261
+ //= ==================================================
1262
+ // Public API
1263
+ //= ==================================================
1264
+ importRegistry(registry) {
1265
+ Object.assign(this._registry, registry);
1266
+ }
1267
+ getChoices(choiceItem, callback) {
1268
+ const choices = this._findChoices(choiceItem);
1269
+ if (choices == null) {
1270
+ // TODO load via xhr and deferred.
1271
+ } else {
1272
+ if (callback == null) {
1273
+ return choices;
1274
+ }
1275
+ callback(choices);
1276
+ }
1277
+ return undefined;
1278
+ }
1279
+
1280
+ //= ==================================================
1281
+ // Private methods
1282
+ //= ==================================================
1283
+ _findChoices(item) {
1284
+ const ontologyChoiceArr = this._registry[item.getOntologyUrl()];
1285
+ if (ontologyChoiceArr != null) {
1286
+ for (let ind = 0; ind < ontologyChoiceArr.length; ind++) {
1287
+ const obj = ontologyChoiceArr[ind];
1288
+ if ((0,external_lodash_namespaceObject.isEqual)(obj.constraints, item.getConstraints()) && item.getParentProperty() === obj.parentProperty && item.getHierarchyProperty() === obj.hierarchyProperty && item.isParentPropertyInverted() === (obj.isParentPropertyInverted || false) && item.isHierarchyPropertyInverted() === (obj.isHierarchyPropertyInverted || false)) {
1289
+ return obj.choices;
1290
+ }
1291
+ }
1292
+ }
1293
+ return undefined;
1294
+ }
1295
+
1296
+ // eslint-disable-next-line class-methods-use-this
1297
+ _constructLoadUrl(choiceItem) {
1298
+ const params = [];
1299
+ params.push(`constr=${encodeURIComponent(JSON.stringify(choiceItem.getConstraints()))}`);
1300
+ if (choiceItem.getParentProperty() != null) {
1301
+ const pp = choiceItem.isParentPropertyInverted() === true ? 'ipp=' : 'pp=';
1302
+ params.push(pp + encodeURIComponent(choiceItem.getParentProperty()));
1303
+ }
1304
+ if (choiceItem.getHierarchyProperty() != null) {
1305
+ const hp = choiceItem.isHierarchyPropertyInverted() === true ? 'ihp=' : 'hp=';
1306
+ params.push(hp + encodeURIComponent(choiceItem.getHierarchyProperty()));
1307
+ }
1308
+ return `${choiceItem.getOntologyUrl()}?${params.join('&')}`;
1309
+ }
1310
+ }
1311
+ ;// ./src/template/Bundle.js
1312
+ let counter = 0;
1313
+ class Bundle {
1314
+ /**
1315
+ * A Bundle corresponds to a set of items typically managed in a single file.
1316
+ */
1317
+ constructor({
1318
+ itemStore,
1319
+ source,
1320
+ path,
1321
+ readOnly = false
1322
+ }) {
1323
+ this._itemStore = itemStore;
1324
+ this._source = source;
1325
+ this._path = path;
1326
+ this._readOnly = readOnly;
1327
+ this._items = [];
1328
+ counter += 1;
1329
+ this._id = `_bundle_${counter}`;
1330
+ this._root = null;
1331
+ this._modified = false;
1332
+ }
1333
+ getInternalId() {
1334
+ return this._id;
1335
+ }
1336
+ getSource() {
1337
+ return this._source;
1338
+ }
1339
+ setRoot(itemId) {
1340
+ this._source.root = itemId;
1341
+ return itemId;
1342
+ }
1343
+ getRoot() {
1344
+ if (this._source.root) {
1345
+ return this._itemStore.getItem(this._source.root);
1346
+ }
1347
+ return undefined;
1348
+ }
1349
+ getItemStore() {
1350
+ return this._itemStore;
1351
+ }
1352
+ getPath() {
1353
+ return this._path;
1354
+ }
1355
+ getItems() {
1356
+ return this._items;
1357
+ }
1358
+ addItem(item) {
1359
+ this._items.push(item);
1360
+ }
1361
+ removeItem(item) {
1362
+ this._items.splice(this._items.indexOf(item), 1);
1363
+ }
1364
+ isModified() {
1365
+ return this._modified;
1366
+ }
1367
+ setModified(modified) {
1368
+ this._modified = modified;
1369
+ }
1370
+ isReadOnly() {
1371
+ return this._readOnly || this._path == null;
1372
+ }
1373
+ }
1374
+ ;// ./src/model/Binding.js
1375
+ /* eslint-disable class-methods-use-this */
1376
+
1377
+ let Binding_counter = 0;
1378
+
1379
+ /**
1380
+ * A binding is a pairing between an item and various RDF statement
1381
+ * (a single statement unless the binding is a group with constraints).
1382
+ * It keeps track of cardinality and validity.
1383
+ * If a binding is valid and all parent bindings are valid,
1384
+ * the statement is asserted, that is, inserted into the RDF graph.
1385
+ */
1386
+ class Binding {
1387
+ /**
1388
+ * @exports {Binding}
1389
+ * @class
1390
+ */
1391
+ constructor({
1392
+ item,
1393
+ statement,
1394
+ graph,
1395
+ matchingCode
1396
+ }) {
1397
+ this._item = item;
1398
+ this._statement = statement;
1399
+ this._graph = graph;
1400
+ this._ancestorValid = true;
1401
+ this._cardinalityTracker = null;
1402
+ this._hash = `b_${Binding_counter}`;
1403
+ this._matchingCode = matchingCode || 'correct';
1404
+ Binding_counter += 1;
1405
+ }
1406
+
1407
+ // ===================================================
1408
+ // Public API
1409
+ // ===================================================
1410
+ getGraph() {
1411
+ if (!this._graph) {
1412
+ if (this._statement) {
1413
+ this._graph = this._statement.getGraph();
1414
+ } else if (this._parent) {
1415
+ this._graph = this._parent.getGraph();
1416
+ }
1417
+ }
1418
+ return this._graph;
1419
+ }
1420
+ isReadOnly() {
1421
+ if (!('_readOnly' in this)) {
1422
+ const parent = this.getParent();
1423
+ if (parent && parent._readOnly) {
1424
+ this._readyOnly = true;
1425
+ } else {
1426
+ this._readOnly = !!(this._statement && this._statement.getNamedGraph());
1427
+ }
1428
+ }
1429
+ return this._readOnly;
1430
+ }
1431
+ remove() {}
1432
+ setSubject(uri) {}
1433
+ getValue() {}
1434
+ setValue(value, silent) {}
1435
+ getGist() {
1436
+ return utils.extractGist(this.getValue(), this.getItem().getValueTemplate());
1437
+ }
1438
+ setGist(value, silent) {
1439
+ let _value = value;
1440
+ let vt = this.getItem().getValueTemplate();
1441
+ if (vt && _value.length > 0) {
1442
+ if (vt.indexOf('$1') === -1) {
1443
+ vt += '$1';
1444
+ }
1445
+ _value = vt.replace('$1', _value);
1446
+ }
1447
+ this.setValue(_value, silent);
1448
+ }
1449
+ getCardinalityTracker() {
1450
+ return this._cardinalityTracker;
1451
+ }
1452
+ setCardinalityTracker(cardTracker) {
1453
+ this._cardinalityTracker = cardTracker;
1454
+ }
1455
+ getItem() {
1456
+ return this._item;
1457
+ }
1458
+ getStatement() {
1459
+ return this._statement;
1460
+ }
1461
+ getParent() {
1462
+ return this._parent;
1463
+ }
1464
+ setParent(parent) {
1465
+ this._parent = parent;
1466
+ }
1467
+
1468
+ /**
1469
+ * A binding is valid if:
1470
+ * <ol><li> it is a leaf and the corresponding statement is valid, </li>
1471
+ * <li>if it is a group and at least one of its children is valid, or</li>
1472
+ * <li>if it is a predicategroup and both the predicate and the object binding are valid.</ol>
1473
+ */
1474
+ isValid() {}
1475
+ getMatchingCode() {
1476
+ return this._matchingCode;
1477
+ }
1478
+ setMatchingCode(matchingCode) {
1479
+ this._matchingCode = matchingCode;
1480
+ }
1481
+
1482
+ /**
1483
+ * stores the validity of ancestors.
1484
+ */
1485
+ setAncestorValid(valid) {
1486
+ this._ancestorValid = valid;
1487
+ this.updateAssertions();
1488
+ }
1489
+
1490
+ /**
1491
+ *
1492
+ */
1493
+ updateAssertions() {}
1494
+ getHash() {
1495
+ return this._hash;
1496
+ }
1497
+ addListener(listener) {
1498
+ if (!this._listeners) {
1499
+ this._listeners = [];
1500
+ }
1501
+ this._listeners.push(listener);
1502
+ }
1503
+ removeListener(listener) {
1504
+ if (this._listeners) {
1505
+ const idx = this._listeners.indexOf(listener);
1506
+ if (idx !== -1) {
1507
+ this._listeners.splice(idx, 1);
1508
+ }
1509
+ }
1510
+ }
1511
+ bindingChange(binding) {
1512
+ if (this._listeners) {
1513
+ for (let i = 0; i < this._listeners.length; i++) {
1514
+ this._listeners[i](binding);
1515
+ }
1516
+ }
1517
+ if (this._parent) {
1518
+ this._parent.bindingChange(binding);
1519
+ }
1520
+ if (this._cardinalityTracker) {
1521
+ this._cardinalityTracker.touch();
1522
+ }
1523
+ }
1524
+
1525
+ // ===================================================
1526
+ // Private methods
1527
+ // ===================================================
1528
+ _isValidObjectValue(value) {
1529
+ let _value = value;
1530
+ if (typeof _value !== 'string' && _value !== null) {
1531
+ throw new Error('In a binding every object value need to be a string!');
1532
+ }
1533
+ const pattern = this._item.getPattern();
1534
+ if (pattern) {
1535
+ _value = utils.extractGist(_value, this.getItem().getValueTemplate());
1536
+ return _value !== undefined && _value !== null && _value !== '' && new RegExp(`^${pattern}$`).test(_value);
1537
+ }
1538
+ return _value !== undefined && _value !== null && _value !== '';
1539
+ }
1540
+ _isValidPredicateValue(value) {
1541
+ if (typeof value !== 'string' && value !== null) {
1542
+ throw new Error('In a binding every predicate need to be a string!');
1543
+ }
1544
+ return value !== undefined && value !== null && value !== '';
1545
+ }
1546
+ }
1547
+ ;// ./src/model/ValueBinding.js
1548
+
1549
+
1550
+
1551
+ class ValueBinding extends Binding {
1552
+ /**
1553
+ * Corresponds to a binding for a Text item type, captures literals, literals with
1554
+ * language, datatyped literals, or non blank resources, that is, URI's.
1555
+ * Validity is determined by a valid predicate and object.
1556
+ * The statement is asserted when the parents are valid and this ValueBinding is valid.
1557
+ * @exports {rdforms/model/ValueBinding}
1558
+ * @class
1559
+ * @see rforms/template/Text
1560
+ */
1561
+ constructor(params) {
1562
+ super(params);
1563
+ this._validObject = true;
1564
+ this._validPredicate = true;
1565
+ this._excludeFromTreeValidityCheck = false;
1566
+ if (this._statement) {
1567
+ this._validPredicate = this._isValidPredicateValue(this._statement.getPredicate(), true);
1568
+ this._validObject = this._isValidObjectValue(this._statement.getValue());
1569
+ }
1570
+ }
1571
+
1572
+ /**
1573
+ * @return {String} corresponding to the value, even if the nodetype is URI
1574
+ * or datatype says for example date.
1575
+ */
1576
+ getValue() {
1577
+ return this._statement.getValue();
1578
+ }
1579
+
1580
+ /**
1581
+ * @param {String} value the value to set, the value will be trimmed and if the value is empty
1582
+ * (like the empty string or null) the statement will be unasserted.
1583
+ * @param {Boolean} silent if true the graph will not be marked as changed,
1584
+ * useful when initializing with default empty values.
1585
+ */
1586
+ setValue(value, silent) {
1587
+ const _value = typeof value === 'string' ? value.trim() : value;
1588
+ const oValidObject = this._validObject;
1589
+ if (this._isValidObjectValue(_value)) {
1590
+ this._statement.setValue(_value, silent);
1591
+ this._validObject = true;
1592
+ if (oValidObject !== true && this._validPredicate === true && !this._excludeFromTreeValidityCheck) {
1593
+ this._parent.oneChildValidityChanged(true);
1594
+ }
1595
+ } else {
1596
+ // If it is a null value, change the statement.
1597
+ if (_value === '' || _value === null) {
1598
+ this._statement.setValue('', silent);
1599
+ }
1600
+ // And unassert the statement.
1601
+ this._validObject = false;
1602
+ if (oValidObject !== false && this._validPredicate === true) {
1603
+ this._parent.oneChildValidityChanged(false);
1604
+ }
1605
+ }
1606
+ this.updateAssertions();
1607
+ }
1608
+ setSubject(uri) {
1609
+ this._statement.setSubject(uri);
1610
+ }
1611
+
1612
+ /**
1613
+ * @return {String} corresponding to a uri.
1614
+ */
1615
+ getPredicate() {
1616
+ return this._statement.getPredicate();
1617
+ }
1618
+
1619
+ /**
1620
+ * @param {String} predicate corresponding to a uri.
1621
+ */
1622
+ setPredicate(predicate) {
1623
+ const oValidPredicate = this._validPredicate;
1624
+ if (this._isValidPredicateValue(predicate)) {
1625
+ this._statement.setPredicate(predicate);
1626
+ this._validPredicate = true;
1627
+ if (oValidPredicate !== true && this._validObject === true) {
1628
+ this._parent.oneChildValidityChanged(true);
1629
+ }
1630
+ } else {
1631
+ // Note that we actually do not set the invalid value, just unassert the statement.
1632
+ this._validPredicate = false;
1633
+ if (oValidPredicate !== false && this._validObject === true) {
1634
+ this._parent.oneChildValidityChanged(false);
1635
+ }
1636
+ }
1637
+ this.updateAssertions();
1638
+ }
1639
+
1640
+ /**
1641
+ * @return {String} a two or three character language code.
1642
+ */
1643
+ getLanguage() {
1644
+ return this._statement.getLanguage();
1645
+ }
1646
+
1647
+ /**
1648
+ * @param {Object} lang a two or three character language code.
1649
+ */
1650
+ setLanguage(lang, silent) {
1651
+ this._statement.setLanguage(lang, silent);
1652
+ if (!silent) {
1653
+ this.bindingChange(this);
1654
+ }
1655
+ }
1656
+
1657
+ /**
1658
+ * @return {String} corresponding to a uri.
1659
+ */
1660
+ getDatatype() {
1661
+ return this._statement.getDatatype();
1662
+ }
1663
+
1664
+ /**
1665
+ * @param {String} dt corresponding to a uri.
1666
+ */
1667
+ setDatatype(dt) {
1668
+ this._statement.setDatatype(rdfjson_namespaceObject.namespaces.expand(dt));
1669
+ this.updateAssertions();
1670
+ }
1671
+ setExcludeFromTreeValidityCheck(value) {
1672
+ this._excludeFromTreeValidityCheck = value;
1673
+ }
1674
+ remove() {
1675
+ this.setValue(null);
1676
+ this._parent.removeChildBinding(this);
1677
+ super.remove(arguments);
1678
+ }
1679
+ updateAssertions() {
1680
+ const assert = this._ancestorValid && this._validObject && this._validPredicate;
1681
+ this._statement.setAsserted(assert, true);
1682
+ this.bindingChange(this);
1683
+ }
1684
+ isValid() {
1685
+ return this._validObject && this._validPredicate && !this._excludeFromTreeValidityCheck;
1686
+ }
1687
+ }
1688
+ ;// ./src/model/CODES.js
1689
+ /* harmony default export */ const model_CODES = ({
1690
+ UNKNOWN: 'unknown',
1691
+ // Used as a marker initially, to not mark everything red initially
1692
+ OK: 'correct',
1693
+ TOO_FEW_VALUES: 'few',
1694
+ // deprecated
1695
+ TOO_FEW_VALUES_MIN: 'min',
1696
+ TOO_FEW_VALUES_PREF: 'pref',
1697
+ TOO_MANY_VALUES: 'many',
1698
+ AT_MOST_ONE_CHILD: 'atmostonechild',
1699
+ AT_LEAST_ONE_CHILD: 'atleastonechild',
1700
+ EXACTLY_ONE_CHILD: 'exactlyonechild',
1701
+ WRONG_VALUE: 'value',
1702
+ WRONG_PATTERN: 'pattern',
1703
+ WRONG_NODETYPE: 'nodetype',
1704
+ WRONG_DATATYPE: 'datatype',
1705
+ MISSING_LANGUAGE: 'language',
1706
+ MISSING_CONSTRAINTS: 'constraints'
1707
+ });
1708
+ ;// ./src/model/ChoiceBinding.js
1709
+
1710
+
1711
+ const label = 'http://www.w3.org/2000/01/rdf-schema#label';
1712
+ const seeAlso = 'http://www.w3.org/2000/01/rdf-schema#seeAlso';
1713
+
1714
+ /**
1715
+ * A ValueBinding that only accepts uris from a controlled vocabulary encoded as choices.
1716
+ * @see rforms.template.Choice#getChoices
1717
+ */
1718
+ class ChoiceBinding extends ValueBinding {
1719
+ constructor({
1720
+ choice,
1721
+ item,
1722
+ statement,
1723
+ matchingCode
1724
+ }) {
1725
+ super({
1726
+ choice,
1727
+ item,
1728
+ statement,
1729
+ matchingCode
1730
+ });
1731
+ this._choice = choice;
1732
+ this._validPredicate = item.getProperty() != null;
1733
+ }
1734
+
1735
+ // Static property on class ChoiceBinding via static getter,
1736
+ static get seeAlso() {
1737
+ return seeAlso;
1738
+ }
1739
+ setChoice(choice, silent) {
1740
+ this._choice = choice;
1741
+ if (choice == null) {
1742
+ this.setValue(null, null, silent);
1743
+ } else if (this.getValue() !== choice.value) {
1744
+ this.setValue(choice.value, choice, silent);
1745
+ }
1746
+ if (choice && choice.mismatch) {
1747
+ this.setMatchingCode(model_CODES.WRONG_VALUE);
1748
+ } else if ((!choice || !choice.mismatch) && this.getMatchingCode() === model_CODES.WRONG_VALUE) {
1749
+ this.setMatchingCode(model_CODES.OK);
1750
+ }
1751
+ }
1752
+ getChoice() {
1753
+ return this._choice;
1754
+ }
1755
+ setValue(value, choice, silent) {
1756
+ const oldval = this.getValue();
1757
+ super.setValue(value, choice, silent);
1758
+ const graph = this._statement.getGraph();
1759
+ graph.findAndRemove(oldval, label, undefined, silent);
1760
+ graph.findAndRemove(oldval, seeAlso, undefined, silent);
1761
+ if (value != null && choice != null) {
1762
+ if (choice.seeAlso && choice.inlineSeeAlso) {
1763
+ graph.create(value, seeAlso, choice.seeAlso, true, silent);
1764
+ }
1765
+ if (choice.inlineLabel === true) {
1766
+ const labelMap = choice.label || {};
1767
+ Object.keys(labelMap).forEach(lang => graph.create(value, label, {
1768
+ value: labelMap[lang],
1769
+ lang,
1770
+ type: 'literal'
1771
+ }, true, silent));
1772
+ }
1773
+ }
1774
+ }
1775
+ }
1776
+ ;// ./src/model/CardinalityTracker.js
1777
+
1778
+ class CardinalityTracker {
1779
+ /**
1780
+ * A counter paired with a item with cardinality restrictions.
1781
+ * To update the counter use the increment and decrement methods.
1782
+ * If the counter passes a cardinality restriction the
1783
+ * corresponding hook is called, that is, maxReached, minReached,
1784
+ * and justFine when the counter moved within the acceptable
1785
+ * cardinality restrictions.
1786
+ */
1787
+ constructor(item) {
1788
+ this._listener = [];
1789
+ this._limits = item.getCardinality() || {};
1790
+ this._counter = 0;
1791
+ this._depsOk = true;
1792
+ this._listeners = [];
1793
+ this._code = 0;
1794
+ }
1795
+ addListener(listener) {
1796
+ this._listeners.push(listener);
1797
+ return listener;
1798
+ }
1799
+ removeListener(listener) {
1800
+ this._listeners.splice(this._listeners.indexOf(listener), 1);
1801
+ }
1802
+ cardinalityChanged() {
1803
+ this._listeners.forEach(listener => listener());
1804
+ }
1805
+
1806
+ // ===================================================
1807
+ // Public API
1808
+ // ===================================================
1809
+ getCardinality() {
1810
+ return this._counter;
1811
+ }
1812
+ isMax() {
1813
+ return this._limits.max != null && this._counter >= this._limits.max;
1814
+ }
1815
+ isMin() {
1816
+ return this._limits.min != null && this._counter <= this._limits.min;
1817
+ }
1818
+ isFine() {
1819
+ return this._fine;
1820
+ }
1821
+ isDepsOk() {
1822
+ return this._depsOk;
1823
+ }
1824
+ getCode() {
1825
+ return this._code;
1826
+ }
1827
+ setCode(code) {
1828
+ if (this._code !== code) {
1829
+ this._code = code;
1830
+ this.cardinalityChanged();
1831
+ }
1832
+ }
1833
+ setDepsOk(ok) {
1834
+ if (this._depsOk !== ok) {
1835
+ this._code = model_CODES.UNKNOWN;
1836
+ this._depsOk = ok;
1837
+ this.cardinalityChanged();
1838
+ }
1839
+ }
1840
+ increment() {
1841
+ this._counter += 1;
1842
+ this._code = model_CODES.UNKNOWN;
1843
+ this._checkCounter();
1844
+ }
1845
+ decrement() {
1846
+ this._counter -= 1;
1847
+ this._code = model_CODES.UNKNOWN;
1848
+ this._checkCounter();
1849
+ }
1850
+ getCounter() {
1851
+ return this._counter;
1852
+ }
1853
+ checkCardinality() {
1854
+ this._checkCounter();
1855
+ }
1856
+ touch() {
1857
+ this._code = model_CODES.UNKNOWN;
1858
+ this.cardinalityChanged();
1859
+ }
1860
+
1861
+ // ===================================================
1862
+ // Private methods
1863
+ // ===================================================
1864
+ _checkCounter() {
1865
+ if (this._limits.max != null && this._counter === this._limits.max) {
1866
+ this._fine = true;
1867
+ } else if (this._limits.max != null && this._counter > this._limits.max) {
1868
+ this._fine = false;
1869
+ } else if (this._limits.min != null && this._counter === this._limits.min) {
1870
+ this._fine = true;
1871
+ } else if (this._limits.min != null && this._counter < this._limits.min) {
1872
+ this._fine = false;
1873
+ }
1874
+ this.cardinalityChanged();
1875
+ }
1876
+ }
1877
+ ;// ./src/model/GroupBinding.js
1878
+
1879
+
1880
+ class GroupBinding extends Binding {
1881
+ /**
1882
+ * Corresponds to a binding for a Group item type.
1883
+ * Handles sub-bindings grouped by item.
1884
+ * Validity of a group is determined by at least one of the sub-bindings being valid.
1885
+ * All sub-bindings must notify when their validity changed so that the validity of
1886
+ * the parent group can be checked and possibly updated.
1887
+ * Potential statement and constraints are asserted when both parents are valid and this GroupBinding is valid.
1888
+ *
1889
+ * @see template/Group
1890
+ */
1891
+ constructor(params) {
1892
+ super(params);
1893
+ const {
1894
+ constraints = [],
1895
+ childrenRootUri = null
1896
+ } = params;
1897
+ this._constraints = constraints;
1898
+ // Generates an array of arrays, one array for each child item.
1899
+
1900
+ this._childBindings = this._item.getChildren().map(() => []);
1901
+ this._rootUri = childrenRootUri;
1902
+ this._validPredicate = true;
1903
+ this._validObject = true;
1904
+ if (this._statement) {
1905
+ this._validPredicate = this._isValidPredicateValue(this._statement.getPredicate());
1906
+ this._validObject = this._isValidObjectValue(this._statement.getValue(), this._statement.getType());
1907
+ }
1908
+ this._cachedChildBindings = null;
1909
+ }
1910
+
1911
+ // ===================================================
1912
+ // Public API
1913
+ // ===================================================
1914
+
1915
+ getValue() {
1916
+ return this.getChildrenRootUri();
1917
+ }
1918
+ oneChildValidityChanged(valid) {
1919
+ if (valid === this._oneValidChild) {
1920
+ return false; // No change
1921
+ }
1922
+ if (!valid) {
1923
+ // Since we do not keep track of which children have valid values,
1924
+ // we need to iterate through all children to check if some other child has a valid value
1925
+ // (and check if valid predicate) if so, abort change.
1926
+ delete this._oneValidChild;
1927
+ if (this.isValid()) {
1928
+ return false; // No change
1929
+ }
1930
+ }
1931
+
1932
+ // Below we change the valid state of this group.
1933
+ this._oneValidChild = valid;
1934
+ // If there is no parent or it's valid state did not change,
1935
+ // then the groups assertions has not been changed and the children
1936
+ // have not been notified of this group change in validity.
1937
+ this._notifyValidityChange(valid && this._validPredicate && this._validObject);
1938
+ return this._validPredicate && this._validObject; // Validity of group changed only if valid predicate and object.
1939
+ }
1940
+ setSubject(uri) {
1941
+ if (this._statement) {
1942
+ this._statement.setSubject(uri);
1943
+ } else {
1944
+ this.getChildBindings().forEach(cb => cb.setSubject(uri));
1945
+ }
1946
+ }
1947
+ getChildrenRootUri() {
1948
+ if (this._statement) {
1949
+ // Either the object of the statement.
1950
+ return this._statement.getValue();
1951
+ } else if (this._rootUri != null) {
1952
+ return this._rootUri;
1953
+ } else if (this._parent != null) {
1954
+ return this._parent.getChildrenRootUri();
1955
+ }
1956
+ return undefined;
1957
+ }
1958
+ addChildBinding(binding) {
1959
+ this.addChildBindings([binding]);
1960
+ }
1961
+ addChildBindings(bindings) {
1962
+ this._cachedChildBindings = null;
1963
+ if (!Array.isArray(bindings) || bindings.length === 0) {
1964
+ return;
1965
+ }
1966
+ const item = bindings[0].getItem();
1967
+ const children = this._item.getChildren();
1968
+ for (let i = children.length; i >= 0; i--) {
1969
+ if (item === children[i]) {
1970
+ const cardTracker = this._childBindings[i].length > 0 ? this._childBindings[i][0].getCardinalityTracker() : new CardinalityTracker(item);
1971
+ this._childBindings[i] = this._childBindings[i].concat(bindings);
1972
+ bindings.forEach(binding => {
1973
+ binding.setParent(this);
1974
+ binding.setCardinalityTracker(cardTracker);
1975
+ cardTracker.increment();
1976
+ }, this);
1977
+ break;
1978
+ }
1979
+ }
1980
+ }
1981
+ removeChildBinding(binding) {
1982
+ this._cachedChildBindings = null;
1983
+ const children = this._item.getChildren();
1984
+ for (let i = children.length; i >= 0; i--) {
1985
+ if (binding.getItem() === children[i]) {
1986
+ this._childBindings[i].splice(this._childBindings[i].indexOf(binding), 1);
1987
+ delete binding._parent;
1988
+ binding.getCardinalityTracker().decrement();
1989
+ break;
1990
+ }
1991
+ }
1992
+ this.oneChildValidityChanged();
1993
+ this.bindingChange(binding);
1994
+ }
1995
+ getChildBindingsFor(item) {
1996
+ const children = this._item.getChildren();
1997
+ for (let i = children.length; i >= 0; i--) {
1998
+ if (item === children[i]) {
1999
+ return this._childBindings[i];
2000
+ }
2001
+ }
2002
+ return undefined;
2003
+ }
2004
+ getItemGroupedChildBindings() {
2005
+ return this._childBindings;
2006
+ }
2007
+ getChildBindings() {
2008
+ if (this._cachedChildBindings == null) {
2009
+ if (this._childBindings && this._childBindings.length > 0) {
2010
+ this._cachedChildBindings = [].concat(...this._childBindings);
2011
+ } else {
2012
+ this._cachedChildBindings = [];
2013
+ }
2014
+ }
2015
+ return this._cachedChildBindings;
2016
+ }
2017
+ setPredicate(predicate) {
2018
+ const oValidPredicate = this._validPredicate;
2019
+ if (this._isValidPredicateValue(predicate)) {
2020
+ this._statement.setPredicate(predicate);
2021
+ this._validPredicate = true;
2022
+ if (oValidPredicate !== true && this._validObject && (this.getItem().getNodetype() === 'URI' ? true : this._oneValidChild)) {
2023
+ this._notifyValidityChange(true);
2024
+ }
2025
+ } else {
2026
+ // Note that we actually do not set the invalid value, just unassert the statement.
2027
+ this._validPredicate = false;
2028
+ if (oValidPredicate !== false && this._validObject && (this.getItem().getNodetype() === 'URI' ? true : this._oneValidChild)) {
2029
+ this._notifyValidityChange(false);
2030
+ }
2031
+ }
2032
+ this.updateAssertions();
2033
+ }
2034
+
2035
+ // eslint-disable-next-line no-dupe-class-members
2036
+ getValue() {
2037
+ if (this._statement && this._statement.isObjectBlank()) {
2038
+ return '';
2039
+ }
2040
+ return this._statement && this._statement.getValue();
2041
+ }
2042
+ setValue(value) {
2043
+ if (this.getItem().getNodetype() !== 'URI' || !this._statement) {
2044
+ throw new Error('Cannot change the value of a group unless its nodetype is URI and' + ' also that it corresponds to a statement');
2045
+ }
2046
+ const oldIsValidObject = this._validObject;
2047
+ const isValidObject = this._isValidObjectValue(value, 'uri');
2048
+ if (isValidObject) {
2049
+ this._validObject = true;
2050
+ this._statement._o.type = 'uri';
2051
+ this._statement.setValue(value);
2052
+ this._constraints.forEach(stmt => stmt.setSubject(value));
2053
+ this.getChildBindings().forEach(cb => cb.setSubject(value));
2054
+ if (!oldIsValidObject && this._validPredicate) {
2055
+ this._notifyValidityChange(true);
2056
+ }
2057
+ } else {
2058
+ this._validObject = false;
2059
+ if (oldIsValidObject && this._validPredicate) {
2060
+ this._notifyValidityChange(false);
2061
+ }
2062
+ }
2063
+ }
2064
+ getPredicate() {
2065
+ return this._statement ? this._statement.getPredicate() : undefined;
2066
+ }
2067
+ remove() {
2068
+ this._oneValidChild = false;
2069
+ this.setAncestorValid(false);
2070
+ if (this._parent != null) {
2071
+ this._parent.removeChildBinding(this);
2072
+ }
2073
+ super.remove(arguments);
2074
+ }
2075
+ _isValid() {
2076
+ if (this.getItem().getNodetype() === 'URI') {
2077
+ return this._validObject && this._validPredicate;
2078
+ }
2079
+ return this._oneValidChild && this._validPredicate && this._validObject;
2080
+ }
2081
+ isValid() {
2082
+ if (this._oneValidChild == null) {
2083
+ this._oneValidChild = this._forceOneValidChildCheck();
2084
+ }
2085
+ return this._isValid();
2086
+ }
2087
+ setAncestorValid(valid) {
2088
+ this._ancestorValid = valid;
2089
+ this.updateAssertions();
2090
+ this._childBindings.forEach(bindingArr => bindingArr.forEach(binding => binding.setAncestorValid(valid && this._isValid()), this), this);
2091
+ }
2092
+ updateAssertions() {
2093
+ if (this._oneValidChild == null) {
2094
+ this.isValid();
2095
+ }
2096
+ const assert = this._ancestorValid && this._isValid();
2097
+ if (this._statement != null) {
2098
+ this._statement.setAsserted(assert);
2099
+ }
2100
+ this._constraints.forEach(constraintStmt => constraintStmt.setAsserted(assert));
2101
+ this.getChildBindings().forEach(binding => binding.updateAssertions());
2102
+ }
2103
+
2104
+ // ===================================================
2105
+ // Private methods
2106
+ // ===================================================
2107
+ _forceOneValidChildCheck() {
2108
+ return this._childBindings.some(arr => arr.some(binding => binding.isValid()));
2109
+ }
2110
+ _notifyValidityChange(newValidity) {
2111
+ if (!this._parent || !this._parent.oneChildValidityChanged(newValidity)) {
2112
+ // We can reuse the setAncestorValid method by setting the current value.
2113
+ this.updateAssertions();
2114
+ this._childBindings.forEach(bindingArr => bindingArr.forEach(binding => binding.setAncestorValid(newValidity)));
2115
+ }
2116
+ }
2117
+ _isValidObjectValue(value, type) {
2118
+ if (this.getItem().getNodetype() === 'URI' && type !== 'uri') {
2119
+ return false;
2120
+ }
2121
+ return super._isValidObjectValue(value);
2122
+ }
2123
+ }
2124
+ ;// ./src/model/PropertyChoiceBinding.js
2125
+ /* eslint-disable class-methods-use-this */
2126
+
2127
+ class PropertyChoiceBinding extends ChoiceBinding {
2128
+ /**
2129
+ * Are only used as the first child of a PropertyGroupBinding to capture
2130
+ * a variable predicate.
2131
+ */
2132
+ constructor(params) {
2133
+ super(params);
2134
+ this._objectBinding = params.objectBinding;
2135
+ }
2136
+
2137
+ /**
2138
+ * Remove shold not be doubled, hence it is handled by the objectBinding
2139
+ * delegated from the parent PropertyGroupBinding.
2140
+ */
2141
+ remove() {}
2142
+
2143
+ /**
2144
+ * The object binding handles the RDF statement, hence go through it to set
2145
+ * the predicate, note not the setValue function because that sets the object
2146
+ * rather than the predicate.
2147
+ * @param {Object} value
2148
+ */
2149
+ setValue(value) {
2150
+ this._objectBinding.setPredicate(value);
2151
+ }
2152
+ getValue() {
2153
+ return this._objectBinding.getPredicate();
2154
+ }
2155
+
2156
+ /**
2157
+ * Validity is controlled via the objectBinding, when it is valid the
2158
+ * parent PropertyGroupBinding will be valid since only one child is
2159
+ * enough to enable validity. Hence, this PropertyChoiceBinding can
2160
+ * be false all the time since it has no children and does not affect
2161
+ * the above hierarchy (and does not control assertment of statements).
2162
+ */
2163
+ isValid() {
2164
+ return false;
2165
+ }
2166
+
2167
+ /**
2168
+ * Does nothing, similar reason as for isValid.
2169
+ */
2170
+ updateAssertions() {}
2171
+ }
2172
+ ;// ./src/model/PropertyGroupBinding.js
2173
+
2174
+
2175
+
2176
+
2177
+
2178
+
2179
+ class PropertyGroupBinding extends GroupBinding {
2180
+ /**
2181
+ * This is a syntactical grouping to capture the case when both the predicate and
2182
+ * the object is variable. It is achieved by having exactly two children, the first being
2183
+ * a binding for the predicate in the form of a PredicateChoiceBinding and the second
2184
+ * being a binding for the object. The object binding can be a ValueBinding, ChoiceBinding or
2185
+ * a GroupBinding. It is not allowed to be another PropertyGroupBinding though.
2186
+ *
2187
+ * @see rforms.template.PropertyGroup
2188
+ */
2189
+ constructor(params) {
2190
+ super(params);
2191
+ const {
2192
+ statement,
2193
+ constraints
2194
+ } = params;
2195
+ this._statement = undefined;
2196
+ this._validPredicate = true; // Reset to initial value to ignore check from incorrect given statement in this case.
2197
+ this._constraints = [];
2198
+ const children = this._item.getChildren();
2199
+ const item = children[1];
2200
+ let oBinding;
2201
+ if (item instanceof Group) {
2202
+ oBinding = new GroupBinding({
2203
+ item,
2204
+ statement,
2205
+ constraints
2206
+ });
2207
+ } else if (item instanceof Choice) {
2208
+ oBinding = new ChoiceBinding({
2209
+ item,
2210
+ statement
2211
+ });
2212
+ } else {
2213
+ oBinding = new ValueBinding({
2214
+ item,
2215
+ statement
2216
+ });
2217
+ }
2218
+ const pBinding = new PropertyChoiceBinding({
2219
+ item: children[0],
2220
+ objectBinding: oBinding
2221
+ });
2222
+ this.addChildBinding(pBinding);
2223
+ this.addChildBinding(oBinding);
2224
+ }
2225
+
2226
+ // ===================================================
2227
+ // Public API
2228
+ // ===================================================
2229
+ getPredicateBinding() {
2230
+ return this._childBindings[0][0];
2231
+ }
2232
+ getObjectBinding() {
2233
+ return this._childBindings[1][0];
2234
+ }
2235
+ getGraph() {
2236
+ return this.getObjectBinding().getGraph();
2237
+ }
2238
+ }
2239
+ ;// ./src/model/GroupURIBinding.js
2240
+
2241
+
2242
+ /**
2243
+ * A binding corresponding to a text item with no property, allowing editing the
2244
+ * object in the statement of the group above.
2245
+ * The purpose it to allow editing URIs inside of a hierarchy, e.g. consider the following graph:
2246
+ *
2247
+ * http://example.com rdfs:seeAlso http://slashdot.org .
2248
+ * http://slashdot.org rdfs:label "Slashdot" .
2249
+ *
2250
+ * Allowing to edit both the URI (slashdot above) and it's label requires a construction like the following:
2251
+ *
2252
+ * - GroupItem with property rdfs:seeAlso (GroupBinding)
2253
+ * - TextItem without property (GroupURIBinding)
2254
+ * - TextItem with property rdfs:label (ValueBinding)
2255
+ *
2256
+ * @exports {rdforms/model/GroupURIBinding}
2257
+ * @class
2258
+ * @see rdforms/template/Text
2259
+ */
2260
+ class GroupURIBinding extends ValueBinding {
2261
+ /**
2262
+ * @return {String} corresponding to the value, even if the nodetype is URI
2263
+ * or datatype says for example date.
2264
+ */
2265
+ getValue() {
2266
+ return this._parent.getValue();
2267
+ }
2268
+ setValue(value) {
2269
+ return this._parent.setValue(value);
2270
+ }
2271
+ getGist() {
2272
+ return this._parent.getGist();
2273
+ }
2274
+ setGist(value, silent) {
2275
+ return this._parent.setGist(value, silent);
2276
+ }
2277
+ setSubject(uri) {}
2278
+
2279
+ /**
2280
+ * @return {String} corresponding to a uri.
2281
+ */
2282
+ getPredicate() {
2283
+ return this._parent.getPredicate();
2284
+ }
2285
+ remove() {
2286
+ this._parent.removeChildBinding(this);
2287
+ super.remove(arguments);
2288
+ }
2289
+ updateAssertions() {}
2290
+ isValid() {
2291
+ return true;
2292
+ }
2293
+ }
2294
+ ;// ./src/model/engine.js
2295
+ /* eslint-disable no-use-before-define */
2296
+
2297
+
2298
+
2299
+
2300
+
2301
+
2302
+
2303
+
2304
+
2305
+
2306
+
2307
+
2308
+
2309
+
2310
+ // See public API at the bottom of this file.
2311
+
2312
+ let _matchGroupItemChildren;
2313
+ let _clearDibbs;
2314
+ let _noDibbs;
2315
+ let _dibbs;
2316
+ let _createStatementsForConstraints;
2317
+ let _fuzzy = false;
2318
+ const match = (graph, uri, template) => {
2319
+ const rootBinding = new GroupBinding({
2320
+ item: template,
2321
+ childrenRootUri: uri,
2322
+ graph
2323
+ });
2324
+ _matchGroupItemChildren(rootBinding);
2325
+ _clearDibbs(rootBinding);
2326
+ return rootBinding;
2327
+ };
2328
+ const fuzzyMatch = (graph, uri, template) => {
2329
+ const rootBinding = new GroupBinding({
2330
+ item: template,
2331
+ childrenRootUri: uri,
2332
+ graph
2333
+ });
2334
+ _matchGroupItemChildren(rootBinding);
2335
+ _fuzzy = true;
2336
+ _matchGroupItemChildren(rootBinding);
2337
+ _fuzzy = false;
2338
+ _clearDibbs(rootBinding);
2339
+ return rootBinding;
2340
+ };
2341
+ const constructTemplate = (graph, uri, itemStore, requiredItems) => {
2342
+ const props = graph.findProperties(uri);
2343
+ const items = [];
2344
+ const fixedProps = {};
2345
+ if (requiredItems != null) {
2346
+ const addProperty = item => {
2347
+ if (item.getProperty() != null) {
2348
+ fixedProps[item.getProperty()] = true;
2349
+ } else if (item instanceof Group) {
2350
+ item.getChildren().forEach(addProperty);
2351
+ }
2352
+ };
2353
+ const addItem = item => {
2354
+ if (item != null) {
2355
+ addProperty(item);
2356
+ items.push(item);
2357
+ }
2358
+ };
2359
+ requiredItems.forEach(id => {
2360
+ let item = itemStore.getItem(id);
2361
+ if (item != null) {
2362
+ if (item instanceof Group && item.getProperty() == null) {
2363
+ item.getChildren().forEach(addItem);
2364
+ } else {
2365
+ addItem(item);
2366
+ }
2367
+ } else {
2368
+ item = itemStore.getItemByProperty(id);
2369
+ if (item) {
2370
+ addItem(item);
2371
+ } else {
2372
+ console.warn(`Warning, when autodetecting a template: Required item '${id}' is neither an id for a loaded item or a property for a loaded item, ignoring.`);
2373
+ }
2374
+ }
2375
+ });
2376
+ }
2377
+ props.forEach(prop => {
2378
+ if (fixedProps[prop]) {
2379
+ return;
2380
+ }
2381
+ const item = itemStore.getItemByProperty(prop);
2382
+ if (item != null) {
2383
+ items.push(item);
2384
+ }
2385
+ }, undefined);
2386
+ // TODO sort according to priority.
2387
+ return itemStore.createTemplateFromChildren(items);
2388
+ };
2389
+
2390
+ //= ==============================================
2391
+ // Core creation engine
2392
+ //= ==============================================
2393
+ const getFirstDataType = item => Array.isArray(item.getDatatype()) ? item.getDatatype()[0] : item.getDatatype();
2394
+ const _createTextItem = (parentBinding, item) => {
2395
+ if (!item.getProperty()) {
2396
+ const groupURIBinding = new GroupURIBinding({
2397
+ item,
2398
+ statement: null,
2399
+ matchingCode: model_CODES.OK
2400
+ });
2401
+ parentBinding.addChildBinding(groupURIBinding);
2402
+ return groupURIBinding;
2403
+ }
2404
+ const graph = parentBinding.getGraph();
2405
+ const nt = item.getNodetype();
2406
+ const obj = {
2407
+ value: '',
2408
+ type: 'literal'
2409
+ };
2410
+ if (nt === 'URI') {
2411
+ obj.type = 'uri';
2412
+ } else if (nt === 'DATATYPE_LITERAL') {
2413
+ obj.datatype = getFirstDataType(item);
2414
+ }
2415
+ const defaultValue = item.getValue();
2416
+ if (defaultValue != null && parentBinding.getChildBindingsFor(item).length === 0) {
2417
+ obj.value = defaultValue;
2418
+ const la = item.getLanguage();
2419
+ if (la != null) {
2420
+ obj.lang = la;
2421
+ }
2422
+ }
2423
+ const stmt = graph.create(parentBinding.getChildrenRootUri(), item.getProperty(), obj, false);
2424
+ const nbinding = new ValueBinding({
2425
+ item,
2426
+ statement: stmt
2427
+ });
2428
+ parentBinding.addChildBinding(nbinding);
2429
+ return nbinding;
2430
+ };
2431
+ const _createChoiceItem = (parentBinding, item) => {
2432
+ const graph = parentBinding.getGraph();
2433
+ const nt = item.getNodetype();
2434
+ const obj = {
2435
+ type: 'literal',
2436
+ value: ''
2437
+ };
2438
+ if (nt === 'DATATYPE_LITERAL') {
2439
+ obj.datatype = getFirstDataType(item);
2440
+ } else if (nt === 'RESOURCE' || nt === 'URI') {
2441
+ obj.type = 'uri';
2442
+ }
2443
+ const stmt = graph.create(parentBinding.getChildrenRootUri(), item.getProperty(), obj, false);
2444
+ const nbinding = new ChoiceBinding({
2445
+ item,
2446
+ statement: stmt
2447
+ });
2448
+ parentBinding.addChildBinding(nbinding);
2449
+ const defaultValue = item.getValue();
2450
+ const cardTracker = nbinding.getCardinalityTracker();
2451
+ if (defaultValue != null && cardTracker.getCounter() === 1) {
2452
+ nbinding.setChoice(_findChoice(item, defaultValue, graph), true);
2453
+ }
2454
+ return nbinding;
2455
+ };
2456
+ const _createGroupItem = (parentBinding, item, parentItems) => {
2457
+ let stmt;
2458
+ let constr;
2459
+ if (item.getProperty() !== undefined) {
2460
+ const graph = parentBinding.getGraph();
2461
+ if (item.getNodetype() === 'URI') {
2462
+ /*stmt = graph.create(parentBinding.getChildrenRootUri(), item.getProperty(), {
2463
+ type: 'uri',
2464
+ value: system.createURI(item, parentBinding),
2465
+ }, false);*/
2466
+ // Create as a blank, will not be asserted until changed into a uri.
2467
+ stmt = graph.create(parentBinding.getChildrenRootUri(), item.getProperty(), null, false);
2468
+ } else {
2469
+ stmt = graph.create(parentBinding.getChildrenRootUri(), item.getProperty(), null, false);
2470
+ }
2471
+ constr = _createStatementsForConstraints(graph, stmt.getValue(), item);
2472
+ }
2473
+ const nBinding = new GroupBinding({
2474
+ item,
2475
+ statement: stmt,
2476
+ constraints: constr
2477
+ });
2478
+ parentBinding.addChildBinding(nBinding);
2479
+
2480
+ // Only do loop detection for items that are stored in the itemStore and hence are
2481
+ // used in more than one place.
2482
+ const itemId = item._source['@id'];
2483
+ if (itemId) {
2484
+ // If loop stop.
2485
+ if (parentItems[itemId]) {
2486
+ return nBinding;
2487
+ }
2488
+ parentItems[itemId] = true;
2489
+ }
2490
+ // Do not create substructures directly, let the view model and user interaction decide
2491
+ // when to create children.
2492
+ /*
2493
+ array.forEach(item.getChildren(), function(childItem) {
2494
+ create(nBinding, childItem, parentItems);
2495
+ }); */
2496
+ return nBinding;
2497
+ };
2498
+ const _createPropertyGroupItem = (parentBinding, item) => {
2499
+ let stmt;
2500
+ let constr;
2501
+ const oItem = item.getChildren()[1];
2502
+ const graph = parentBinding.getGraph();
2503
+ if (oItem instanceof Group) {
2504
+ stmt = graph.create(parentBinding.getChildrenRootUri(), '', null, false);
2505
+ constr = _createStatementsForConstraints(graph, stmt.getSubject(), oItem);
2506
+ } else if (oItem instanceof Choice) {
2507
+ stmt = graph.create(parentBinding.getChildrenRootUri(), '', {
2508
+ type: 'uri',
2509
+ value: ''
2510
+ }, false);
2511
+ } else {
2512
+ stmt = graph.create(parentBinding.getChildrenRootUri(), '', {
2513
+ type: 'literal',
2514
+ value: ''
2515
+ }, false);
2516
+ }
2517
+ const nBinding = new PropertyGroupBinding({
2518
+ item,
2519
+ statement: stmt,
2520
+ constraints: constr
2521
+ });
2522
+ parentBinding.addChildBinding(nBinding);
2523
+ if (oItem instanceof Group) {
2524
+ oItem.getChildren().forEach(childItem => {
2525
+ create(nBinding.getObjectBinding(), childItem);
2526
+ });
2527
+ }
2528
+ return nBinding;
2529
+ };
2530
+ const create = (parentBinding, item, parentItems) => {
2531
+ if (item instanceof Text) {
2532
+ return _createTextItem(parentBinding, item);
2533
+ } else if (item instanceof PropertyGroup) {
2534
+ return _createPropertyGroupItem(parentBinding, item);
2535
+ } else if (item instanceof Group) {
2536
+ return _createGroupItem(parentBinding, item, parentItems || {});
2537
+ } else if (item instanceof Choice) {
2538
+ return _createChoiceItem(parentBinding, item);
2539
+ }
2540
+ return undefined;
2541
+ };
2542
+
2543
+ // ===============================================
2544
+ // Core matching engine
2545
+ // ===============================================
2546
+
2547
+ let _matchItem;
2548
+ let _isNodeTypeMatch;
2549
+ let _isDataTypeMatch;
2550
+ let _isPatternMatch;
2551
+ let _findChoice;
2552
+ let _findStatementsForConstraints;
2553
+ _matchGroupItemChildren = pb => {
2554
+ if (_fuzzy) {
2555
+ pb.getChildBindings().forEach(cb => {
2556
+ if (cb.getItem().getType() === 'group') {
2557
+ _matchGroupItemChildren(cb);
2558
+ }
2559
+ });
2560
+ }
2561
+ pb.getItem().getChildren().forEach(item => {
2562
+ if (_fuzzy) {
2563
+ if (item.getProperty() !== undefined) {
2564
+ _matchItem(pb, item);
2565
+ }
2566
+ } else {
2567
+ _matchItem(pb, item);
2568
+ }
2569
+ });
2570
+ };
2571
+ const _matchGroupItem = (pb, item) => {
2572
+ let stmts;
2573
+ let bindings;
2574
+ let constStmts;
2575
+ let groupBinding;
2576
+ const graph = pb.getGraph();
2577
+ // Case 1: there is a property in the item
2578
+ if (item.getProperty() !== undefined) {
2579
+ stmts = graph.find(pb.getChildrenRootUri(), item.getProperty());
2580
+ if (stmts.length > 0) {
2581
+ bindings = [];
2582
+ stmts.forEach(stmt => {
2583
+ if (_noDibbs(stmt)) {
2584
+ const pMatch = _isPatternMatch(item, stmt);
2585
+ const ntMatch = _isNodeTypeMatch(item, stmt);
2586
+ if (pMatch && ntMatch || _fuzzy) {
2587
+ constStmts = _findStatementsForConstraints(graph, stmt.getValue(), item);
2588
+ if (constStmts !== undefined || _fuzzy) {
2589
+ let matchingCode = !ntMatch ? model_CODES.WRONG_NODETYPE : model_CODES.OK;
2590
+ if (!pMatch) {
2591
+ matchingCode = model_CODES.WRONG_PATTERN;
2592
+ } else if (constStmts === undefined) {
2593
+ matchingCode = model_CODES.MISSING_CONSTRAINTS;
2594
+ }
2595
+ _dibbs(stmt);
2596
+ groupBinding = new GroupBinding({
2597
+ item,
2598
+ statement: stmt,
2599
+ constraints: constStmts,
2600
+ matchingCode
2601
+ });
2602
+ bindings.push(groupBinding);
2603
+ if (matchingCode === model_CODES.OK) {
2604
+ _matchGroupItemChildren(groupBinding); // Recursive call
2605
+ }
2606
+ }
2607
+ }
2608
+ }
2609
+ });
2610
+ pb.addChildBindings(bindings);
2611
+ }
2612
+ // Case 2: there is no property in the item, i.e. a layout item.
2613
+ } else {
2614
+ groupBinding = new GroupBinding({
2615
+ item
2616
+ });
2617
+ pb.addChildBindings([groupBinding]);
2618
+ _matchGroupItemChildren(groupBinding); // Recursive call
2619
+ }
2620
+ };
2621
+ const _matchPropertyGroupItem = (pb, item) => {
2622
+ const pItem = item.getPropertyItem();
2623
+ const oItem = item.getObjectItem();
2624
+ const graph = pb.getGraph();
2625
+ let constStmts;
2626
+ let binding;
2627
+ let pChoice;
2628
+ let oChoice;
2629
+ const bindings = [];
2630
+ graph.find(pb.getChildrenRootUri()).forEach(stmt => {
2631
+ if (_noDibbs(stmt) && _isNodeTypeMatch(oItem, stmt) && _isPatternMatch(oItem, stmt)) {
2632
+ pChoice = _findChoice(pItem, stmt.getPredicate(), stmt.getGraph());
2633
+ if (pChoice !== undefined) {
2634
+ binding = null;
2635
+ if (oItem instanceof Group) {
2636
+ constStmts = _findStatementsForConstraints(graph, stmt.getValue(), oItem);
2637
+ if (constStmts !== undefined) {
2638
+ _dibbs(stmt);
2639
+ binding = new PropertyGroupBinding({
2640
+ item,
2641
+ statement: stmt,
2642
+ constraints: constStmts
2643
+ });
2644
+ _matchGroupItemChildren(binding.getObjectBinding()); // Recursive call
2645
+ }
2646
+ } else if (oItem instanceof Choice) {
2647
+ oChoice = _findChoice(oItem, stmt.getValue(), stmt.getGraph());
2648
+ if (oChoice !== undefined) {
2649
+ _dibbs(stmt);
2650
+ binding = new PropertyGroupBinding({
2651
+ item,
2652
+ statement: stmt
2653
+ });
2654
+ binding.getObjectBinding().setChoice(oChoice);
2655
+ }
2656
+ } else {
2657
+ _dibbs(stmt);
2658
+ binding = new PropertyGroupBinding({
2659
+ item,
2660
+ statement: stmt
2661
+ });
2662
+ }
2663
+ if (binding !== null) {
2664
+ binding.getPredicateBinding().setChoice(pChoice);
2665
+ bindings.push(binding);
2666
+ }
2667
+ }
2668
+ }
2669
+ });
2670
+ if (bindings.length > 0) {
2671
+ pb.addChildBindings(bindings);
2672
+ }
2673
+ };
2674
+ const _matchTextItem = (pb, item) => {
2675
+ const bindings = [];
2676
+ if (item.getProperty() == null) {
2677
+ bindings.push(new GroupURIBinding({
2678
+ item,
2679
+ statement: null,
2680
+ matchingCode: model_CODES.OK
2681
+ }));
2682
+ } else {
2683
+ pb.getGraph().find(pb.getChildrenRootUri(), item.getProperty()).forEach(stmt => {
2684
+ if (_noDibbs(stmt) && _isPatternMatch(item, stmt)) {
2685
+ const ntMatch = _isNodeTypeMatch(item, stmt);
2686
+ const dtMatch = _isDataTypeMatch(item, stmt);
2687
+ if (ntMatch && (dtMatch || item.hasStyle('relaxedDatatypeMatch')) || _fuzzy) {
2688
+ _dibbs(stmt);
2689
+ let matchingCode = ntMatch ? model_CODES.OK : model_CODES.WRONG_NODETYPE;
2690
+ if (!dtMatch) {
2691
+ matchingCode = model_CODES.WRONG_DATATYPE;
2692
+ }
2693
+ bindings.push(new ValueBinding({
2694
+ item,
2695
+ statement: stmt,
2696
+ matchingCode
2697
+ }));
2698
+ }
2699
+ }
2700
+ });
2701
+ }
2702
+ if (bindings.length > 0) {
2703
+ pb.addChildBindings(bindings);
2704
+ }
2705
+ };
2706
+ const _matchChoiceItem = (pb, item) => {
2707
+ if (item.getProperty() == null) {
2708
+ return;
2709
+ }
2710
+ const bindings = [];
2711
+ pb.getGraph().find(pb.getChildrenRootUri(), item.getProperty()).forEach(stmt => {
2712
+ if (_noDibbs(stmt)) {
2713
+ const ntMatch = _isNodeTypeMatch(item, stmt);
2714
+ const dtMatch = _isDataTypeMatch(item, stmt);
2715
+ const pMatch = _isPatternMatch(item, stmt);
2716
+ if (ntMatch && dtMatch && pMatch || _fuzzy) {
2717
+ const choice = _findChoice(item, stmt.getValue(), stmt.getGraph());
2718
+ if (choice !== undefined) {
2719
+ _dibbs(stmt);
2720
+ let matchingCode = !ntMatch ? model_CODES.WRONG_NODETYPE : model_CODES.OK;
2721
+ if (!dtMatch) {
2722
+ matchingCode = model_CODES.WRONG_DATATYPE;
2723
+ } else if (!pMatch) {
2724
+ matchingCode = model_CODES.WRONG_PATTERN;
2725
+ } else if (choice.mismatch) {
2726
+ matchingCode = model_CODES.WRONG_VALUE;
2727
+ }
2728
+ bindings.push(new ChoiceBinding({
2729
+ item,
2730
+ statement: stmt,
2731
+ choice,
2732
+ matchingCode
2733
+ }));
2734
+ }
2735
+ }
2736
+ }
2737
+ });
2738
+ if (bindings.length > 0) {
2739
+ pb.addChildBindings(bindings);
2740
+ }
2741
+ };
2742
+ _matchItem = (pb, item) => {
2743
+ if (item instanceof Choice) {
2744
+ _matchChoiceItem(pb, item);
2745
+ } else if (item instanceof PropertyGroup) {
2746
+ _matchPropertyGroupItem(pb, item);
2747
+ } else if (item instanceof Group) {
2748
+ _matchGroupItem(pb, item);
2749
+ } else if (item instanceof Text) {
2750
+ _matchTextItem(pb, item);
2751
+ }
2752
+ };
2753
+
2754
+ // ===============================================
2755
+ // Utility functions used for matching purposes
2756
+ // ===============================================
2757
+
2758
+ /**
2759
+ * Compares the the type specified in the item and the type of the statements object.
2760
+ * @param {rdforms/template/Item} item
2761
+ * @param {jsonrdf/Statement} stmt
2762
+ */
2763
+ _isNodeTypeMatch = (item, stmt) => {
2764
+ const objectType = stmt.getType();
2765
+ // eslint-disable-next-line default-case
2766
+ switch (item.getNodetype()) {
2767
+ case 'LITERAL': // Any form of literal
2768
+ case 'ONLY_LITERAL': // No language, no datatype
2769
+ case 'PLAIN_LITERAL': // No datatype, perhaps a language
2770
+ case 'LANGUAGE_LITERAL':
2771
+ // Definitely a language
2772
+ return objectType === 'literal';
2773
+ case 'DATATYPE_LITERAL':
2774
+ // Definitiely a datatype
2775
+ return objectType === 'literal';
2776
+ case 'RESOURCE':
2777
+ return objectType === 'uri' || objectType === 'bnode';
2778
+ case 'URI':
2779
+ return objectType === 'uri';
2780
+ case 'BLANK':
2781
+ return objectType === 'bnode';
2782
+ }
2783
+ return false;
2784
+ };
2785
+ _isDataTypeMatch = (item, stmt) => {
2786
+ const dt = item.getNodetype() === 'DATATYPE_LITERAL' ? item.getDatatype() || null : null;
2787
+ if (dt != null) {
2788
+ return Array.isArray(dt) ? dt.indexOf(stmt.getDatatype()) !== -1 : stmt.getDatatype() === dt;
2789
+ }
2790
+ return true;
2791
+ };
2792
+ _isPatternMatch = (item, stmt) => {
2793
+ const pattern = item.getPattern();
2794
+ const value = utils.extractGist(stmt.getValue(), item.getValueTemplate());
2795
+ if (typeof pattern !== 'undefined') {
2796
+ try {
2797
+ return new RegExp(`^${pattern}$`).test(value);
2798
+ } catch (e) {
2799
+ return true;
2800
+ }
2801
+ }
2802
+ return true;
2803
+ };
2804
+
2805
+ /**
2806
+ * Matches constraints in the item to statements in the graph with the given uri as subject.
2807
+ *
2808
+ * @param {rdfjson/Graph} graph containing all available statements to match against.
2809
+ * @param {String} uri the subject to start matching from
2810
+ * @param {rdforms/template/Item} item containing the constraints.
2811
+ * @return an array of statements on success, undefined on failure.
2812
+ * If there are no constraints to match in the item an empty array is returned.
2813
+ */
2814
+ _findStatementsForConstraints = (graph, uri, item) => {
2815
+ let stmts;
2816
+ const constr = item.getConstraints();
2817
+ const results = [];
2818
+ const f = (predicate, object) => {
2819
+ stmts = graph.find(uri, predicate, {
2820
+ type: 'uri',
2821
+ value: object
2822
+ });
2823
+ if (stmts.length === 1) {
2824
+ results.push(stmts[0]);
2825
+ return undefined;
2826
+ }
2827
+ return false;
2828
+ };
2829
+ if (typeof constr === 'object' && constr !== null) {
2830
+ const keys = Object.keys(constr);
2831
+ for (let idx = 0; idx < keys.length; idx++) {
2832
+ const key = keys[idx];
2833
+ const obj = constr[key];
2834
+ if (obj instanceof Array) {
2835
+ let noMatch = true;
2836
+ obj.forEach(o => {
2837
+ if (f(key, o) !== false) {
2838
+ noMatch = false;
2839
+ }
2840
+ });
2841
+ if (noMatch) {
2842
+ return undefined;
2843
+ }
2844
+ } else if (f(key, obj) === false) {
2845
+ return undefined;
2846
+ }
2847
+ }
2848
+ return results;
2849
+ }
2850
+ return [];
2851
+ };
2852
+ _createStatementsForConstraints = (graph, uri, item) => {
2853
+ const results = [];
2854
+ const constr = item.getConstraints();
2855
+ if (typeof constr === 'object' && constr !== null) {
2856
+ Object.keys(constr).forEach(key => {
2857
+ const obj = constr[key];
2858
+ if (Array.isArray(obj)) {
2859
+ results.push(graph.create(uri, key, {
2860
+ type: 'uri',
2861
+ value: obj[0]
2862
+ }, false));
2863
+ } else {
2864
+ results.push(graph.create(uri, key, {
2865
+ type: 'uri',
2866
+ value: obj
2867
+ }, false));
2868
+ }
2869
+ });
2870
+ }
2871
+ return results;
2872
+ };
2873
+ _findChoice = (item, obj, graph) => {
2874
+ let index;
2875
+ let choices;
2876
+ if (item.hasChoices()) {
2877
+ choices = item.getChoices();
2878
+ for (index = 0; index < choices.length; index++) {
2879
+ if (choices[index].value === obj) {
2880
+ return choices[index];
2881
+ }
2882
+ }
2883
+ if (!item.hasStyle('strictmatch') || _fuzzy) {
2884
+ return {
2885
+ value: obj,
2886
+ label: {
2887
+ '': obj
2888
+ },
2889
+ mismatch: true
2890
+ };
2891
+ }
2892
+ } else {
2893
+ let label = utils.getLocalizedMap(graph, obj, item.getLabelProperties());
2894
+ if (label == null && _fuzzy) {
2895
+ label = {
2896
+ '': obj,
2897
+ mismatch: true
2898
+ };
2899
+ }
2900
+ const sa = graph.findFirstValue(obj, ChoiceBinding.seeAlso);
2901
+ if (label != null) {
2902
+ const choice = {
2903
+ label,
2904
+ value: obj
2905
+ };
2906
+ if (sa) {
2907
+ choice.seeAlso = sa;
2908
+ }
2909
+ return choice;
2910
+ } else if (system.getChoice != null) {
2911
+ return system.getChoice(item, obj, sa, graph);
2912
+ }
2913
+ }
2914
+ return undefined;
2915
+ };
2916
+ _dibbs = stmt => {
2917
+ stmt._dibbs = true;
2918
+ };
2919
+ _noDibbs = stmt => stmt._dibbs !== true;
2920
+ _clearDibbs = groupBinding => {
2921
+ let i;
2922
+ let j;
2923
+ let arr;
2924
+ const arrarr = groupBinding.getItemGroupedChildBindings() || [];
2925
+ for (i = 0; i < arrarr.length; i++) {
2926
+ arr = arrarr[i];
2927
+ for (j = 0; j < arr.length; j++) {
2928
+ const binding = arr[j];
2929
+ if (binding._statement) {
2930
+ delete binding._statement._dibbs;
2931
+ }
2932
+ if (binding instanceof GroupBinding || binding instanceof PropertyGroupBinding) {
2933
+ _clearDibbs(binding);
2934
+ }
2935
+ }
2936
+ }
2937
+ };
2938
+ const findFirstValueBinding = (binding, createIfMissing) => {
2939
+ if (binding instanceof ValueBinding) {
2940
+ return binding;
2941
+ }
2942
+ const cbs = binding.getItemGroupedChildBindings();
2943
+ const loc = external_moment_default().locale();
2944
+ for (let idx = 0; idx < cbs.length; idx++) {
2945
+ const vbs = cbs[idx];
2946
+ const childItem = binding.getItem().getChildren()[idx];
2947
+ if (vbs.length !== 0) {
2948
+ if (!(childItem instanceof Text)) {
2949
+ return findFirstValueBinding(vbs[0]);
2950
+ } else if (childItem.getNodetype() === 'LANGUAGE_LITERAL') {
2951
+ const result = {
2952
+ firstValue: vbs[0]
2953
+ };
2954
+ for (let i = 0; i < vbs.length; i++) {
2955
+ const l = vbs[i].getLanguage();
2956
+ if (l == null) {
2957
+ result.emptyLanguageValue = vbs[i];
2958
+ } else if (l === loc) {
2959
+ result.perfectLocaleLanguageValue = vbs[i];
2960
+ } else if (l.substring(0, 1) === loc.substring(0, 1)) {
2961
+ result.localeLanguageValue = vbs[i];
2962
+ } else if (l.indexOf('en') !== -1) {
2963
+ result.defaultLanguageValue = vbs[i];
2964
+ } else {
2965
+ result.anyLanguageValue = vbs[i];
2966
+ }
2967
+ }
2968
+ return result.perfectLocaleLanguageValue || result.localeLanguageValue || result.defaultLanguageValue || result.anyLanguageValue || result.firstValue;
2969
+ }
2970
+ return vbs[0];
2971
+ } else if (createIfMissing) {
2972
+ const b = create(binding, childItem, {});
2973
+ if (b instanceof ValueBinding) {
2974
+ b.setLanguage(loc);
2975
+ return b;
2976
+ }
2977
+ return findFirstValueBinding(b, true);
2978
+ }
2979
+ }
2980
+ return undefined;
2981
+ };
2982
+ const matchPathBelowBinding = (bindingTree, path) => {
2983
+ const _path = path[0] === '/' ? path.slice(1) : path;
2984
+ const gb = bindingTree.getItemGroupedChildBindings();
2985
+ const pred = _path[0];
2986
+ for (let i = 0; i < gb.length; i++) {
2987
+ const bindings = gb[i];
2988
+ let res;
2989
+ for (let j = 0; j < bindings.length; j++) {
2990
+ let b = bindings[j];
2991
+ let item = b.getItem();
2992
+ // Empty property choices.
2993
+ if (item instanceof Choice && typeof item.getProperty() === 'undefined' && pred === item.getId() && _path.length === 2 && _path[1] === b.getValue()) {
2994
+ return b;
2995
+ }
2996
+ if (!b.isValid()) {
2997
+ // eslint-disable-next-line no-continue
2998
+ continue;
2999
+ }
3000
+ if (item.getType() === 'propertygroup') {
3001
+ b = b.getObjectBinding();
3002
+ item = b.getItem();
3003
+ } else if (typeof item.getProperty() === 'undefined') {
3004
+ res = matchPathBelowBinding(b, _path);
3005
+ if (res) {
3006
+ return res;
3007
+ }
3008
+ }
3009
+ if (pred === '*' || pred === b.getPredicate() || pred === item.getId()) {
3010
+ if (item.getType() === 'group') {
3011
+ res = matchPathBelowBinding(b, _path.slice(1));
3012
+ if (res) {
3013
+ return res;
3014
+ }
3015
+ } else if (_path.length === 1 || _path[1] === '*' || _path[1] === b.getValue()) {
3016
+ return b;
3017
+ }
3018
+ }
3019
+ }
3020
+ }
3021
+ return undefined;
3022
+ };
3023
+ const findBindingRelativeToParentBinding = (parentBinding, path) => {
3024
+ const first = path[0];
3025
+ let b = parentBinding;
3026
+ if (first === '/') {
3027
+ while (b.getParent()) {
3028
+ b = b.getParent();
3029
+ }
3030
+ return b;
3031
+ } else if (first === '..') {
3032
+ for (let i = 0; i < path.length; i++) {
3033
+ if (path[i] === '..' && b.getParent()) {
3034
+ b = b.getParent();
3035
+ } else {
3036
+ return b;
3037
+ }
3038
+ }
3039
+ return b;
3040
+ }
3041
+ return parentBinding;
3042
+ };
3043
+ const findPopularChoice = (choiceItem, rootBinding) => {
3044
+ const id = choiceItem.getId();
3045
+ const values = {};
3046
+ const val2choice = {};
3047
+ choiceItem.getChoices().forEach(choice => {
3048
+ values[choice.value] = 0;
3049
+ val2choice[choice.value] = choice;
3050
+ });
3051
+ const recurse = groupB => {
3052
+ groupB.getChildBindings().forEach(cb => {
3053
+ const deps = cb.getItem().getDeps();
3054
+ if (deps && (deps[0] === id || deps[0] === '/' && deps[1] === id)) {
3055
+ const val = deps[0] === '/' ? deps[2] : deps[1];
3056
+ const counter = values[val];
3057
+ if (typeof counter !== 'undefined') {
3058
+ values[val] = counter + 1;
3059
+ }
3060
+ }
3061
+ });
3062
+ };
3063
+ recurse(rootBinding);
3064
+ let popularCount = 0;
3065
+ let value;
3066
+ Object.keys(values).forEach(val => {
3067
+ if (!value || values[val] > popularCount) {
3068
+ value = val;
3069
+ popularCount = values[val];
3070
+ }
3071
+ });
3072
+ return val2choice[value];
3073
+ };
3074
+ const _levelProfile = (profile, item, ignoreTopLevelGroup) => {
3075
+ const card = item.getCardinality();
3076
+ if (!ignoreTopLevelGroup || item.getType() !== 'group') {
3077
+ if (card != null) {
3078
+ if (card.min > 0) {
3079
+ profile.mandatory += 1;
3080
+ } else if (card.pref > 0) {
3081
+ profile.recommended += 1;
3082
+ } else {
3083
+ profile.optional += 1;
3084
+ }
3085
+ } else {
3086
+ profile.optional += 1;
3087
+ }
3088
+ }
3089
+ if (item.getType() === 'group') {
3090
+ item.getChildren().forEach(i => _levelProfile(profile, i));
3091
+ }
3092
+ return profile;
3093
+ };
3094
+ const levelProfile = item => {
3095
+ const profile = _levelProfile({
3096
+ mandatory: 0,
3097
+ recommended: 0,
3098
+ optional: 0
3099
+ }, item, true);
3100
+ profile.itemCount = profile.mandatory + profile.recommended + profile.optional;
3101
+ return profile;
3102
+ };
3103
+ const detectLevel = profile => {
3104
+ if (profile.mandatory > 0) {
3105
+ if (profile.recommended === 0 && profile.optional === 0) {
3106
+ return 'mandatory';
3107
+ } else if (profile.optional === 0) {
3108
+ return 'mixed_mandatory_recommended';
3109
+ } else if (profile.recommended === 0) {
3110
+ return 'mixed_mandatory_optional';
3111
+ }
3112
+ } else if (profile.recommended > 0) {
3113
+ if (profile.optional === 0) {
3114
+ return 'recommended';
3115
+ }
3116
+ return 'mixed_recommended_optional';
3117
+ } else if (profile.recommended === 0 && profile.recommended > 0 && profile.optional === 0) {
3118
+ return 'optional';
3119
+ }
3120
+ return 'mixed_all';
3121
+ };
3122
+
3123
+ //= ==============================================
3124
+ // Public API for matching and creation engine
3125
+ //= ==============================================
3126
+ /**
3127
+ * Matches a tree of statements with the uri as root
3128
+ * according to the constraints of the given template.
3129
+ * All the statements matched are found in the graph.
3130
+ * All statements in the graph that could be matched into the tree
3131
+ * are matched into the tree.
3132
+ * The tree is represented as a binding tree.
3133
+ *
3134
+ * @param {rdfjson/Graph} graph
3135
+ * @param {String} uri
3136
+ * @param {rdforms/template/Item} template
3137
+ * @return {rdforms/model/GroupBinding} which is the root of binding tree.
3138
+ */
3139
+
3140
+
3141
+
3142
+
3143
+
3144
+ /**
3145
+ * Finds the choice in a choice item that are the most popular, i.e. the choice that most
3146
+ * valid bindings hava a dependency to.
3147
+ * @param {rdforms/template/Choice} choiceItem
3148
+ * @param {rdforms/model/GroupBinding} rootBinding
3149
+ */
3150
+
3151
+
3152
+ /**
3153
+ * Constructs a template by finding an item per outgoing property for provided graph and
3154
+ * resource starting point.
3155
+ *
3156
+ * @param {Object} graph
3157
+ * @param {Object} uri
3158
+ * @param {Object} itemStore
3159
+ * @param {Array} requiredItems an array of required items specified by id or property that
3160
+ * will be enforced independent of corresponding property exists in the graph or not.
3161
+ * @return {rdforms/template/Item} the constructed template.
3162
+ */
3163
+
3164
+
3165
+ /**
3166
+ * Creates a new binding below the given parentBinding according to what the item specifies.
3167
+ * New triples are created in the provided graph although not expressed if they have an
3168
+ * empty predicate or object. The item must be a direct child of the item
3169
+ * of the parentBinding.
3170
+ *
3171
+ * @param {rdforms/model/Binding} parentBinding
3172
+ * @param {rdforms/template/Item} item
3173
+ * @param {Object} parentItems is a hash of parent Items to use for loop detection.
3174
+ */
3175
+
3176
+
3177
+ /**
3178
+ * Finds the first value binding in a binding tree, depth first.
3179
+ * If multiple value bindings are found with nodeType LANGUAGE_LITERAL
3180
+ * on the same level, the binding with the most appropriate language is chosen.
3181
+ * Most appropriate means checking for:
3182
+ * 1) exact current language (e.g. en_US),
3183
+ * 2) current language (e.g. matches en even if locale is en_US)
3184
+ * 3) default language (currently set to en)
3185
+ * 4) any literal with a language set found
3186
+ * 5) the first literal found
3187
+ *
3188
+ * @return {rdforms/model/ValueBinding}
3189
+ */
3190
+
3191
+
3192
+ /**
3193
+ * Calculates the level profile, i.e. the amount of items on mandatory, recommended
3194
+ * and optional level in a given template.
3195
+ *
3196
+ * @param {rdforms/template/Item} item
3197
+ * @return {Object} with keys mandatory, recommended and optional, each pointing to an integer.
3198
+ */
3199
+
3200
+
3201
+ /**
3202
+ * Investigates a level profile and provides the following responses:
3203
+ * * mandatory - only mandatory items
3204
+ * * recommended - only recommended items
3205
+ * * optional - only optional items
3206
+ * * mixed_all - a mix of all items
3207
+ * * mixed_mandatory_recommended - only mandatory and recommended items
3208
+ * * mixed_mandatory_optional - only mandatory and optional items
3209
+ * * mixed_recommended_optional - only recommended and optional items
3210
+ * @param {object} profile as provided by the levelProfile function.
3211
+ * @return {string} one of the responses outlined
3212
+ */
3213
+
3214
+
3215
+ /* harmony default export */ const engine = ({
3216
+ // TODO @valentino anti-pattern. This is done because engine is used in EntryScape. It shouldn't really...
3217
+ detectLevel,
3218
+ levelProfile,
3219
+ findFirstValueBinding,
3220
+ create,
3221
+ constructTemplate,
3222
+ findPopularChoice,
3223
+ match,
3224
+ fuzzyMatch,
3225
+ matchPathBelowBinding,
3226
+ findBindingRelativeToParentBinding,
3227
+ CODES: model_CODES
3228
+ });
3229
+ ;// ./src/template/ItemStore.js
3230
+
3231
+
3232
+
3233
+
3234
+
3235
+
3236
+
3237
+
3238
+ const deepMerge = (source1, source2) => {
3239
+ if (!source1 || !source2) {
3240
+ return source2 === undefined ? source1 : source2;
3241
+ }
3242
+ if (Array.isArray(source1) && Array.isArray(source2)) {
3243
+ return [].concat(origSource[key], extSource[key]);
3244
+ }
3245
+ if (typeof source1 === 'object' && typeof source2 === 'object') {
3246
+ const obj = {};
3247
+ Object.keys(source1).concat(Object.keys(source2)).forEach(key => {
3248
+ obj[key] = deepMerge(source1[key], source2[key]);
3249
+ });
3250
+ return obj;
3251
+ }
3252
+ return source2;
3253
+ };
3254
+ class ItemStore {
3255
+ /**
3256
+ * Keeps a registry of templates and reusable items.
3257
+ * Use the createTemplate method to create templates from a source
3258
+ * json structure, if the structure contains reusable items they are
3259
+ * created and stored separately as well.
3260
+ */
3261
+ constructor(ontologyStore) {
3262
+ this.automaticSortAllowed = true;
3263
+ /**
3264
+ * Value may be console methods or 'throw'.
3265
+ * @type {string}
3266
+ */
3267
+ this.handleErrorAs = 'throw';
3268
+
3269
+ //= =================================================;
3270
+ // Private Attribute;
3271
+ //= =================================================;
3272
+ this._bundles = [];
3273
+ this._registry = {};
3274
+ this._registryByProperty = {};
3275
+ this._ontologyStore = ontologyStore || new OntologyStore();
3276
+ }
3277
+
3278
+ //= ==================================================
3279
+ // Public API
3280
+ //= ==================================================
3281
+ getTemplate(id) {
3282
+ return this.getItem(id);
3283
+ }
3284
+ getChildren(group, original) {
3285
+ if (group == null) {
3286
+ return [];
3287
+ }
3288
+ const origSource = group.getSource(true);
3289
+ const origSourceContent = origSource.content || origSource.items || [];
3290
+ if (original) {
3291
+ return this._createItems(origSourceContent, group._forceChildrenClones, group.getBundle());
3292
+ }
3293
+ const ext = this.getItem(origSource.extends);
3294
+ if (ext) {
3295
+ const children = group.getChildren(true);
3296
+ if (group.getEnhanced('items') || children.length === 0) {
3297
+ return ext.getChildren().concat(children);
3298
+ }
3299
+ return children;
3300
+ }
3301
+ return group.getChildren(true);
3302
+ }
3303
+ getItem(id) {
3304
+ if (id != null) {
3305
+ return this._registry[id];
3306
+ }
3307
+ return undefined;
3308
+ }
3309
+ getItems() {
3310
+ return Object.keys(this._registry).map(key => this._registry[key]);
3311
+ }
3312
+ renameItem(from, to) {
3313
+ if (this._registry[to]) {
3314
+ this._handleError(`Cannot rename to ${to} since an item with that id already exists.`);
3315
+ return;
3316
+ }
3317
+ if (to === '' || to === null) {
3318
+ this._handleError('Cannot give an item an empty string or null as id.');
3319
+ return;
3320
+ }
3321
+ const item = this._registry[from];
3322
+ if (item) {
3323
+ delete this._registry[from];
3324
+ this._registry[to] = item;
3325
+ item.setId(to);
3326
+ }
3327
+ const renameInGroup = source => {
3328
+ const children = source.content;
3329
+ if (children) {
3330
+ for (let j = 0; j < children.length; j++) {
3331
+ const child = children[j];
3332
+ if (child.id === from || child['@id'] === from) {
3333
+ child.id = to;
3334
+ delete child['@id']; // Clean up backward compatability.
3335
+ }
3336
+ if (child.content) {
3337
+ renameInGroup(child);
3338
+ }
3339
+ }
3340
+ }
3341
+ };
3342
+ const items = this.getItems();
3343
+ for (let i = 0; i < items.length; i++) {
3344
+ const childItem = items[i];
3345
+ if (childItem instanceof Group) {
3346
+ renameInGroup(childItem._source);
3347
+ }
3348
+ }
3349
+ }
3350
+ getItemIds() {
3351
+ return Object.keys(this._registry);
3352
+ }
3353
+ getItemByProperty(property) {
3354
+ return this._registryByProperty[property];
3355
+ }
3356
+ detectTemplate(graph, uri, requiredItems) {
3357
+ return constructTemplate(graph, uri, this, requiredItems);
3358
+ }
3359
+
3360
+ /**
3361
+ * Bundle is an object containing:
3362
+ * path - can be a relative or absolute path to where the templates are/will be loaded from, optional.
3363
+ * source - a RDForms template object, mandatory.
3364
+ *
3365
+ * @param {Object} bundleSrc
3366
+ * @return {Bundle} the created bundle.
3367
+ */
3368
+ registerBundle(bundle) {
3369
+ bundle.itemStore = this;
3370
+ const b = new Bundle(bundle);
3371
+ this._bundles.push(b);
3372
+ if (bundle.source && bundle.source.namespaces) {
3373
+ rdfjson_namespaceObject.namespaces.add(bundle.source.namespaces);
3374
+ }
3375
+ const templates = bundle.source.templates || bundle.source.auxilliary;
3376
+ if (templates instanceof Array) {
3377
+ this._createItems(templates, false, b);
3378
+ }
3379
+ if (typeof bundle.source.cachedChoices === 'object') {
3380
+ this._ontologyStore.importRegistry(bundle.source.cachedChoices);
3381
+ }
3382
+ return b;
3383
+ }
3384
+ getBundles() {
3385
+ return this._bundles;
3386
+ }
3387
+
3388
+ // Backward compatability
3389
+ createTemplate(source) {
3390
+ const b = this.registerBundle({
3391
+ source
3392
+ });
3393
+ return b.getRoot();
3394
+ }
3395
+ createTemplateFromChildren(children) {
3396
+ const childrenObj = (children || []).map(child => typeof child === 'string' ? this.getItem(child) : child);
3397
+ return new Group({
3398
+ source: {},
3399
+ children: childrenObj,
3400
+ itemStore: this
3401
+ });
3402
+ }
3403
+ setPriorities(priorities) {
3404
+ this.priorities = priorities;
3405
+ }
3406
+
3407
+ // eslint-disable-next-line class-methods-use-this
3408
+ createExtendedSource(origSource, extSource) {
3409
+ const newSource = Object.assign({}, origSource, extSource);
3410
+ if (extSource.id === undefined) {
3411
+ // If no new id is provided in the original source,
3412
+ // don't inherit the id of the extention as then
3413
+ // it will be overwrite it in the ItemStore
3414
+ delete newSource.id;
3415
+ }
3416
+ if (extSource.enhanced) {
3417
+ let keys;
3418
+ if (extSource.enhanced === true) {
3419
+ keys = Object.keys(origSource).concat(Object.keys(extSource));
3420
+ } else {
3421
+ keys = Object.keys(extSource.enhanced);
3422
+ }
3423
+ keys.forEach(key => {
3424
+ newSource[key] = deepMerge(origSource[key], extSource[key]);
3425
+ });
3426
+ }
3427
+ newSource._extendedSource = extSource;
3428
+ newSource.extends = null; // Avoid infinite recursion when creating the fleshed out item.
3429
+ delete newSource.children;
3430
+ return newSource;
3431
+ }
3432
+
3433
+ /**
3434
+ * At a minimum the source must contain a type, the rest can be changed later.
3435
+ *
3436
+ * @param source
3437
+ * @returns {*}
3438
+ */
3439
+ createItem(source, forceClone, skipRegistration, bundle) {
3440
+ let item;
3441
+ const id = source.id || source['@id'];
3442
+ const type = source.type || source['@type'];
3443
+ if (source.extends) {
3444
+ // Explicit extends given
3445
+ const extItem = this._registry[source.extends];
3446
+ if (extItem == null) {
3447
+ this._handleError(`Cannot find item to extend with id: ${source.extends}`);
3448
+ }
3449
+ if (extItem) {
3450
+ const newSource = this.createExtendedSource(extItem.getSource(), source);
3451
+ return this.createItem(newSource, false, false, bundle);
3452
+ }
3453
+ }
3454
+ if (type != null) {
3455
+ // If there is a type in the source then it means that the object is a new item.
3456
+ // eslint-disable-next-line default-case
3457
+ switch (type) {
3458
+ case 'text':
3459
+ item = new Text({
3460
+ source,
3461
+ itemStore: this,
3462
+ bundle
3463
+ });
3464
+ break;
3465
+ case 'choice':
3466
+ item = new Choice({
3467
+ source,
3468
+ itemStore: this,
3469
+ ontologyStore: this._ontologyStore,
3470
+ bundle
3471
+ });
3472
+ break;
3473
+ case 'group':
3474
+ item = new Group({
3475
+ source,
3476
+ children: null,
3477
+ itemStore: this,
3478
+ bundle
3479
+ }); // Lazy loading of children.
3480
+ break;
3481
+ case 'propertygroup':
3482
+ item = new PropertyGroup({
3483
+ source,
3484
+ children: null,
3485
+ itemStore: this,
3486
+ bundle
3487
+ }); // Lazy loading of children.
3488
+ break;
3489
+ }
3490
+ if (skipRegistration !== true) {
3491
+ if (source.property != null) {
3492
+ this._registryByProperty[source.property] = item;
3493
+ if (this.priorities && this.priorities[source.property] != null) {
3494
+ item.priority = this.priorities[source.property];
3495
+ }
3496
+ }
3497
+ if (id != null) {
3498
+ if (this._registry[id]) {
3499
+ console.log(`RDForms conflict with item id ${id}, overwriting item from bundle "${this._registry[id].getBundle()?.getPath() || ''}" with item from bundle "${item.getBundle()?.getPath() || ''}".`);
3500
+ }
3501
+ this._registry[id] = item;
3502
+ if (bundle != null) {
3503
+ bundle.addItem(item);
3504
+ }
3505
+ }
3506
+ }
3507
+ return item;
3508
+ }
3509
+ // No type means it is a reference, check that the referred item (via id) exists
3510
+ if (id == null) {
3511
+ this._handleError('Cannot create subitem, `type` for creating new or `id` for referencing external are required.');
3512
+ return;
3513
+ }
3514
+ if (this._registry[id] == null) {
3515
+ this._handleError(`Cannot find referenced subitem using identifier: ${id}`);
3516
+ return;
3517
+ }
3518
+
3519
+ // Clone if forceClone set to true or if the source contains non-id properties.
3520
+ if (forceClone === true || Object.keys(source).find(key => key !== 'id' && key !== '@id')) {
3521
+ const newSource = Object.assign(Object.assign({}, this._registry[id]._source), source);
3522
+ return this.createItem(newSource, false, true);
3523
+ }
3524
+ return this._registry[id];
3525
+ }
3526
+ removeItem(item, removereferences) {
3527
+ const b = item.getBundle();
3528
+ if (b != null) {
3529
+ b.removeItem(item);
3530
+ }
3531
+ if (item.getId() != null) {
3532
+ delete this._registry[item.getId()];
3533
+ }
3534
+ const prop = item.getProperty();
3535
+ if (prop != null && this._registryByProperty[prop] === item) {
3536
+ delete this._registryByProperty[prop];
3537
+ }
3538
+ if (removereferences) {
3539
+ // TODO
3540
+ }
3541
+ }
3542
+
3543
+ //= ==================================================
3544
+ // Private methods
3545
+ //= ==================================================
3546
+ _createItems(sourceArray, forceClone, bundle) {
3547
+ return sourceArray.map((child, index) => {
3548
+ // If child is not a object but a direct string reference,
3549
+ const childToUse = typeof child === 'string' ? sourceArray[index] = {
3550
+ id: child
3551
+ } : child;
3552
+ return this.createItem(childToUse, forceClone, false, bundle);
3553
+ }).filter(item => item);
3554
+ }
3555
+ _handleError(message) {
3556
+ if (this.handleErrorAs === 'throw') {
3557
+ throw new Error(message);
3558
+ }
3559
+ console[this.handleErrorAs](message);
3560
+ }
3561
+ }
3562
+ ;// ./src/template/bundleLoader.js
3563
+ /* eslint-disable no-await-in-loop */
3564
+ /**
3565
+ * Check if there's any iterations left in a hypothetical array with 'length' given.
3566
+ *
3567
+ * @param {number} iteration
3568
+ * @param {number} length
3569
+ * @param {string} templateId
3570
+ * @throws
3571
+ */
3572
+ const stopFetchingOrJustLog = (iteration, length, templateId) => {
3573
+ const message = `Fetching template bundle ${templateId} failed.`;
3574
+ if (iteration === length - 1) {
3575
+ throw Error(`${message} Cannot recover from this, please fix.`);
3576
+ } else {
3577
+ console.log(`${message} Will try to fetch from a fallback option.`);
3578
+ }
3579
+ };
3580
+
3581
+ /**
3582
+ * Return the first successfully fetched bundle from a list of urls or throw en error if none could be fetched
3583
+ *
3584
+ * @param {Array<String>} urls
3585
+ * @returns {Promise<Response | never | void>}
3586
+ */
3587
+ const fetchBundle = async urls => {
3588
+ const totalUrls = urls.length;
3589
+ let response;
3590
+ let bundle;
3591
+ let path;
3592
+ for (let i = 0; i < totalUrls; i++) {
3593
+ // try to fetch the bundle, fails only if there's some network error. A 404 is not an error
3594
+ path = urls[i];
3595
+ try {
3596
+ response = await fetch(path);
3597
+ } catch (e) {
3598
+ throw Error(`A network error ocurred while trying to fetch bundle ${path}`);
3599
+ }
3600
+
3601
+ // check if we got a 2xx
3602
+ if (response && response.ok) {
3603
+ // check if what we got back looks like json and try to parse
3604
+ // if all good, then you're done
3605
+ // if it cannot parse, then fail soft or hard depending on if there's a fallback left to check
3606
+ try {
3607
+ const contentType = response.headers.has('content-type') && response.headers.get('content-type');
3608
+ if (contentType && contentType.includes('application/json')) {
3609
+ bundle = await response.json();
3610
+ break;
3611
+ } else {
3612
+ throw new Error(`Failed fetching template ${path}. Expected a JSON file and got ${contentType}`);
3613
+ }
3614
+ } catch (e) {
3615
+ stopFetchingOrJustLog(i, totalUrls, path);
3616
+ }
3617
+ // got back something that's not a 2xx
3618
+ } else {
3619
+ stopFetchingOrJustLog(i, totalUrls, path);
3620
+ }
3621
+ }
3622
+ return {
3623
+ path,
3624
+ source: bundle
3625
+ };
3626
+ };
3627
+
3628
+ /**
3629
+ * Fetch or if loaded just wrap it in Promise.resolve
3630
+ * @param bundles
3631
+ * @returns {Promise<*>}
3632
+ */
3633
+ const promisifyBundles = bundles => bundles.map(bundle => bundle instanceof Array ? fetchBundle(bundle) : Promise.resolve({
3634
+ source: bundle
3635
+ }));
3636
+
3637
+ /**
3638
+ * Register bundle templates
3639
+ *
3640
+ * @param {ItemStore} itemStore
3641
+ * @param {array} bundles
3642
+ */
3643
+ const registerBundles = (itemStore, bundles = []) => bundles.map(bundle => itemStore.registerBundle(bundle));
3644
+
3645
+ /**
3646
+ *
3647
+ * @param {ItemStore} itemStore
3648
+ * @param bundlePaths {Array<Object|String>} an array of object (bundles) or paths
3649
+ * @param callback
3650
+ */
3651
+ /* harmony default export */ const bundleLoader = (async (itemStore, bundlePaths = [], callback = () => {}) => {
3652
+ if (bundlePaths.length === 0 && callback) {
3653
+ // nothing to load
3654
+ callback([]);
3655
+ }
3656
+
3657
+ // Fetch or if loaded just wrap it in Promise.resolve
3658
+ const bundlePromises = promisifyBundles(bundlePaths);
3659
+ const loadedBundles = await Promise.all(bundlePromises);
3660
+ const registeredBundles = registerBundles(itemStore, loadedBundles);
3661
+ callback(registeredBundles); // TODO remove; should be deprecated
3662
+ return registeredBundles;
3663
+ });
3664
+ ;// ./src/model/validate.js
3665
+
3666
+
3667
+
3668
+ const _clearMatchingCodes = binding => {
3669
+ binding.setMatchingCode(CODES.OK);
3670
+ if (binding.getItem().getType() === 'group') {
3671
+ binding.getChildBindings().forEach(childBinding => _clearMatchingCodes(childBinding));
3672
+ }
3673
+ };
3674
+
3675
+ /**
3676
+ * Generates a report for the given binding. Below is an example of a report:
3677
+ * {
3678
+ * errors: [{
3679
+ * parentBinding: parent_group_binding_instance
3680
+ * item: item_instance_where_error_is,
3681
+ * code: "many"
3682
+ * }, ...],
3683
+ * warnings: [{
3684
+ * parentBinding: parent_group_binding_instance
3685
+ * item: item_instance_where_warning_is,
3686
+ * code: "few"
3687
+ * }, ...],
3688
+ * deprecated: [binding_instance_of_deprecated_value, ...]
3689
+ * }
3690
+ *
3691
+ * @type {Object}
3692
+ */
3693
+ const bindingReport = (groupbinding, reportObj) => {
3694
+ let _reportObj = reportObj;
3695
+ if (_reportObj == null) {
3696
+ _reportObj = {
3697
+ errors: [],
3698
+ warnings: [],
3699
+ deprecated: []
3700
+ };
3701
+ } else {
3702
+ _reportObj.errors = _reportObj.errors || [];
3703
+ _reportObj.warnings = _reportObj.warnings || [];
3704
+ _reportObj.deprecated = _reportObj.deprecated || [];
3705
+ }
3706
+ // _clearMatchingCodes(groupbinding);
3707
+ // eslint-disable-next-line no-use-before-define
3708
+ return _createReport(groupbinding, _reportObj, true);
3709
+ };
3710
+ const _countValidBindings = bindings => {
3711
+ let counter = 0;
3712
+ for (let i = 0; i < bindings.length; i++) {
3713
+ if (bindings[i].isValid()) {
3714
+ counter += 1;
3715
+ }
3716
+ }
3717
+ return counter;
3718
+ };
3719
+ const updateViaCardinalityTracker = (bindings, code) => {
3720
+ if (bindings.length > 0) {
3721
+ const cardTr = bindings[0].getCardinalityTracker();
3722
+ cardTr.setCode(code);
3723
+ }
3724
+ };
3725
+ const doNotProceedFurther = (groupBinding, childItem) => {
3726
+ // Don't check further if the childItem is deprecated
3727
+ if (childItem.hasStyle('deprecated')) {
3728
+ return true;
3729
+ }
3730
+
3731
+ // Don't check further if the binding is hidden due to missing dependencies
3732
+ const childPath = childItem.getDeps();
3733
+ if (childPath) {
3734
+ const fromBinding = findBindingRelativeToParentBinding(groupBinding, childPath);
3735
+ if (!matchPathBelowBinding(fromBinding, childPath)) {
3736
+ return true;
3737
+ }
3738
+ }
3739
+ return false;
3740
+ };
3741
+ const _createReport = (groupbinding, report, firstLevel) => {
3742
+ if (groupbinding.getMatchingCode() !== model_CODES.OK) {
3743
+ return undefined;
3744
+ }
3745
+ const groupitem = groupbinding.getItem();
3746
+
3747
+ // Abort check if the groupbinding is hidden due to a missing dependency.
3748
+ // Check disabled since it is done for each child before recursive call
3749
+ /* const path = groupitem.getDeps();
3750
+ if (path && groupbinding.getParent() != null) {
3751
+ const fromBinding = findBindingRelativeToParentBinding(groupbinding.getParent(), path);
3752
+ if (!matchPathBelowBinding(fromBinding, path)) {
3753
+ return undefined;
3754
+ }
3755
+ } */
3756
+
3757
+ if (firstLevel === true || groupbinding.isValid() || groupitem.getProperty() == null || groupitem.hasStyle('atLeastOneChild') || groupitem.hasStyle('exactlyOneChild')) {
3758
+ const childrenItems = groupitem.getChildren();
3759
+
3760
+ // disjoint is deprecated in favour of atMostOneChild
3761
+ if (groupitem.hasStyle('disjoint') || groupitem.hasStyle('atMostOneChild') || groupitem.hasStyle('atLeastOneChild') || groupitem.hasStyle('exactlyOneChild')) {
3762
+ const bindings = groupbinding.getChildBindings();
3763
+ const nrOfValid = _countValidBindings(bindings);
3764
+ let code;
3765
+ if (nrOfValid > 1 && (groupitem.hasStyle('disjoint') || groupitem.hasStyle('atMostOneChild'))) {
3766
+ code = model_CODES.AT_MOST_ONE_CHILD;
3767
+ } else if (nrOfValid !== 1 && groupitem.hasStyle('exactlyOneChild')) {
3768
+ code = model_CODES.EXACTLY_ONE_CHILD;
3769
+ } else if (nrOfValid === 0 && groupitem.hasStyle('atLeastOneChild')) {
3770
+ code = model_CODES.AT_LEAST_ONE_CHILD;
3771
+ }
3772
+ if (code) {
3773
+ updateViaCardinalityTracker([groupbinding], code);
3774
+ // groupbinding.setMatchingCode(code);
3775
+ // Correct to set only on first child?
3776
+ if (childrenItems.length > 0) {
3777
+ report.errors.push({
3778
+ parentBinding: groupbinding,
3779
+ item: childrenItems[0],
3780
+ code
3781
+ });
3782
+ }
3783
+ }
3784
+ } else {
3785
+ groupbinding.getItemGroupedChildBindings().forEach((bindings, index) => {
3786
+ const childItem = childrenItems[index];
3787
+ if (doNotProceedFurther(groupbinding, childItem)) {
3788
+ return;
3789
+ }
3790
+ if (childItem.getProperty() != null) {
3791
+ const nrOfValid = _countValidBindings(bindings);
3792
+ const card = childItem.getCardinality();
3793
+ if (card.min != null && card.min > nrOfValid) {
3794
+ report.errors.push({
3795
+ parentBinding: groupbinding,
3796
+ item: childItem,
3797
+ code: model_CODES.TOO_FEW_VALUES_MIN
3798
+ });
3799
+ updateViaCardinalityTracker(bindings, model_CODES.TOO_FEW_VALUES_MIN);
3800
+ /* let counter = 0;
3801
+ bindings.forEach((binding) => {
3802
+ if (!binding.isValid()) {
3803
+ if (counter < card.min) {
3804
+ counter += 1;
3805
+ binding.setMatchingCode(CODES.TOO_FEW_VALUES_MIN);
3806
+ }
3807
+ }
3808
+ }); */
3809
+ } else if (card.pref != null && card.pref > nrOfValid) {
3810
+ report.warnings.push({
3811
+ parentBinding: groupbinding,
3812
+ item: childItem,
3813
+ code: model_CODES.TOO_FEW_VALUES_PREF
3814
+ });
3815
+ // updateViaCardinalityTracker(bindings, CODES.TOO_FEW_VALUES_PREF);
3816
+ }
3817
+ if (card.max != null && card.max < nrOfValid) {
3818
+ report.errors.push({
3819
+ parentBinding: groupbinding,
3820
+ item: childItem,
3821
+ code: model_CODES.TOO_MANY_VALUES
3822
+ });
3823
+ updateViaCardinalityTracker(bindings, model_CODES.TOO_MANY_VALUES);
3824
+ /* let counter = 0;
3825
+ bindings.forEach((binding) => {
3826
+ if (binding.isValid()) {
3827
+ counter += 1;
3828
+ if (counter > card.max) {
3829
+ binding.setMatchingCode(CODES.TOO_MANY_VALUES);
3830
+ }
3831
+ }
3832
+ }); */
3833
+ }
3834
+ }
3835
+ }, undefined);
3836
+ }
3837
+ groupbinding.getChildBindings().forEach(binding => {
3838
+ const item = binding.getItem();
3839
+ if (binding.getMatchingCode() !== model_CODES.OK) {
3840
+ report.errors.push({
3841
+ parentBinding: binding,
3842
+ item,
3843
+ code: binding.getMatchingCode()
3844
+ });
3845
+ }
3846
+ if (item.hasStyle('deprecated')) {
3847
+ report.deprecated.push(binding);
3848
+ }
3849
+ if (!doNotProceedFurther(groupbinding, item)) {
3850
+ // Recursive step
3851
+ if (item.getType() === 'group') {
3852
+ _createReport(binding, report);
3853
+ }
3854
+ }
3855
+ });
3856
+ }
3857
+ return report;
3858
+ };
3859
+
3860
+ /**
3861
+ * Generates a report for all resources identified. Resources are identified
3862
+ * according to their type. If a resource is present in the graph but its type is not
3863
+ * in the type2template map it will not be in the report.
3864
+ * Each resource will be validated according to the template for its type.
3865
+ * Below is an example of a report:
3866
+ * {
3867
+ * errors: 5,
3868
+ * warnings: 10,
3869
+ * deprecated: 1,
3870
+ * mandatoryError: ["dcat:Dataset"]
3871
+ * resources: [
3872
+ * {
3873
+ * uri: "http://example.com",
3874
+ * type: "vcard:Kind",
3875
+ * template: "vc:Kind",
3876
+ * errors: [{
3877
+ * path: "vcard:hasAddress > vcard:hasStreetAddress",
3878
+ * code: "few"
3879
+ * }],
3880
+ * warnings: [{
3881
+ * path: "vcard:hasFN",
3882
+ * code: "few"
3883
+ * }],
3884
+ * deprecated: [https://challengesgov.se/challenge-sthlm/
3885
+ * "vcard:fn"
3886
+ * ]
3887
+ * },
3888
+ * ...
3889
+ * ]
3890
+ * }
3891
+ *
3892
+ * @param {rdfjson/Graph} graph an rdf graph against which all validation is done
3893
+ * @param {Object} type2template a map between each type to check for and the template to use for validation
3894
+ * (the type may be given with namespace abbreviations)
3895
+ * @param {Array} mandatoryTypes an array of types to check that there are instances for
3896
+ * @return {Object} a report of the validity of the graph
3897
+ */
3898
+ const graphReport = (graph, type2template, mandatoryTypes = []) => {
3899
+ const type2resources = {};
3900
+ const allResources = {};
3901
+ let template;
3902
+ let rr;
3903
+ const report = {
3904
+ errors: 0,
3905
+ warnings: 0,
3906
+ deprecated: 0,
3907
+ resources: []
3908
+ };
3909
+ Object.keys(type2template).forEach(type => {
3910
+ // eslint-disable-next-line no-use-before-define
3911
+ const resources = _findResources(graph, type);
3912
+ type2resources[type] = resources;
3913
+ resources.forEach(resource => {
3914
+ allResources[resource] = true;
3915
+ });
3916
+ });
3917
+ Object.keys(type2resources).forEach(type => {
3918
+ template = type2template[type];
3919
+ type2resources[type].forEach(resource => {
3920
+ // eslint-disable-next-line no-use-before-define
3921
+ rr = _resourceReport(resource, graph, template, allResources);
3922
+ rr.uri = resource;
3923
+ rr.type = type;
3924
+ rr.template = template.getId();
3925
+ report.resources.push(rr);
3926
+ report.errors += rr.errors.length;
3927
+ report.warnings += rr.warnings.length;
3928
+ report.deprecated += rr.deprecated.length;
3929
+ });
3930
+ });
3931
+ const mandatoryError = [];
3932
+ mandatoryTypes.forEach(mt => {
3933
+ if (type2resources[mt].length === 0) {
3934
+ mandatoryError.push(mt);
3935
+ }
3936
+ });
3937
+ if (mandatoryError.length > 0) {
3938
+ report.mandatoryError = mandatoryError;
3939
+ report.errors += mandatoryError.length;
3940
+ }
3941
+ return report;
3942
+ };
3943
+ const _findResources = (graph, cls) => graph.find(null, 'rdf:type', cls).map(stmt => stmt.getSubject());
3944
+ const _includeIssue = (binding, resource, otherResources) => {
3945
+ let pb = binding;
3946
+ while (true) {
3947
+ const uri = pb.getChildrenRootUri ? pb.getChildrenRootUri() : pb.getParent().getChildrenRootUri();
3948
+ if (uri === resource) {
3949
+ return true;
3950
+ } else if (otherResources[uri]) {
3951
+ return false;
3952
+ }
3953
+ pb = pb.getParent();
3954
+ }
3955
+ };
3956
+ const _createPath = (binding, item) => {
3957
+ let _binding = binding;
3958
+ const path = [];
3959
+ if (item.getProperty() != null && _binding.getItem() !== item) {
3960
+ path.push(rdfjson_namespaceObject.namespaces.shorten(item.getProperty()));
3961
+ }
3962
+ while (true) {
3963
+ if (_binding.getItem().getProperty() != null) {
3964
+ path.push(rdfjson_namespaceObject.namespaces.shorten(_binding.getItem().getProperty()));
3965
+ }
3966
+ if (_binding.getParent() == null) {
3967
+ break;
3968
+ } else {
3969
+ _binding = _binding.getParent();
3970
+ }
3971
+ }
3972
+ path.reverse();
3973
+ return path.join(' > ');
3974
+ };
3975
+ const _filterReport = (report, resource, otherResources) => {
3976
+ const {
3977
+ errors,
3978
+ warnings,
3979
+ deprecated
3980
+ } = report;
3981
+ report.errors = errors.filter(err => _includeIssue(err.parentBinding, resource, otherResources));
3982
+ report.warnings = warnings.filter(warn => _includeIssue(warn.parentBinding, resource, otherResources));
3983
+ report.deprecated = deprecated.filter(depr => _includeIssue(depr, resource, otherResources));
3984
+ };
3985
+ const _createDepPath = dep => `${_createPath(dep.getParent(), dep.getItem())} > ${dep.getValue()}`;
3986
+ const _simplifyReport = report => {
3987
+ const {
3988
+ errors,
3989
+ warnings,
3990
+ deprecated
3991
+ } = report;
3992
+ report.errors = errors.map(err => ({
3993
+ path: _createPath(err.parentBinding, err.item),
3994
+ code: err.code
3995
+ }));
3996
+ report.warnings = warnings.map(warn => ({
3997
+ path: _createPath(warn.parentBinding, warn.item),
3998
+ code: warn.code
3999
+ }));
4000
+ report.deprecated = deprecated.map(dep => _createDepPath(dep));
4001
+ };
4002
+ const _resourceReport = (resource, graph, template, ignoreResources) => {
4003
+ const binding = fuzzyMatch(graph, resource, template);
4004
+ const report = bindingReport(binding);
4005
+ _filterReport(report, resource, ignoreResources || {});
4006
+ _simplifyReport(report);
4007
+ return report;
4008
+ };
4009
+
4010
+ /* harmony default export */ const validate = ({
4011
+ // TODO @valentino don't export default. Used in EntryScape
4012
+ graphReport,
4013
+ bindingReport
4014
+ });
4015
+ ;// ./main.node.js
4016
+ // import 'node-fetch';
4017
+
4018
+ global.fetch = (__webpack_require__(229)["default"]);
4019
+
4020
+
4021
+
4022
+
4023
+
4024
+
4025
+ module.exports = __webpack_exports__;
4026
+ /******/ })()
4027
+ ;