blob: 47f3a96f13ff01a7bfdec10eb350144615dc0c5a [file] [log] [blame]
Nils Diewald0e6992a2015-04-14 20:13:52 +00001/**
2 * Document group criterion
3 */
Nils Diewald4347ee92015-05-04 20:32:48 +00004/*
5 * TODO: Let the UPDATE event bubble up through parents!
6 */
Nils Diewald0e6992a2015-04-14 20:13:52 +00007define([
8 'vc/jsonld',
9 'vc/unspecified',
10 'vc/doc',
Akronb19803c2018-08-16 16:39:42 +020011 'vc/docgroupref',
Nils Diewald0e6992a2015-04-14 20:13:52 +000012 'util'
13], function (jsonldClass,
Akronb19803c2018-08-16 16:39:42 +020014 unspecClass,
15 docClass,
Akronadab5e52018-08-20 13:50:53 +020016 docGroupRefClass) {
Nils Diewald0e6992a2015-04-14 20:13:52 +000017
Akron0b489ad2018-02-02 16:49:32 +010018 const _validGroupOpRE = new RegExp("^(?:and|or)$");
Nils Diewald0e6992a2015-04-14 20:13:52 +000019
Akron0b489ad2018-02-02 16:49:32 +010020 const docGroupClass = {
Nils Diewald0e6992a2015-04-14 20:13:52 +000021 _ldType : "docGroup",
22
23 create : function (parent, json) {
24 var obj = Object.create(jsonldClass).upgradeTo(this);
25 obj._operands = [];
26 obj.fromJson(json);
27 if (parent !== undefined)
Akron0b489ad2018-02-02 16:49:32 +010028 obj._parent = parent;
Nils Diewald0e6992a2015-04-14 20:13:52 +000029 return obj;
30 },
31
32 newAfter : function (obj) {
Akronb50964a2020-10-12 11:44:37 +020033 this._operands.forEach(function (op, i) {
34 if (op === obj) {
Akron0b489ad2018-02-02 16:49:32 +010035 var operand = unspecClass.create(this);
36 this._operands.splice(i + 1, 0, operand);
37 return this.update();
38 };
Akronb50964a2020-10-12 11:44:37 +020039 }, this);
Nils Diewald0e6992a2015-04-14 20:13:52 +000040 },
41
42 // The doc is already set in the group
43 _duplicate : function (operand) {
Akronb19803c2018-08-16 16:39:42 +020044
45 // TODO:
46 // Also check for duplicate docGroupRefs!
47
Nils Diewald0e6992a2015-04-14 20:13:52 +000048 if (operand.ldType() !== 'doc')
Akron0b489ad2018-02-02 16:49:32 +010049 return null;
Nils Diewald0e6992a2015-04-14 20:13:52 +000050
Akronb50964a2020-10-12 11:44:37 +020051 const f = this._operands.find(
52 op =>
53 op.ldType() === 'doc'
54 && operand.key() === op.key()
55 && operand.matchop() === op.matchop()
56 && operand.value() === op.value()
57 );
58
59 if (f)
60 return f;
61
Nils Diewald0e6992a2015-04-14 20:13:52 +000062 return null;
63 },
64
65 append : function (operand) {
66
67 // Append unspecified object
68 if (operand === undefined) {
69
Akron0b489ad2018-02-02 16:49:32 +010070 // Be aware of cyclic structures!
71 operand = unspecClass.create(this);
72 this._operands.push(operand);
73 return operand;
Nils Diewald0e6992a2015-04-14 20:13:52 +000074 };
75
76 switch (operand["@type"]) {
Akron0b489ad2018-02-02 16:49:32 +010077
Nils Diewald0e6992a2015-04-14 20:13:52 +000078 case undefined:
Akron0b489ad2018-02-02 16:49:32 +010079 // No @type defined
80 if (operand["ldType"] !== undefined) {
81 if (operand.ldType() !== 'doc' &&
Akronb19803c2018-08-16 16:39:42 +020082 operand.ldType() !== 'docGroup' &&
83 operand.ldType() !== 'docGroupRef') {
Akron0b489ad2018-02-02 16:49:32 +010084 KorAP.log(812, "Operand not supported in document group");
85 return;
86 };
Nils Diewald0e6992a2015-04-14 20:13:52 +000087
Akron0b489ad2018-02-02 16:49:32 +010088 // Be aware of cyclic structures!
89 operand.parent(this);
Nils Diewald0e6992a2015-04-14 20:13:52 +000090
Akron0b489ad2018-02-02 16:49:32 +010091 var dupl = this._duplicate(operand);
92 if (dupl === null) {
93 this._operands.push(operand);
94 return operand;
95 };
96 return dupl;
97 };
Nils Diewald0e6992a2015-04-14 20:13:52 +000098
Akron0b489ad2018-02-02 16:49:32 +010099 KorAP.log(701, "JSON-LD group has no @type attribute");
100 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000101
102 case "koral:doc":
Akron0b489ad2018-02-02 16:49:32 +0100103 // Be aware of cyclic structures!
104 var doc = docClass.create(this, operand);
105 if (doc === undefined)
106 return;
107 var dupl = this._duplicate(doc);
108 if (dupl === null) {
109 this._operands.push(doc);
110 return doc;
111 };
112 return dupl;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000113
114 case "koral:docGroup":
Akron0b489ad2018-02-02 16:49:32 +0100115 // Be aware of cyclic structures!
116 var docGroup = docGroupClass.create(this, operand);
117 if (docGroup === undefined)
118 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000119
Akron0b489ad2018-02-02 16:49:32 +0100120 // Flatten group
121 if (docGroup.operation() === this.operation()) {
Akron678c26f2020-10-09 08:52:50 +0200122 docGroup.operands().forEach(function(op) {
Akron0b489ad2018-02-02 16:49:32 +0100123 var dupl = this._duplicate(op);
124 if (dupl === null) {
125 this._operands.push(op);
126 op.parent(this);
127 };
Akron678c26f2020-10-09 08:52:50 +0200128 }, this);
Akron0b489ad2018-02-02 16:49:32 +0100129 docGroup._operands = [];
130 docGroup.destroy();
131 return this;
132 };
133 this._operands.push(docGroup);
134 return docGroup;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000135
Akronb19803c2018-08-16 16:39:42 +0200136 case "koral:docGroupRef":
137
138 var docGroupRef = docGroupRefClass.create(this, operand);
139
140 if (docGroupRef === undefined) {
141 return
142 };
143
144 // TODO:
145 // Currently this doesn't do anything meaningful,
146 // as duplicate only checks on docs for the moment
147 /*
148 var dupl = this._duplicate(doc);
149 if (dupl === null) {
150 this._operands.push(doc);
151 return doc;
152 };
153 return dupl;
154 */
155 this._operands.push(docGroupRef);
156 return docGroupRef;
157
Nils Diewald0e6992a2015-04-14 20:13:52 +0000158 default:
Akron0b489ad2018-02-02 16:49:32 +0100159 KorAP.log(812, "Operand not supported in document group");
160 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000161 };
162 },
163
164 update : function () {
165 // There is only one operand in group
166
167 if (this._operands.length === 1) {
Akron0b489ad2018-02-02 16:49:32 +0100168
169 var parent = this.parent();
170 var op = this.getOperand(0);
171
172 // This will prevent destruction of
173 // the operand
174 this._operands = [];
Nils Diewald0e6992a2015-04-14 20:13:52 +0000175
Akron0b489ad2018-02-02 16:49:32 +0100176 // Parent is a group
177 if (parent.ldType() !== null)
178 return parent.replaceOperand(this, op).update();
Nils Diewald0e6992a2015-04-14 20:13:52 +0000179
Akron0b489ad2018-02-02 16:49:32 +0100180 // Parent is vc
181 else {
182 this.destroy();
183 // Cyclic madness
184 parent.root(op);
185 op.parent(parent);
186 return parent.root();
187 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000188 };
189
190 if (this._element === undefined)
Akron0b489ad2018-02-02 16:49:32 +0100191 return this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000192
193 var group = this._element;
194 group.setAttribute('data-operation', this.operation());
195
196 _removeChildren(group);
197
198 // Append operands
Akronb50964a2020-10-12 11:44:37 +0200199 this._operands.forEach(
200 op => group.appendChild(op.element())
201 );
Nils Diewald0e6992a2015-04-14 20:13:52 +0000202
203 // Set operators
204 var op = this.operators(
Akron0b489ad2018-02-02 16:49:32 +0100205 this.operation() == 'and' ? false : true,
206 this.operation() == 'or' ? false : true,
207 true
Nils Diewald0e6992a2015-04-14 20:13:52 +0000208 );
209
210 group.appendChild(op.element());
211
Akron13af2f42019-07-25 15:06:21 +0200212 if (KorAP.vc) {
213 var vcchevent = new CustomEvent('vcChange', {'detail':this});
214 KorAP.vc.element().dispatchEvent(vcchevent);
215 };
hebastade595b42019-02-05 13:53:10 +0100216
Nils Diewald0e6992a2015-04-14 20:13:52 +0000217 return this;
218 },
219
220 element : function () {
221 if (this._element !== undefined)
Akron0b489ad2018-02-02 16:49:32 +0100222 return this._element;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000223
224 this._element = document.createElement('div');
225 this._element.setAttribute('class', 'docGroup');
226
227 // Update the object - including optimization
228 this.update();
229
230 return this._element;
231 },
232
233 operation : function (op) {
234 if (arguments.length === 1) {
Akron0b489ad2018-02-02 16:49:32 +0100235 if (_validGroupOpRE.test(op)) {
236 this._op = op;
237 }
238 else {
239 KorAP.log(810, "Unknown operation type");
240 return;
241 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000242 };
243 return this._op || 'and';
244 },
245
246 operands : function () {
247 return this._operands;
248 },
249
250 getOperand : function (index) {
251 return this._operands[index];
252 },
253
254 // Replace operand
255 replaceOperand : function (oldOp, newOp) {
256
257 for (var i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +0100258 if (this._operands[i] === oldOp) {
259
260 // Just insert a doc or ...
261 if (newOp.ldType() === "doc" ||
262 newOp.ldType() === "non" ||
Akronb19803c2018-08-16 16:39:42 +0200263 newOp.ldType() === 'docGroupRef' ||
Akron0b489ad2018-02-02 16:49:32 +0100264 // ... insert a group of a different operation
265 // (i.e. "and" in "or"/"or" in "and")
266 newOp.operation() != this.operation()) {
267 this._operands[i] = newOp;
268 newOp.parent(this);
269 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000270
Akron0b489ad2018-02-02 16:49:32 +0100271 // Flatten group
272 else {
273 // Remove old group
274 this._operands.splice(i, 1);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000275
Akron0b489ad2018-02-02 16:49:32 +0100276 // Inject new operands
Akron678c26f2020-10-09 08:52:50 +0200277 newOp.operands().reverse().forEach(function(op) {
Akron0b489ad2018-02-02 16:49:32 +0100278 this._operands.splice(i, 0, op);
279 op.parent(this);
Akron678c26f2020-10-09 08:52:50 +0200280 }, this);
Akron0b489ad2018-02-02 16:49:32 +0100281 // Prevent destruction of operands
282 newOp._operands = [];
283 newOp.destroy();
284 };
285 oldOp.destroy();
286 return this;
287 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000288 };
289 return false;
290 },
291
292 // Delete operand from group
293 delOperand : function (obj) {
294 for (var i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +0100295 if (this._operands[i] === obj) {
296
297 // Delete identified operand
298 this._operands.splice(i,1);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000299
Akron0b489ad2018-02-02 16:49:32 +0100300 // Destroy object for cyclic references
301 obj.destroy();
Nils Diewald0e6992a2015-04-14 20:13:52 +0000302
Akron0b489ad2018-02-02 16:49:32 +0100303 return this;
304 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000305 };
306
307 // Operand not found
308 return undefined;
309 },
310
311 // Deserialize from json
312 fromJson : function (json) {
313 if (json === undefined)
Akron0b489ad2018-02-02 16:49:32 +0100314 return this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000315
316 if (json["@type"] === undefined) {
Akron0b489ad2018-02-02 16:49:32 +0100317 KorAP.log(701, "JSON-LD group has no @type attribute");
318 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000319 };
320
321 if (json["operation"] === undefined ||
Akron0b489ad2018-02-02 16:49:32 +0100322 typeof json["operation"] !== 'string') {
323 KorAP.log(811, "Document group expects operation");
324 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000325 };
326
327 var operation = json["operation"];
328
329 this.operation(operation.replace(/^operation:/,''));
330
331 if (json["operands"] === undefined ||
Akron0b489ad2018-02-02 16:49:32 +0100332 !(json["operands"] instanceof Array)) {
333 KorAP.log(704, "Operation needs operand list")
334 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000335 };
336
337 // Add all documents
Akron678c26f2020-10-09 08:52:50 +0200338 json["operands"].forEach(i => this.append(i));
Nils Diewald0e6992a2015-04-14 20:13:52 +0000339
340 return this;
341 },
342
343 toJson : function () {
344 var opArray = new Array();
Akronb50964a2020-10-12 11:44:37 +0200345 this._operands.forEach(function(op) {
346 if (op.ldType() !== 'non')
347 opArray.push(op.toJson());
348 });
Nils Diewald0e6992a2015-04-14 20:13:52 +0000349 return {
Akron0b489ad2018-02-02 16:49:32 +0100350 "@type" : "koral:" + this.ldType(),
351 "operation" : "operation:" + this.operation(),
352 "operands" : opArray
Nils Diewald0e6992a2015-04-14 20:13:52 +0000353 };
354 },
355
356 toQuery : function (brackets) {
357 var list = this._operands
Akron0b489ad2018-02-02 16:49:32 +0100358 .filter(function (op) {
hebastaa0282be2018-12-05 16:58:00 +0100359 return !op.incomplete();
Akron0b489ad2018-02-02 16:49:32 +0100360 })
361 .map(function (op) {
362 return (op.ldType() === 'docGroup') ?
363 op.toQuery(true) :
364 op.toQuery();
365 });
Nils Diewald0e6992a2015-04-14 20:13:52 +0000366
367 if (list.length === 1)
Akron0b489ad2018-02-02 16:49:32 +0100368 return list.join('');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000369 else {
Akron0b489ad2018-02-02 16:49:32 +0100370 var str = list.join(this.operation() === 'or' ? ' | ' : ' & ');
371 return brackets ? '(' + str + ')' : str;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000372 };
373 }
374 };
375 return docGroupClass;
376});