blob: da1f6abf04d6d37edcc8f388a49becff4205ea1c [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',
11 'util'
12], function (jsonldClass,
13 unspecClass,
14 docClass) {
15
16 var _validGroupOpRE = new RegExp("^(?:and|or)$");
17
Nils Diewald359a72c2015-04-20 17:40:29 +000018 var loc = KorAP.Locale;
19
Nils Diewald0e6992a2015-04-14 20:13:52 +000020 var docGroupClass = {
21 _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)
28 obj._parent = parent;
29 return obj;
30 },
31
32 newAfter : function (obj) {
33 for (var i = 0; i < this._operands.length; i++) {
34 if (this._operands[i] === obj) {
35 var operand = unspecClass.create(this);
36 this._operands.splice(i + 1, 0, operand);
37 return this.update();
38 };
39 };
40 },
41
42 // The doc is already set in the group
43 _duplicate : function (operand) {
44 if (operand.ldType() !== 'doc')
45 return null;
46
47 for (var i = 0; i < this._operands.length; i++) {
48 var op = this.getOperand(i);
49 if (op.ldType() === 'doc'
50 && operand.key() === op.key()
51 && operand.matchop() === op.matchop()
52 && operand.value() === op.value()) {
53 return op;
54 };
55 };
56 return null;
57 },
58
59 append : function (operand) {
60
61 // Append unspecified object
62 if (operand === undefined) {
63
64 // Be aware of cyclic structures!
65 operand = unspecClass.create(this);
66 this._operands.push(operand);
67 return operand;
68 };
69
70 switch (operand["@type"]) {
71
72 case undefined:
73 // No @type defined
74 if (operand["ldType"] !== undefined) {
75 if (operand.ldType() !== 'doc' &&
76 operand.ldType() !== 'docGroup') {
77 KorAP.log(812, "Operand not supported in document group");
78 return;
79 };
80
81 // Be aware of cyclic structures!
82 operand.parent(this);
83
84 var dupl = this._duplicate(operand);
85 if (dupl === null) {
86 this._operands.push(operand);
87 return operand;
88 };
89 return dupl;
90 };
91
92 KorAP.log(701, "JSON-LD group has no @type attribute");
93 return;
94
95 case "koral:doc":
96 // Be aware of cyclic structures!
97 var doc = docClass.create(this, operand);
98 if (doc === undefined)
99 return;
100 var dupl = this._duplicate(doc);
101 if (dupl === null) {
102 this._operands.push(doc);
103 return doc;
104 };
105 return dupl;
106
107 case "koral:docGroup":
108 // Be aware of cyclic structures!
109 var docGroup = docGroupClass.create(this, operand);
110 if (docGroup === undefined)
111 return;
112
113 // Flatten group
114 if (docGroup.operation() === this.operation()) {
115 for (var op in docGroup.operands()) {
116 op = docGroup.getOperand(op);
117 var dupl = this._duplicate(op);
118 if (dupl === null) {
119 this._operands.push(op);
120 op.parent(this);
121 };
122 };
123 docGroup._operands = [];
124 docGroup.destroy();
125 return this;
126 };
127 this._operands.push(docGroup);
128 return docGroup;
129
130 default:
131 KorAP.log(812, "Operand not supported in document group");
132 return;
133 };
134 },
135
136 update : function () {
137 // There is only one operand in group
138
139 if (this._operands.length === 1) {
140
141 var parent = this.parent();
142 var op = this.getOperand(0);
143
144 // This will prevent destruction of
145 // the operand
146 this._operands = [];
147
148 // Parent is a group
149 if (parent.ldType() !== null)
150 return parent.replaceOperand(this, op).update();
151
152 // Parent is vc
153 else {
154 this.destroy();
155 // Cyclic madness
156 parent.root(op);
157 op.parent(parent);
158 return parent.root();
159 };
160 };
161
162 if (this._element === undefined)
163 return this;
164
165 var group = this._element;
166 group.setAttribute('data-operation', this.operation());
167
168 _removeChildren(group);
169
170 // Append operands
171 for (var i = 0; i < this._operands.length; i++) {
172 group.appendChild(
173 this.getOperand(i).element()
174 );
175 };
176
177 // Set operators
178 var op = this.operators(
179 this.operation() == 'and' ? false : true,
180 this.operation() == 'or' ? false : true,
181 true
182 );
183
184 group.appendChild(op.element());
185
186 return this;
187 },
188
189 element : function () {
190 if (this._element !== undefined)
191 return this._element;
192
193 this._element = document.createElement('div');
194 this._element.setAttribute('class', 'docGroup');
195
196 // Update the object - including optimization
197 this.update();
198
199 return this._element;
200 },
201
202 operation : function (op) {
203 if (arguments.length === 1) {
204 if (_validGroupOpRE.test(op)) {
205 this._op = op;
206 }
207 else {
208 KorAP.log(810, "Unknown operation type");
209 return;
210 };
211 };
212 return this._op || 'and';
213 },
214
215 operands : function () {
216 return this._operands;
217 },
218
219 getOperand : function (index) {
220 return this._operands[index];
221 },
222
223 // Replace operand
224 replaceOperand : function (oldOp, newOp) {
225
226 for (var i = 0; i < this._operands.length; i++) {
227 if (this._operands[i] === oldOp) {
228
229 // Just insert a doc or ...
230 if (newOp.ldType() === "doc" ||
231 newOp.ldType() === "non" ||
232 // ... insert a group of a different operation
233 // (i.e. "and" in "or"/"or" in "and")
234 newOp.operation() != this.operation()) {
235 this._operands[i] = newOp;
236 newOp.parent(this);
237 }
238
239 // Flatten group
240 else {
241 // Remove old group
242 this._operands.splice(i, 1);
243
244 // Inject new operands
245 for (var op in newOp.operands().reverse()) {
246 op = newOp.getOperand(op);
247 this._operands.splice(i, 0, op);
248 op.parent(this);
249 };
250 // Prevent destruction of operands
251 newOp._operands = [];
252 newOp.destroy();
253 };
254 oldOp.destroy();
255 return this;
256 }
257 };
258 return false;
259 },
260
261 // Delete operand from group
262 delOperand : function (obj) {
263 for (var i = 0; i < this._operands.length; i++) {
264 if (this._operands[i] === obj) {
265
266 // Delete identified operand
267 this._operands.splice(i,1);
268
269 // Destroy object for cyclic references
270 obj.destroy();
271
272 return this;
273 };
274 };
275
276 // Operand not found
277 return undefined;
278 },
279
280 // Deserialize from json
281 fromJson : function (json) {
282 if (json === undefined)
283 return this;
284
285 if (json["@type"] === undefined) {
286 KorAP.log(701, "JSON-LD group has no @type attribute");
287 return;
288 };
289
290 if (json["operation"] === undefined ||
291 typeof json["operation"] !== 'string') {
292 KorAP.log(811, "Document group expects operation");
293 return;
294 };
295
296 var operation = json["operation"];
297
298 this.operation(operation.replace(/^operation:/,''));
299
300 if (json["operands"] === undefined ||
301 !(json["operands"] instanceof Array)) {
302 KorAP.log(704, "Operation needs operand list")
303 return;
304 };
305
306 // Add all documents
307 for (var i in json["operands"]) {
308 var operand = json["operands"][i];
309 this.append(operand);
310 };
311
312 return this;
313 },
314
315 toJson : function () {
316 var opArray = new Array();
317 for (var i = 0; i < this._operands.length; i++) {
318 if (this._operands[i].ldType() !== 'non')
319 opArray.push(this._operands[i].toJson());
320 };
321 return {
322 "@type" : "koral:" + this.ldType(),
323 "operation" : "operation:" + this.operation(),
324 "operands" : opArray
325 };
326 },
327
328 toQuery : function (brackets) {
329 var list = this._operands
330 .filter(function (op) {
331 return op.ldType() !== 'non';
332 })
333 .map(function (op) {
334 return (op.ldType() === 'docGroup') ?
335 op.toQuery(true) :
336 op.toQuery();
337 });
338
339 if (list.length === 1)
340 return list.join('');
341 else {
342 var str = list.join(this.operation() === 'or' ? ' | ' : ' & ');
343 return brackets ? '(' + str + ')' : str;
344 };
345 }
346 };
347 return docGroupClass;
348});