blob: 3a22034262576ee7e1ba5e4db1253e1d0e4f2c36 [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) {
33 for (var i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +010034 if (this._operands[i] === obj) {
35 var operand = unspecClass.create(this);
36 this._operands.splice(i + 1, 0, operand);
37 return this.update();
38 };
Nils Diewald0e6992a2015-04-14 20:13:52 +000039 };
40 },
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
51 for (var i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +010052 var op = this.getOperand(i);
53 if (op.ldType() === 'doc'
54 && operand.key() === op.key()
55 && operand.matchop() === op.matchop()
56 && operand.value() === op.value()) {
57 return op;
58 };
Nils Diewald0e6992a2015-04-14 20:13:52 +000059 };
60 return null;
61 },
62
63 append : function (operand) {
64
65 // Append unspecified object
66 if (operand === undefined) {
67
Akron0b489ad2018-02-02 16:49:32 +010068 // Be aware of cyclic structures!
69 operand = unspecClass.create(this);
70 this._operands.push(operand);
71 return operand;
Nils Diewald0e6992a2015-04-14 20:13:52 +000072 };
73
74 switch (operand["@type"]) {
Akron0b489ad2018-02-02 16:49:32 +010075
Nils Diewald0e6992a2015-04-14 20:13:52 +000076 case undefined:
Akron0b489ad2018-02-02 16:49:32 +010077 // No @type defined
78 if (operand["ldType"] !== undefined) {
79 if (operand.ldType() !== 'doc' &&
Akronb19803c2018-08-16 16:39:42 +020080 operand.ldType() !== 'docGroup' &&
81 operand.ldType() !== 'docGroupRef') {
Akron0b489ad2018-02-02 16:49:32 +010082 KorAP.log(812, "Operand not supported in document group");
83 return;
84 };
Nils Diewald0e6992a2015-04-14 20:13:52 +000085
Akron0b489ad2018-02-02 16:49:32 +010086 // Be aware of cyclic structures!
87 operand.parent(this);
Nils Diewald0e6992a2015-04-14 20:13:52 +000088
Akron0b489ad2018-02-02 16:49:32 +010089 var dupl = this._duplicate(operand);
90 if (dupl === null) {
91 this._operands.push(operand);
92 return operand;
93 };
94 return dupl;
95 };
Nils Diewald0e6992a2015-04-14 20:13:52 +000096
Akron0b489ad2018-02-02 16:49:32 +010097 KorAP.log(701, "JSON-LD group has no @type attribute");
98 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +000099
100 case "koral:doc":
Akron0b489ad2018-02-02 16:49:32 +0100101 // Be aware of cyclic structures!
102 var doc = docClass.create(this, operand);
103 if (doc === undefined)
104 return;
105 var dupl = this._duplicate(doc);
106 if (dupl === null) {
107 this._operands.push(doc);
108 return doc;
109 };
110 return dupl;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000111
112 case "koral:docGroup":
Akron0b489ad2018-02-02 16:49:32 +0100113 // Be aware of cyclic structures!
114 var docGroup = docGroupClass.create(this, operand);
115 if (docGroup === undefined)
116 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000117
Akron0b489ad2018-02-02 16:49:32 +0100118 // Flatten group
119 if (docGroup.operation() === this.operation()) {
120 for (var op in docGroup.operands()) {
121 op = docGroup.getOperand(op);
122 var dupl = this._duplicate(op);
123 if (dupl === null) {
124 this._operands.push(op);
125 op.parent(this);
126 };
127 };
128 docGroup._operands = [];
129 docGroup.destroy();
130 return this;
131 };
132 this._operands.push(docGroup);
133 return docGroup;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000134
Akronb19803c2018-08-16 16:39:42 +0200135 case "koral:docGroupRef":
136
137 var docGroupRef = docGroupRefClass.create(this, operand);
138
139 if (docGroupRef === undefined) {
140 return
141 };
142
143 // TODO:
144 // Currently this doesn't do anything meaningful,
145 // as duplicate only checks on docs for the moment
146 /*
147 var dupl = this._duplicate(doc);
148 if (dupl === null) {
149 this._operands.push(doc);
150 return doc;
151 };
152 return dupl;
153 */
154 this._operands.push(docGroupRef);
155 return docGroupRef;
156
Nils Diewald0e6992a2015-04-14 20:13:52 +0000157 default:
Akron0b489ad2018-02-02 16:49:32 +0100158 KorAP.log(812, "Operand not supported in document group");
159 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000160 };
161 },
162
163 update : function () {
164 // There is only one operand in group
165
166 if (this._operands.length === 1) {
Akron0b489ad2018-02-02 16:49:32 +0100167
168 var parent = this.parent();
169 var op = this.getOperand(0);
170
171 // This will prevent destruction of
172 // the operand
173 this._operands = [];
Nils Diewald0e6992a2015-04-14 20:13:52 +0000174
Akron0b489ad2018-02-02 16:49:32 +0100175 // Parent is a group
176 if (parent.ldType() !== null)
177 return parent.replaceOperand(this, op).update();
Nils Diewald0e6992a2015-04-14 20:13:52 +0000178
Akron0b489ad2018-02-02 16:49:32 +0100179 // Parent is vc
180 else {
181 this.destroy();
182 // Cyclic madness
183 parent.root(op);
184 op.parent(parent);
185 return parent.root();
186 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000187 };
188
189 if (this._element === undefined)
Akron0b489ad2018-02-02 16:49:32 +0100190 return this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000191
192 var group = this._element;
193 group.setAttribute('data-operation', this.operation());
194
195 _removeChildren(group);
196
197 // Append operands
198 for (var i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +0100199 group.appendChild(
200 this.getOperand(i).element()
201 );
Nils Diewald0e6992a2015-04-14 20:13:52 +0000202 };
203
204 // Set operators
205 var op = this.operators(
Akron0b489ad2018-02-02 16:49:32 +0100206 this.operation() == 'and' ? false : true,
207 this.operation() == 'or' ? false : true,
208 true
Nils Diewald0e6992a2015-04-14 20:13:52 +0000209 );
210
211 group.appendChild(op.element());
212
Akron13af2f42019-07-25 15:06:21 +0200213 if (KorAP.vc) {
214 var vcchevent = new CustomEvent('vcChange', {'detail':this});
215 KorAP.vc.element().dispatchEvent(vcchevent);
216 };
hebastade595b42019-02-05 13:53:10 +0100217
Nils Diewald0e6992a2015-04-14 20:13:52 +0000218 return this;
219 },
220
221 element : function () {
222 if (this._element !== undefined)
Akron0b489ad2018-02-02 16:49:32 +0100223 return this._element;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000224
225 this._element = document.createElement('div');
226 this._element.setAttribute('class', 'docGroup');
227
228 // Update the object - including optimization
229 this.update();
230
231 return this._element;
232 },
233
234 operation : function (op) {
235 if (arguments.length === 1) {
Akron0b489ad2018-02-02 16:49:32 +0100236 if (_validGroupOpRE.test(op)) {
237 this._op = op;
238 }
239 else {
240 KorAP.log(810, "Unknown operation type");
241 return;
242 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000243 };
244 return this._op || 'and';
245 },
246
247 operands : function () {
248 return this._operands;
249 },
250
251 getOperand : function (index) {
252 return this._operands[index];
253 },
254
255 // Replace operand
256 replaceOperand : function (oldOp, newOp) {
257
258 for (var i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +0100259 if (this._operands[i] === oldOp) {
260
261 // Just insert a doc or ...
262 if (newOp.ldType() === "doc" ||
263 newOp.ldType() === "non" ||
Akronb19803c2018-08-16 16:39:42 +0200264 newOp.ldType() === 'docGroupRef' ||
Akron0b489ad2018-02-02 16:49:32 +0100265 // ... insert a group of a different operation
266 // (i.e. "and" in "or"/"or" in "and")
267 newOp.operation() != this.operation()) {
268 this._operands[i] = newOp;
269 newOp.parent(this);
270 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000271
Akron0b489ad2018-02-02 16:49:32 +0100272 // Flatten group
273 else {
274 // Remove old group
275 this._operands.splice(i, 1);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000276
Akron0b489ad2018-02-02 16:49:32 +0100277 // Inject new operands
278 for (var op in newOp.operands().reverse()) {
279 op = newOp.getOperand(op);
280 this._operands.splice(i, 0, op);
281 op.parent(this);
282 };
283 // Prevent destruction of operands
284 newOp._operands = [];
285 newOp.destroy();
286 };
287 oldOp.destroy();
288 return this;
289 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000290 };
291 return false;
292 },
293
294 // Delete operand from group
295 delOperand : function (obj) {
296 for (var i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +0100297 if (this._operands[i] === obj) {
298
299 // Delete identified operand
300 this._operands.splice(i,1);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000301
Akron0b489ad2018-02-02 16:49:32 +0100302 // Destroy object for cyclic references
303 obj.destroy();
Nils Diewald0e6992a2015-04-14 20:13:52 +0000304
Akron0b489ad2018-02-02 16:49:32 +0100305 return this;
306 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000307 };
308
309 // Operand not found
310 return undefined;
311 },
312
313 // Deserialize from json
314 fromJson : function (json) {
315 if (json === undefined)
Akron0b489ad2018-02-02 16:49:32 +0100316 return this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000317
318 if (json["@type"] === undefined) {
Akron0b489ad2018-02-02 16:49:32 +0100319 KorAP.log(701, "JSON-LD group has no @type attribute");
320 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000321 };
322
323 if (json["operation"] === undefined ||
Akron0b489ad2018-02-02 16:49:32 +0100324 typeof json["operation"] !== 'string') {
325 KorAP.log(811, "Document group expects operation");
326 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000327 };
328
329 var operation = json["operation"];
330
331 this.operation(operation.replace(/^operation:/,''));
332
333 if (json["operands"] === undefined ||
Akron0b489ad2018-02-02 16:49:32 +0100334 !(json["operands"] instanceof Array)) {
335 KorAP.log(704, "Operation needs operand list")
336 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000337 };
338
339 // Add all documents
340 for (var i in json["operands"]) {
Akron0b489ad2018-02-02 16:49:32 +0100341 var operand = json["operands"][i];
342 this.append(operand);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000343 };
344
345 return this;
346 },
347
348 toJson : function () {
349 var opArray = new Array();
350 for (var i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +0100351 if (this._operands[i].ldType() !== 'non')
352 opArray.push(this._operands[i].toJson());
Nils Diewald0e6992a2015-04-14 20:13:52 +0000353 };
354 return {
Akron0b489ad2018-02-02 16:49:32 +0100355 "@type" : "koral:" + this.ldType(),
356 "operation" : "operation:" + this.operation(),
357 "operands" : opArray
Nils Diewald0e6992a2015-04-14 20:13:52 +0000358 };
359 },
360
361 toQuery : function (brackets) {
362 var list = this._operands
Akron0b489ad2018-02-02 16:49:32 +0100363 .filter(function (op) {
hebastaa0282be2018-12-05 16:58:00 +0100364 return !op.incomplete();
Akron0b489ad2018-02-02 16:49:32 +0100365 })
366 .map(function (op) {
367 return (op.ldType() === 'docGroup') ?
368 op.toQuery(true) :
369 op.toQuery();
370 });
Nils Diewald0e6992a2015-04-14 20:13:52 +0000371
372 if (list.length === 1)
Akron0b489ad2018-02-02 16:49:32 +0100373 return list.join('');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000374 else {
Akron0b489ad2018-02-02 16:49:32 +0100375 var str = list.join(this.operation() === 'or' ? ' | ' : ' & ');
376 return brackets ? '(' + str + ')' : str;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000377 };
378 }
379 };
380 return docGroupClass;
381});