blob: 35734adaa61a6ed9b0360a2b82449044b5a94507 [file] [log] [blame]
Nils Diewald86dad5b2015-01-28 15:09:07 +00001/**
hebastaa79d69d2018-07-24 12:13:02 +02002 * Create virtual collections with a visual user interface. This resembles the
3 * collection type objects of a KoralQuery "collection" object.
4 *
Nils Diewald4c221252015-04-21 20:19:25 +00005 * KoralQuery v0.3 is expected.
hebastaa79d69d2018-07-24 12:13:02 +02006 *
Nils Diewald86dad5b2015-01-28 15:09:07 +00007 * @author Nils Diewald
8 */
Nils Diewald2fe12e12015-03-06 16:47:06 +00009/*
Nils Diewald1fcb2ad2015-04-20 19:19:18 +000010 * This replaces a previous version written by Mengfei Zhou
Nils Diewald2fe12e12015-03-06 16:47:06 +000011 */
Nils Diewald2fe12e12015-03-06 16:47:06 +000012
Nils Diewaldd0770492014-12-19 03:55:00 +000013/*
hebasta86759392018-07-25 15:44:37 +020014 TODO: Disable "and" or "or" in case it's followed
15 by an unspecified document
16 TODO: Add "and"-method to root to add further constraints
17 based on match-input (like clicking on a pubDate timestamp in a match)
18 TODO: Implement "persistence"-Option, injecting the current creation
19 date stamp
20 TODO: Implement vec-Type for document-id vectors like docID in [1,2,3,4 ...]
21
22 Error codes:
23 701: "JSON-LD group has no @type attribute"
24 704: "Operation needs operand list"
25 802: "Match type is not supported by value type"
26 804: "Unknown value type"
27 805: "Value is invalid"
28 806: "Value is not a valid date string"
29 807: "Value is not a valid regular expression"
30 810: "Unknown document group operation" (like 711)
31 811: "Document group expects operation" (like 703)
32 812: "Operand not supported in document group" (like 744)
33 813: "Collection type is not supported" (like 713)
34 814: "Unknown rewrite operation"
35 815: "Rewrite expects source"
36
37 Localization strings:
38 KorAP.Locale = {
39 EMPTY : '...',
40 AND : 'and',
41 OR : 'or',
42 DELETE : 'x' }
43
44 and various field names with the prefix 'VC_'
hebastaa79d69d2018-07-24 12:13:02 +020045 */
Nils Diewald86dad5b2015-01-28 15:09:07 +000046
Akronb19803c2018-08-16 16:39:42 +020047define([
48 'vc/unspecified',
49 'vc/doc',
50 'vc/docgroup',
51 'vc/docgroupref',
52 'vc/menu',
53 'vc/statistic',
54 'datepicker',
55 'buttongroup',
56 'panel',
57 'view/corpstatv',
58 'util'
59], function(
60 unspecDocClass,
61 docClass,
62 docGroupClass,
63 docGroupRefClass,
64 menuClass,
65 statClass,
66 dpClass,
67 buttonGrClass,
68 panelClass,
69 corpStatVClass) {
70 "use strict";
Nils Diewald1fcb2ad2015-04-20 19:19:18 +000071
Akronb19803c2018-08-16 16:39:42 +020072 KorAP._validUnspecMatchRE = new RegExp(
73 "^(?:eq|ne|contains(?:not)?|excludes)$");
74 KorAP._validStringMatchRE = new RegExp("^(?:eq|ne)$");
75 KorAP._validTextMatchRE = KorAP._validUnspecMatchRE;
76 KorAP._validTextOnlyMatchRE = new RegExp(
77 "^(?:contains(?:not)?|excludes)$");
78 KorAP._overrideStyles = false;
79 // KorAP._validDateMatchRE is defined in datepicker.js!
Nils Diewaldd0770492014-12-19 03:55:00 +000080
Akronb19803c2018-08-16 16:39:42 +020081 const loc = KorAP.Locale;
82 loc.SHOW_STAT = loc.SHOW_STAT || 'Statistics';
83 loc.VERB_SHOWSTAT = loc.VERB_SHOWSTAT || 'Corpus Statistics';
Akron8a670162018-08-28 10:09:13 +020084 loc.VC_allCorpora = loc.VC_allCorpora || 'all corpora';
85 loc.VC_oneCollection = loc.VC_oneCollection || 'a virtual corpus';
Nils Diewald3a2d8022014-12-16 02:45:41 +000086
Akronb19803c2018-08-16 16:39:42 +020087 KorAP._vcKeyMenu = undefined;
88 KorAP._vcDatePicker = dpClass.create();
Nils Diewald3a2d8022014-12-16 02:45:41 +000089
Akronb19803c2018-08-16 16:39:42 +020090 // Create match menus ....
91 KorAP._vcMatchopMenu = {
92 'string' : menuClass.create([
93 [ 'eq', null ],
94 [ 'ne', null ]
95 ]),
96 'text' : menuClass.create([
97 [ 'eq', null ], // Requires exact match
98 [ 'ne', null ],
99 [ 'contains', null ], // Requires token sequence match
100 [ 'containsnot', null ]
101 ]),
102 'date' : menuClass.create([
103 [ 'eq', null ],
104 [ 'ne', null ],
105 [ 'geq', null ],
106 [ 'leq', null ]
107 ]),
108 'regex' : menuClass.create([
109 [ 'eq', null ],
110 [ 'ne', null ]
111 ])
112 };
113
114 /**
115 * Virtual Collection
116 */
117 return {
118
119 /**
120 * The JSON-LD type of the virtual collection
121 */
122 ldType : function() {
123 return null;
124 },
125
126 // Initialize virtual collection
127 _init : function(keyList) {
128
129 // Inject localized css styles
130 if (!KorAP._overrideStyles) {
131 var sheet = KorAP.newStyleSheet();
132
133 // Add css rule for OR operations
134 sheet.insertRule('.vc .docGroup[data-operation=or] > .doc::before,'
135 + '.vc .docGroup[data-operation=or] > .docGroup::before '
136 + '{ content: "' + loc.OR + '" }', 0);
137
138 // Add css rule for AND operations
139 sheet.insertRule(
140 '.vc .docGroup[data-operation=and] > .doc::before,'
141 + '.vc .docGroup[data-operation=and] > .docGroup::before '
142 + '{ content: "' + loc.AND + '" }', 1);
143
144 KorAP._overrideStyles = true;
Nils Diewald359a72c2015-04-20 17:40:29 +0000145 };
146
Akron3ad46942018-08-22 16:47:14 +0200147 var l;
148 if (keyList) {
Akronadab5e52018-08-20 13:50:53 +0200149 l = keyList.slice();
Akron3ad46942018-08-22 16:47:14 +0200150 l.unshift(['referTo', 'ref']);
151 }
152 else {
153 l = [['referTo', 'ref']];
154 }
Akronadab5e52018-08-20 13:50:53 +0200155
Akronb19803c2018-08-16 16:39:42 +0200156 // Create key menu
Akron3ad46942018-08-22 16:47:14 +0200157 KorAP._vcKeyMenu = menuClass.create(l);
Akronb19803c2018-08-16 16:39:42 +0200158 KorAP._vcKeyMenu.limit(6);
Akron712733a2018-04-05 18:17:47 +0200159
Akronb19803c2018-08-16 16:39:42 +0200160 return this;
161 },
Nils Diewald359a72c2015-04-20 17:40:29 +0000162
Akronb19803c2018-08-16 16:39:42 +0200163 /**
164 * Create a new virtual collection.
165 */
166 create : function(keyList) {
167 var obj = Object.create(this)._init(keyList);
168 obj._root = unspecDocClass.create(obj);
169 return obj;
170 },
Nils Diewaldd599d542015-01-08 20:41:34 +0000171
Akron8a670162018-08-28 10:09:13 +0200172
Akronb19803c2018-08-16 16:39:42 +0200173 /**
174 * Create and render a new virtual collection based on a KoralQuery
175 * collection document
176 */
177 fromJson : function(json) {
178 if (json !== undefined) {
179 // Parse root document
180 if (json['@type'] == 'koral:doc') {
181 this._root = docClass.create(this, json);
hebastaa79d69d2018-07-24 12:13:02 +0200182 }
Akronb19803c2018-08-16 16:39:42 +0200183 // parse root group
184 else if (json['@type'] == 'koral:docGroup') {
185 this._root = docGroupClass.create(this, json);
186 }
187
188 // parse root reference
189 else if (json['@type'] == 'koral:docGroupRef') {
190 this._root = docGroupRefClass.create(this, json);
191 }
192
193 // Unknown collection type
194 else {
195 KorAP.log(813, "Collection type is not supported");
196 return;
197 };
198 }
199
200 else {
201 // Add unspecified object
202 this._root = unspecDocClass.create(this);
Nils Diewald845282c2015-05-14 07:53:03 +0000203 };
Akronb19803c2018-08-16 16:39:42 +0200204
205 // Init element and update
206 this.update();
207
208 return this;
209 },
Akrond2474aa2018-08-28 12:06:27 +0200210
211
212 // Check if the virtual corpus contains a rerite
213 wasRewritten : function (obj) {
214
215 var obj;
216 if (arguments.length === 1) {
217 obj = arguments[0];
218 }
219 else {
220 obj = this._root;
221 };
222
223 // Check for rewrite
224 if (obj.rewrites() && obj.rewrites().length() > 0) {
225 return true;
226 }
227
228 // Check recursively
229 else if (obj.ldType() === 'docGroup') {
230 for (var i in obj.operands()) {
231 if (this.wasRewritten(obj.getOperand(i))) {
232 return true;
233 }
234 };
235 };
236
237 return false;
238 },
Akron43c5cc62018-08-28 13:10:25 +0200239
Akron8a670162018-08-28 10:09:13 +0200240
Akronb19803c2018-08-16 16:39:42 +0200241 /**
242 * Clean the virtual document to uspecified doc.
243 */
244 clean : function() {
245 if (this._root.ldType() !== "non") {
246 this._root.destroy();
247 this.root(unspecDocClass.create(this));
248 };
249 return this;
250 },
251
252 /**
253 * Get or set the root object of the virtual collection.
254 */
255 root : function(obj) {
256 if (arguments.length === 1) {
Akronadab5e52018-08-20 13:50:53 +0200257 var e = this.builder();
Akronb19803c2018-08-16 16:39:42 +0200258
259 if (e.firstChild !== null) {
Akronadab5e52018-08-20 13:50:53 +0200260
261 // Object not yet set
Akronb19803c2018-08-16 16:39:42 +0200262 if (e.firstChild !== obj.element()) {
263 e.replaceChild(obj.element(), e.firstChild);
264 };
265 }
266
267 // Append root element
268 else {
269 e.appendChild(obj.element());
270 };
271
272 // Update parent child relations
273 this._root = obj;
274 obj.parent(this);
275
276 this.update();
277 };
278 return this._root;
279 },
280
Akronadab5e52018-08-20 13:50:53 +0200281
282 /**
283 * Get the wrapper element associated with the vc
284 */
285 builder : function () {
286
287 // Initialize if necessary
288 if (this._builder !== undefined)
289 return this._builder;
290
291 this.element();
292 return this._builder;
293 },
294
Akronb19803c2018-08-16 16:39:42 +0200295 /**
296 * Get the element associated with the virtual collection
297 */
298 element : function() {
Akronb19803c2018-08-16 16:39:42 +0200299 if (this._element !== undefined) {
300 return this._element;
301 };
302
303 this._element = document.createElement('div');
304 this._element.setAttribute('class', 'vc');
305
Akronadab5e52018-08-20 13:50:53 +0200306 this._builder = this._element.addE('div');
307 this._builder.setAttribute('class', 'builder');
308
Akronb19803c2018-08-16 16:39:42 +0200309 // Initialize root
Akronadab5e52018-08-20 13:50:53 +0200310 this._builder.appendChild(this._root.element());
Akronb19803c2018-08-16 16:39:42 +0200311
312 // Add panel to display corpus statistic, ...
313 this.addVcInfPanel();
314
315 return this._element;
316 },
317
318 /**
319 * Update the whole object based on the underlying data structure
320 */
321 update : function() {
322 this._root.update();
323 return this;
324 },
325
326 /**
327 * Make the vc persistant by injecting the current timestamp as a
328 * creation date limit criterion.
329 * THIS IS CURRENTLY NOT USED
330 */
331 makePersistant : function() {
332 // this.root().wrapOnRoot('and');
333 var todayStr = KorAP._vcDatePicker.today();
334 var doc = docClass.create();
335 var root = this.root();
336
337 if (root.ldType() === 'docGroup' && root.operation === 'and') {
338 root.append(cond);
339 } else {
340 root.wrapOnRoot('and');
341 root.append(doc);
342 };
343
344 doc.key("creationDate");
345 doc.type("date");
346 doc.matchop("leq");
347 doc.value(todayStr);
348
349 /*
350 * { "@type" : "koral:doc", "key" : "creationDate", "type" :
351 * "type:date", "match" : "match:leq", "value" : todayStr }
352 * this.root().append(cond);
353 */
354 this.update();
355 },
356
Akron8a670162018-08-28 10:09:13 +0200357
358 // Get the reference name
359 getName : function () {
360 if (this._root.ldType() === 'non') {
361 return loc.VC_allCorpora;
362 }
363 else if (this._root.ldType() === 'docGroupRef') {
364 return this._root.ref();
365 }
366 else {
367 return loc.VC_oneCollection;
368 }
369 },
370
371
Akronb19803c2018-08-16 16:39:42 +0200372 /**
373 * Get the generated json string
374 */
375 toJson : function() {
376 return this._root.toJson();
377 },
378
379 /**
380 * Get the generated query string
381 */
382 toQuery : function() {
383 return this._root.toQuery();
384 },
385
386
387 /*
388 * Add panel to display virtual corpus information
389 */
390 addVcInfPanel : function() {
391
392 var dv = this._element.addE('div');
393 var panel = panelClass.create([ 'vcinfo' ]);
394 dv.appendChild(panel.element());
395
396 var that = this;
397 var actions = panel.actions;
398 var statView;
399
400 actions.add(loc.SHOW_STAT, [ 'statistic' ], function() {
401 if (statView === undefined || !statView.shown()) {
402 statView = corpStatVClass.create(that);
403 panel.add(statView);
404 }
405 });
406 }
407 };
408});