blob: 1dbd5d75aad0036b158cbd7189c1edbaa16958af [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 */
Akron88d237e2020-10-21 08:05:18 +02007"use strict";
8
Nils Diewald0e6992a2015-04-14 20:13:52 +00009define([
10 'vc/jsonld',
11 'vc/unspecified',
12 'vc/doc',
Akronb19803c2018-08-16 16:39:42 +020013 'vc/docgroupref',
Nils Diewald0e6992a2015-04-14 20:13:52 +000014 'util'
15], function (jsonldClass,
Akronb19803c2018-08-16 16:39:42 +020016 unspecClass,
17 docClass,
Akronadab5e52018-08-20 13:50:53 +020018 docGroupRefClass) {
Nils Diewald0e6992a2015-04-14 20:13:52 +000019
Akron0b489ad2018-02-02 16:49:32 +010020 const _validGroupOpRE = new RegExp("^(?:and|or)$");
Nils Diewald0e6992a2015-04-14 20:13:52 +000021
Akron0b489ad2018-02-02 16:49:32 +010022 const docGroupClass = {
Akron88d237e2020-10-21 08:05:18 +020023
Nils Diewald0e6992a2015-04-14 20:13:52 +000024 _ldType : "docGroup",
25
26 create : function (parent, json) {
Akron88d237e2020-10-21 08:05:18 +020027 const obj = Object.create(jsonldClass).upgradeTo(this);
Nils Diewald0e6992a2015-04-14 20:13:52 +000028 obj._operands = [];
29 obj.fromJson(json);
30 if (parent !== undefined)
Akron0b489ad2018-02-02 16:49:32 +010031 obj._parent = parent;
Nils Diewald0e6992a2015-04-14 20:13:52 +000032 return obj;
33 },
34
Akron88d237e2020-10-21 08:05:18 +020035
Nils Diewald0e6992a2015-04-14 20:13:52 +000036 newAfter : function (obj) {
Akronb50964a2020-10-12 11:44:37 +020037 this._operands.forEach(function (op, i) {
38 if (op === obj) {
Akron88d237e2020-10-21 08:05:18 +020039 const operand = unspecClass.create(this);
Akron0b489ad2018-02-02 16:49:32 +010040 this._operands.splice(i + 1, 0, operand);
41 return this.update();
42 };
Akronb50964a2020-10-12 11:44:37 +020043 }, this);
Nils Diewald0e6992a2015-04-14 20:13:52 +000044 },
45
Akron88d237e2020-10-21 08:05:18 +020046
Nils Diewald0e6992a2015-04-14 20:13:52 +000047 // The doc is already set in the group
48 _duplicate : function (operand) {
Akronb19803c2018-08-16 16:39:42 +020049
50 // TODO:
51 // Also check for duplicate docGroupRefs!
Nils Diewald0e6992a2015-04-14 20:13:52 +000052 if (operand.ldType() !== 'doc')
Akron0b489ad2018-02-02 16:49:32 +010053 return null;
Nils Diewald0e6992a2015-04-14 20:13:52 +000054
Akronb50964a2020-10-12 11:44:37 +020055 const f = this._operands.find(
56 op =>
57 op.ldType() === 'doc'
58 && operand.key() === op.key()
59 && operand.matchop() === op.matchop()
60 && operand.value() === op.value()
61 );
62
63 if (f)
64 return f;
65
Nils Diewald0e6992a2015-04-14 20:13:52 +000066 return null;
67 },
68
Akron88d237e2020-10-21 08:05:18 +020069
Nils Diewald0e6992a2015-04-14 20:13:52 +000070 append : function (operand) {
71
72 // Append unspecified object
73 if (operand === undefined) {
74
Akron0b489ad2018-02-02 16:49:32 +010075 // Be aware of cyclic structures!
76 operand = unspecClass.create(this);
77 this._operands.push(operand);
78 return operand;
Nils Diewald0e6992a2015-04-14 20:13:52 +000079 };
80
81 switch (operand["@type"]) {
Akron0b489ad2018-02-02 16:49:32 +010082
Nils Diewald0e6992a2015-04-14 20:13:52 +000083 case undefined:
Akron0b489ad2018-02-02 16:49:32 +010084 // No @type defined
85 if (operand["ldType"] !== undefined) {
86 if (operand.ldType() !== 'doc' &&
Akronb19803c2018-08-16 16:39:42 +020087 operand.ldType() !== 'docGroup' &&
88 operand.ldType() !== 'docGroupRef') {
Akron0b489ad2018-02-02 16:49:32 +010089 KorAP.log(812, "Operand not supported in document group");
90 return;
91 };
Nils Diewald0e6992a2015-04-14 20:13:52 +000092
Akron0b489ad2018-02-02 16:49:32 +010093 // Be aware of cyclic structures!
94 operand.parent(this);
Nils Diewald0e6992a2015-04-14 20:13:52 +000095
Akron88d237e2020-10-21 08:05:18 +020096 const dupl = this._duplicate(operand);
Akron0b489ad2018-02-02 16:49:32 +010097 if (dupl === null) {
98 this._operands.push(operand);
99 return operand;
100 };
101 return dupl;
102 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000103
Akron0b489ad2018-02-02 16:49:32 +0100104 KorAP.log(701, "JSON-LD group has no @type attribute");
105 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000106
107 case "koral:doc":
Akron0b489ad2018-02-02 16:49:32 +0100108 // Be aware of cyclic structures!
Akron88d237e2020-10-21 08:05:18 +0200109 const doc = docClass.create(this, operand);
Akron0b489ad2018-02-02 16:49:32 +0100110 if (doc === undefined)
111 return;
Akron88d237e2020-10-21 08:05:18 +0200112
113 const dupl = this._duplicate(doc);
Akron0b489ad2018-02-02 16:49:32 +0100114 if (dupl === null) {
115 this._operands.push(doc);
116 return doc;
117 };
118 return dupl;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000119
120 case "koral:docGroup":
Akron88d237e2020-10-21 08:05:18 +0200121
122 // Be aware of cyclic structures!
123 const docGroup = docGroupClass.create(this, operand);
Akron0b489ad2018-02-02 16:49:32 +0100124 if (docGroup === undefined)
125 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000126
Akron0b489ad2018-02-02 16:49:32 +0100127 // Flatten group
128 if (docGroup.operation() === this.operation()) {
Akron678c26f2020-10-09 08:52:50 +0200129 docGroup.operands().forEach(function(op) {
Akron88d237e2020-10-21 08:05:18 +0200130 const dupl = this._duplicate(op);
Akron0b489ad2018-02-02 16:49:32 +0100131 if (dupl === null) {
132 this._operands.push(op);
133 op.parent(this);
134 };
Akron678c26f2020-10-09 08:52:50 +0200135 }, this);
Akron0b489ad2018-02-02 16:49:32 +0100136 docGroup._operands = [];
137 docGroup.destroy();
138 return this;
139 };
140 this._operands.push(docGroup);
141 return docGroup;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000142
Akronb19803c2018-08-16 16:39:42 +0200143 case "koral:docGroupRef":
144
Akron88d237e2020-10-21 08:05:18 +0200145 const docGroupRef = docGroupRefClass.create(this, operand);
Akronb19803c2018-08-16 16:39:42 +0200146
Akron88d237e2020-10-21 08:05:18 +0200147 if (docGroupRef === undefined)
Akronb19803c2018-08-16 16:39:42 +0200148 return
Akronb19803c2018-08-16 16:39:42 +0200149
150 // TODO:
151 // Currently this doesn't do anything meaningful,
152 // as duplicate only checks on docs for the moment
153 /*
154 var dupl = this._duplicate(doc);
155 if (dupl === null) {
156 this._operands.push(doc);
157 return doc;
158 };
159 return dupl;
160 */
161 this._operands.push(docGroupRef);
162 return docGroupRef;
163
Nils Diewald0e6992a2015-04-14 20:13:52 +0000164 default:
Akron0b489ad2018-02-02 16:49:32 +0100165 KorAP.log(812, "Operand not supported in document group");
166 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000167 };
168 },
169
Akron88d237e2020-10-21 08:05:18 +0200170
Nils Diewald0e6992a2015-04-14 20:13:52 +0000171 update : function () {
Akron88d237e2020-10-21 08:05:18 +0200172 const t = this;
173
Nils Diewald0e6992a2015-04-14 20:13:52 +0000174 // There is only one operand in group
175
Akron88d237e2020-10-21 08:05:18 +0200176 if (t._operands.length === 1) {
Akron0b489ad2018-02-02 16:49:32 +0100177
Akron88d237e2020-10-21 08:05:18 +0200178 const parent = t.parent();
179 const op = t.getOperand(0);
Akron0b489ad2018-02-02 16:49:32 +0100180
181 // This will prevent destruction of
182 // the operand
Akron88d237e2020-10-21 08:05:18 +0200183 t._operands = [];
Nils Diewald0e6992a2015-04-14 20:13:52 +0000184
Akron0b489ad2018-02-02 16:49:32 +0100185 // Parent is a group
186 if (parent.ldType() !== null)
Akron88d237e2020-10-21 08:05:18 +0200187 return parent.replaceOperand(t, op).update();
Nils Diewald0e6992a2015-04-14 20:13:52 +0000188
Akron0b489ad2018-02-02 16:49:32 +0100189 // Parent is vc
190 else {
Akron88d237e2020-10-21 08:05:18 +0200191 t.destroy();
Akron0b489ad2018-02-02 16:49:32 +0100192 // Cyclic madness
193 parent.root(op);
194 op.parent(parent);
195 return parent.root();
196 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000197 };
198
Akron88d237e2020-10-21 08:05:18 +0200199 if (t._element === undefined)
200 return t;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000201
Akron88d237e2020-10-21 08:05:18 +0200202 const group = t._element;
203 group.setAttribute('data-operation', t.operation());
Nils Diewald0e6992a2015-04-14 20:13:52 +0000204
205 _removeChildren(group);
206
207 // Append operands
Akron88d237e2020-10-21 08:05:18 +0200208 t._operands.forEach(
Akronb50964a2020-10-12 11:44:37 +0200209 op => group.appendChild(op.element())
210 );
Nils Diewald0e6992a2015-04-14 20:13:52 +0000211
212 // Set operators
Akron88d237e2020-10-21 08:05:18 +0200213 var op = t.operators(
214 t.operation() == 'and' ? false : true,
215 t.operation() == 'or' ? false : true,
Akron0b489ad2018-02-02 16:49:32 +0100216 true
Nils Diewald0e6992a2015-04-14 20:13:52 +0000217 );
218
219 group.appendChild(op.element());
220
Akron13af2f42019-07-25 15:06:21 +0200221 if (KorAP.vc) {
Akron88d237e2020-10-21 08:05:18 +0200222 KorAP.vc.element().dispatchEvent(
223 new CustomEvent('vcChange', {'detail' : t})
224 );
Akron13af2f42019-07-25 15:06:21 +0200225 };
hebastade595b42019-02-05 13:53:10 +0100226
Akron88d237e2020-10-21 08:05:18 +0200227 return t;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000228 },
229
Akron88d237e2020-10-21 08:05:18 +0200230
Nils Diewald0e6992a2015-04-14 20:13:52 +0000231 element : function () {
Akron88d237e2020-10-21 08:05:18 +0200232 const t = this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000233
Akron88d237e2020-10-21 08:05:18 +0200234 if (t._element !== undefined)
235 return t._element;
236
237 const e = t._element = document.createElement('div');
238 e.setAttribute('class', 'docGroup');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000239
240 // Update the object - including optimization
Akron88d237e2020-10-21 08:05:18 +0200241 t.update();
Nils Diewald0e6992a2015-04-14 20:13:52 +0000242
Akron88d237e2020-10-21 08:05:18 +0200243 return e;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000244 },
245
Akron88d237e2020-10-21 08:05:18 +0200246
Nils Diewald0e6992a2015-04-14 20:13:52 +0000247 operation : function (op) {
248 if (arguments.length === 1) {
Akron0b489ad2018-02-02 16:49:32 +0100249 if (_validGroupOpRE.test(op)) {
250 this._op = op;
251 }
252 else {
253 KorAP.log(810, "Unknown operation type");
254 return;
255 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000256 };
257 return this._op || 'and';
258 },
259
Akron88d237e2020-10-21 08:05:18 +0200260
Nils Diewald0e6992a2015-04-14 20:13:52 +0000261 operands : function () {
262 return this._operands;
263 },
264
Akron88d237e2020-10-21 08:05:18 +0200265
Nils Diewald0e6992a2015-04-14 20:13:52 +0000266 getOperand : function (index) {
267 return this._operands[index];
268 },
269
Akron88d237e2020-10-21 08:05:18 +0200270
Nils Diewald0e6992a2015-04-14 20:13:52 +0000271 // Replace operand
272 replaceOperand : function (oldOp, newOp) {
273
Akron88d237e2020-10-21 08:05:18 +0200274 for (let i = 0; i < this._operands.length; i++) {
275
Akron0b489ad2018-02-02 16:49:32 +0100276 if (this._operands[i] === oldOp) {
277
278 // Just insert a doc or ...
279 if (newOp.ldType() === "doc" ||
280 newOp.ldType() === "non" ||
Akronb19803c2018-08-16 16:39:42 +0200281 newOp.ldType() === 'docGroupRef' ||
Akron0b489ad2018-02-02 16:49:32 +0100282 // ... insert a group of a different operation
283 // (i.e. "and" in "or"/"or" in "and")
284 newOp.operation() != this.operation()) {
285 this._operands[i] = newOp;
286 newOp.parent(this);
287 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000288
Akron0b489ad2018-02-02 16:49:32 +0100289 // Flatten group
290 else {
Akron88d237e2020-10-21 08:05:18 +0200291
292 // Remove old group
Akron0b489ad2018-02-02 16:49:32 +0100293 this._operands.splice(i, 1);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000294
Akron0b489ad2018-02-02 16:49:32 +0100295 // Inject new operands
Akron88d237e2020-10-21 08:05:18 +0200296 newOp.operands().reverse().forEach(
297 function(op) {
298 this._operands.splice(i, 0, op);
299 op.parent(this);
300 },
301 this
302 );
303
Akron0b489ad2018-02-02 16:49:32 +0100304 // Prevent destruction of operands
305 newOp._operands = [];
306 newOp.destroy();
307 };
Akron88d237e2020-10-21 08:05:18 +0200308
Akron0b489ad2018-02-02 16:49:32 +0100309 oldOp.destroy();
310 return this;
311 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000312 };
313 return false;
314 },
315
Akron88d237e2020-10-21 08:05:18 +0200316
Nils Diewald0e6992a2015-04-14 20:13:52 +0000317 // Delete operand from group
318 delOperand : function (obj) {
Akron88d237e2020-10-21 08:05:18 +0200319
320 for (let i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +0100321 if (this._operands[i] === obj) {
322
323 // Delete identified operand
324 this._operands.splice(i,1);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000325
Akron0b489ad2018-02-02 16:49:32 +0100326 // Destroy object for cyclic references
327 obj.destroy();
Nils Diewald0e6992a2015-04-14 20:13:52 +0000328
Akron0b489ad2018-02-02 16:49:32 +0100329 return this;
330 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000331 };
332
333 // Operand not found
334 return undefined;
335 },
336
Akron88d237e2020-10-21 08:05:18 +0200337
Nils Diewald0e6992a2015-04-14 20:13:52 +0000338 // Deserialize from json
339 fromJson : function (json) {
Akron88d237e2020-10-21 08:05:18 +0200340
Nils Diewald0e6992a2015-04-14 20:13:52 +0000341 if (json === undefined)
Akron0b489ad2018-02-02 16:49:32 +0100342 return this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000343
344 if (json["@type"] === undefined) {
Akron0b489ad2018-02-02 16:49:32 +0100345 KorAP.log(701, "JSON-LD group has no @type attribute");
346 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000347 };
348
349 if (json["operation"] === undefined ||
Akron0b489ad2018-02-02 16:49:32 +0100350 typeof json["operation"] !== 'string') {
351 KorAP.log(811, "Document group expects operation");
352 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000353 };
354
Akron88d237e2020-10-21 08:05:18 +0200355 const operation = json["operation"];
Nils Diewald0e6992a2015-04-14 20:13:52 +0000356
357 this.operation(operation.replace(/^operation:/,''));
358
359 if (json["operands"] === undefined ||
Akron0b489ad2018-02-02 16:49:32 +0100360 !(json["operands"] instanceof Array)) {
361 KorAP.log(704, "Operation needs operand list")
362 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000363 };
364
365 // Add all documents
Akron678c26f2020-10-09 08:52:50 +0200366 json["operands"].forEach(i => this.append(i));
Nils Diewald0e6992a2015-04-14 20:13:52 +0000367
368 return this;
369 },
370
Akron88d237e2020-10-21 08:05:18 +0200371
Nils Diewald0e6992a2015-04-14 20:13:52 +0000372 toJson : function () {
Akron88d237e2020-10-21 08:05:18 +0200373 const opArray = new Array();
Akronb50964a2020-10-12 11:44:37 +0200374 this._operands.forEach(function(op) {
375 if (op.ldType() !== 'non')
376 opArray.push(op.toJson());
377 });
Akron88d237e2020-10-21 08:05:18 +0200378
Nils Diewald0e6992a2015-04-14 20:13:52 +0000379 return {
Akron0b489ad2018-02-02 16:49:32 +0100380 "@type" : "koral:" + this.ldType(),
381 "operation" : "operation:" + this.operation(),
382 "operands" : opArray
Nils Diewald0e6992a2015-04-14 20:13:52 +0000383 };
384 },
385
Akron88d237e2020-10-21 08:05:18 +0200386
Nils Diewald0e6992a2015-04-14 20:13:52 +0000387 toQuery : function (brackets) {
388 var list = this._operands
Akron0b489ad2018-02-02 16:49:32 +0100389 .filter(function (op) {
hebastaa0282be2018-12-05 16:58:00 +0100390 return !op.incomplete();
Akron0b489ad2018-02-02 16:49:32 +0100391 })
392 .map(function (op) {
393 return (op.ldType() === 'docGroup') ?
394 op.toQuery(true) :
395 op.toQuery();
396 });
Nils Diewald0e6992a2015-04-14 20:13:52 +0000397
398 if (list.length === 1)
Akron0b489ad2018-02-02 16:49:32 +0100399 return list.join('');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000400 else {
Akron88d237e2020-10-21 08:05:18 +0200401 const str = list.join(this.operation() === 'or' ? ' | ' : ' & ');
Akron0b489ad2018-02-02 16:49:32 +0100402 return brackets ? '(' + str + ')' : str;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000403 };
404 }
405 };
Akron88d237e2020-10-21 08:05:18 +0200406
Nils Diewald0e6992a2015-04-14 20:13:52 +0000407 return docGroupClass;
408});