blob: d3097e9534e15666564d56ae3888ce2571275837 [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,
16 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 loc = KorAP.Locale;
Nils Diewald359a72c2015-04-20 17:40:29 +000021
Akron0b489ad2018-02-02 16:49:32 +010022 const docGroupClass = {
Nils Diewald0e6992a2015-04-14 20:13:52 +000023 _ldType : "docGroup",
24
25 create : function (parent, json) {
26 var obj = Object.create(jsonldClass).upgradeTo(this);
27 obj._operands = [];
28 obj.fromJson(json);
29 if (parent !== undefined)
Akron0b489ad2018-02-02 16:49:32 +010030 obj._parent = parent;
Nils Diewald0e6992a2015-04-14 20:13:52 +000031 return obj;
32 },
33
34 newAfter : function (obj) {
35 for (var i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +010036 if (this._operands[i] === obj) {
37 var operand = unspecClass.create(this);
38 this._operands.splice(i + 1, 0, operand);
39 return this.update();
40 };
Nils Diewald0e6992a2015-04-14 20:13:52 +000041 };
42 },
43
44 // The doc is already set in the group
45 _duplicate : function (operand) {
Akronb19803c2018-08-16 16:39:42 +020046
47 // TODO:
48 // Also check for duplicate docGroupRefs!
49
Nils Diewald0e6992a2015-04-14 20:13:52 +000050 if (operand.ldType() !== 'doc')
Akron0b489ad2018-02-02 16:49:32 +010051 return null;
Nils Diewald0e6992a2015-04-14 20:13:52 +000052
53 for (var i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +010054 var op = this.getOperand(i);
55 if (op.ldType() === 'doc'
56 && operand.key() === op.key()
57 && operand.matchop() === op.matchop()
58 && operand.value() === op.value()) {
59 return op;
60 };
Nils Diewald0e6992a2015-04-14 20:13:52 +000061 };
62 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()) {
122 for (var op in docGroup.operands()) {
123 op = docGroup.getOperand(op);
124 var dupl = this._duplicate(op);
125 if (dupl === null) {
126 this._operands.push(op);
127 op.parent(this);
128 };
129 };
130 docGroup._operands = [];
131 docGroup.destroy();
132 return this;
133 };
134 this._operands.push(docGroup);
135 return docGroup;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000136
Akronb19803c2018-08-16 16:39:42 +0200137 case "koral:docGroupRef":
138
139 var docGroupRef = docGroupRefClass.create(this, operand);
140
141 if (docGroupRef === undefined) {
142 return
143 };
144
145 // TODO:
146 // Currently this doesn't do anything meaningful,
147 // as duplicate only checks on docs for the moment
148 /*
149 var dupl = this._duplicate(doc);
150 if (dupl === null) {
151 this._operands.push(doc);
152 return doc;
153 };
154 return dupl;
155 */
156 this._operands.push(docGroupRef);
157 return docGroupRef;
158
Nils Diewald0e6992a2015-04-14 20:13:52 +0000159 default:
Akron0b489ad2018-02-02 16:49:32 +0100160 KorAP.log(812, "Operand not supported in document group");
161 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000162 };
163 },
164
165 update : function () {
166 // There is only one operand in group
167
168 if (this._operands.length === 1) {
Akron0b489ad2018-02-02 16:49:32 +0100169
170 var parent = this.parent();
171 var op = this.getOperand(0);
172
173 // This will prevent destruction of
174 // the operand
175 this._operands = [];
Nils Diewald0e6992a2015-04-14 20:13:52 +0000176
Akron0b489ad2018-02-02 16:49:32 +0100177 // Parent is a group
178 if (parent.ldType() !== null)
179 return parent.replaceOperand(this, op).update();
Nils Diewald0e6992a2015-04-14 20:13:52 +0000180
Akron0b489ad2018-02-02 16:49:32 +0100181 // Parent is vc
182 else {
183 this.destroy();
184 // Cyclic madness
185 parent.root(op);
186 op.parent(parent);
187 return parent.root();
188 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000189 };
190
191 if (this._element === undefined)
Akron0b489ad2018-02-02 16:49:32 +0100192 return this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000193
194 var group = this._element;
195 group.setAttribute('data-operation', this.operation());
196
197 _removeChildren(group);
198
199 // Append operands
200 for (var i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +0100201 group.appendChild(
202 this.getOperand(i).element()
203 );
Nils Diewald0e6992a2015-04-14 20:13:52 +0000204 };
205
206 // Set operators
207 var op = this.operators(
Akron0b489ad2018-02-02 16:49:32 +0100208 this.operation() == 'and' ? false : true,
209 this.operation() == 'or' ? false : true,
210 true
Nils Diewald0e6992a2015-04-14 20:13:52 +0000211 );
212
213 group.appendChild(op.element());
214
215 return this;
216 },
217
218 element : function () {
219 if (this._element !== undefined)
Akron0b489ad2018-02-02 16:49:32 +0100220 return this._element;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000221
222 this._element = document.createElement('div');
223 this._element.setAttribute('class', 'docGroup');
224
225 // Update the object - including optimization
226 this.update();
227
228 return this._element;
229 },
230
231 operation : function (op) {
232 if (arguments.length === 1) {
Akron0b489ad2018-02-02 16:49:32 +0100233 if (_validGroupOpRE.test(op)) {
234 this._op = op;
235 }
236 else {
237 KorAP.log(810, "Unknown operation type");
238 return;
239 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000240 };
241 return this._op || 'and';
242 },
243
244 operands : function () {
245 return this._operands;
246 },
247
248 getOperand : function (index) {
249 return this._operands[index];
250 },
251
252 // Replace operand
253 replaceOperand : function (oldOp, newOp) {
254
255 for (var i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +0100256 if (this._operands[i] === oldOp) {
257
258 // Just insert a doc or ...
259 if (newOp.ldType() === "doc" ||
260 newOp.ldType() === "non" ||
Akronb19803c2018-08-16 16:39:42 +0200261 newOp.ldType() === 'docGroupRef' ||
Akron0b489ad2018-02-02 16:49:32 +0100262 // ... insert a group of a different operation
263 // (i.e. "and" in "or"/"or" in "and")
264 newOp.operation() != this.operation()) {
265 this._operands[i] = newOp;
266 newOp.parent(this);
267 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000268
Akron0b489ad2018-02-02 16:49:32 +0100269 // Flatten group
270 else {
271 // Remove old group
272 this._operands.splice(i, 1);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000273
Akron0b489ad2018-02-02 16:49:32 +0100274 // Inject new operands
275 for (var op in newOp.operands().reverse()) {
276 op = newOp.getOperand(op);
277 this._operands.splice(i, 0, op);
278 op.parent(this);
279 };
280 // Prevent destruction of operands
281 newOp._operands = [];
282 newOp.destroy();
283 };
284 oldOp.destroy();
285 return this;
286 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000287 };
288 return false;
289 },
290
291 // Delete operand from group
292 delOperand : function (obj) {
293 for (var i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +0100294 if (this._operands[i] === obj) {
295
296 // Delete identified operand
297 this._operands.splice(i,1);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000298
Akron0b489ad2018-02-02 16:49:32 +0100299 // Destroy object for cyclic references
300 obj.destroy();
Nils Diewald0e6992a2015-04-14 20:13:52 +0000301
Akron0b489ad2018-02-02 16:49:32 +0100302 return this;
303 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000304 };
305
306 // Operand not found
307 return undefined;
308 },
309
310 // Deserialize from json
311 fromJson : function (json) {
312 if (json === undefined)
Akron0b489ad2018-02-02 16:49:32 +0100313 return this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000314
315 if (json["@type"] === undefined) {
Akron0b489ad2018-02-02 16:49:32 +0100316 KorAP.log(701, "JSON-LD group has no @type attribute");
317 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000318 };
319
320 if (json["operation"] === undefined ||
Akron0b489ad2018-02-02 16:49:32 +0100321 typeof json["operation"] !== 'string') {
322 KorAP.log(811, "Document group expects operation");
323 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000324 };
325
326 var operation = json["operation"];
327
328 this.operation(operation.replace(/^operation:/,''));
329
330 if (json["operands"] === undefined ||
Akron0b489ad2018-02-02 16:49:32 +0100331 !(json["operands"] instanceof Array)) {
332 KorAP.log(704, "Operation needs operand list")
333 return;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000334 };
335
336 // Add all documents
337 for (var i in json["operands"]) {
Akron0b489ad2018-02-02 16:49:32 +0100338 var operand = json["operands"][i];
339 this.append(operand);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000340 };
341
342 return this;
343 },
344
345 toJson : function () {
346 var opArray = new Array();
347 for (var i = 0; i < this._operands.length; i++) {
Akron0b489ad2018-02-02 16:49:32 +0100348 if (this._operands[i].ldType() !== 'non')
349 opArray.push(this._operands[i].toJson());
Nils Diewald0e6992a2015-04-14 20:13:52 +0000350 };
351 return {
Akron0b489ad2018-02-02 16:49:32 +0100352 "@type" : "koral:" + this.ldType(),
353 "operation" : "operation:" + this.operation(),
354 "operands" : opArray
Nils Diewald0e6992a2015-04-14 20:13:52 +0000355 };
356 },
357
358 toQuery : function (brackets) {
359 var list = this._operands
Akron0b489ad2018-02-02 16:49:32 +0100360 .filter(function (op) {
361 return op.ldType() !== 'non';
362 })
363 .map(function (op) {
364 return (op.ldType() === 'docGroup') ?
365 op.toQuery(true) :
366 op.toQuery();
367 });
Nils Diewald0e6992a2015-04-14 20:13:52 +0000368
369 if (list.length === 1)
Akron0b489ad2018-02-02 16:49:32 +0100370 return list.join('');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000371 else {
Akron0b489ad2018-02-02 16:49:32 +0100372 var str = list.join(this.operation() === 'or' ? ' | ' : ' & ');
373 return brackets ? '(' + str + ')' : str;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000374 };
375 }
376 };
377 return docGroupClass;
378});