blob: 07731c7082e499414871a4f20f4633105cb7f071 [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';
Nils Diewald3a2d8022014-12-16 02:45:41 +000084
Akronb19803c2018-08-16 16:39:42 +020085 KorAP._vcKeyMenu = undefined;
86 KorAP._vcDatePicker = dpClass.create();
Nils Diewald3a2d8022014-12-16 02:45:41 +000087
Akronb19803c2018-08-16 16:39:42 +020088 // Create match menus ....
89 KorAP._vcMatchopMenu = {
90 'string' : menuClass.create([
91 [ 'eq', null ],
92 [ 'ne', null ]
93 ]),
94 'text' : menuClass.create([
95 [ 'eq', null ], // Requires exact match
96 [ 'ne', null ],
97 [ 'contains', null ], // Requires token sequence match
98 [ 'containsnot', null ]
99 ]),
100 'date' : menuClass.create([
101 [ 'eq', null ],
102 [ 'ne', null ],
103 [ 'geq', null ],
104 [ 'leq', null ]
105 ]),
106 'regex' : menuClass.create([
107 [ 'eq', null ],
108 [ 'ne', null ]
109 ])
110 };
111
112 /**
113 * Virtual Collection
114 */
115 return {
116
117 /**
118 * The JSON-LD type of the virtual collection
119 */
120 ldType : function() {
121 return null;
122 },
123
124 // Initialize virtual collection
125 _init : function(keyList) {
126
127 // Inject localized css styles
128 if (!KorAP._overrideStyles) {
129 var sheet = KorAP.newStyleSheet();
130
131 // Add css rule for OR operations
132 sheet.insertRule('.vc .docGroup[data-operation=or] > .doc::before,'
133 + '.vc .docGroup[data-operation=or] > .docGroup::before '
134 + '{ content: "' + loc.OR + '" }', 0);
135
136 // Add css rule for AND operations
137 sheet.insertRule(
138 '.vc .docGroup[data-operation=and] > .doc::before,'
139 + '.vc .docGroup[data-operation=and] > .docGroup::before '
140 + '{ content: "' + loc.AND + '" }', 1);
141
142 KorAP._overrideStyles = true;
Nils Diewald359a72c2015-04-20 17:40:29 +0000143 };
144
Akron3ad46942018-08-22 16:47:14 +0200145 var l;
146 if (keyList) {
147 l = keyList.slice();
148 l.unshift(['referTo', 'ref']);
149 }
150 else {
151 l = [['referTo', 'ref']];
152 }
153
Akronb19803c2018-08-16 16:39:42 +0200154 // Create key menu
Akron3ad46942018-08-22 16:47:14 +0200155 KorAP._vcKeyMenu = menuClass.create(l);
Akronb19803c2018-08-16 16:39:42 +0200156 KorAP._vcKeyMenu.limit(6);
Akron712733a2018-04-05 18:17:47 +0200157
Akronb19803c2018-08-16 16:39:42 +0200158 return this;
159 },
Nils Diewald359a72c2015-04-20 17:40:29 +0000160
Akronb19803c2018-08-16 16:39:42 +0200161 /**
162 * Create a new virtual collection.
163 */
164 create : function(keyList) {
165 var obj = Object.create(this)._init(keyList);
166 obj._root = unspecDocClass.create(obj);
167 return obj;
168 },
Nils Diewaldd599d542015-01-08 20:41:34 +0000169
Akronb19803c2018-08-16 16:39:42 +0200170 /**
171 * Create and render a new virtual collection based on a KoralQuery
172 * collection document
173 */
174 fromJson : function(json) {
175 if (json !== undefined) {
176 // Parse root document
177 if (json['@type'] == 'koral:doc') {
178 this._root = docClass.create(this, json);
hebastaa79d69d2018-07-24 12:13:02 +0200179 }
Akronb19803c2018-08-16 16:39:42 +0200180 // parse root group
181 else if (json['@type'] == 'koral:docGroup') {
182 this._root = docGroupClass.create(this, json);
183 }
184
185 // parse root reference
186 else if (json['@type'] == 'koral:docGroupRef') {
187 this._root = docGroupRefClass.create(this, json);
188 }
189
190 // Unknown collection type
191 else {
192 KorAP.log(813, "Collection type is not supported");
193 return;
194 };
195 }
196
197 else {
198 // Add unspecified object
199 this._root = unspecDocClass.create(this);
Nils Diewald845282c2015-05-14 07:53:03 +0000200 };
Akronb19803c2018-08-16 16:39:42 +0200201
202 // Init element and update
203 this.update();
204
205 return this;
206 },
207
208 // Check if the virtual corpus contains a rewrite
209 // This is a class method
210 checkRewrite : function(json) {
211
212 // There is a rewrite attribute
213 if (json['rewrites'] !== undefined) {
214 return true;
215 }
216
217 // There is a group to check for rewrites
218 else if (json['@type'] === 'koral:docGroup') {
219 var ops = json['operands'];
220 if (ops === undefined)
221 return false;
222
223 for ( var i in ops) {
224
225 // "this" is the class
226 if (this.checkRewrite(ops[i])) {
227 return true;
228 };
229 };
230 };
231 return false;
232 },
233
234 /**
235 * Clean the virtual document to uspecified doc.
236 */
237 clean : function() {
238 if (this._root.ldType() !== "non") {
239 this._root.destroy();
240 this.root(unspecDocClass.create(this));
241 };
242 return this;
243 },
244
245 /**
246 * Get or set the root object of the virtual collection.
247 */
248 root : function(obj) {
249 if (arguments.length === 1) {
250 var e = this.element();
251
252 if (e.firstChild !== null) {
253 if (e.firstChild !== obj.element()) {
254 e.replaceChild(obj.element(), e.firstChild);
255 };
256 }
257
258 // Append root element
259 else {
260 e.appendChild(obj.element());
261 };
262
263 // Update parent child relations
264 this._root = obj;
265 obj.parent(this);
266
267 this.update();
268 };
269 return this._root;
270 },
271
272 /**
273 * Get the element associated with the virtual collection
274 */
275 element : function() {
276
277
278 if (this._element !== undefined) {
279 return this._element;
280 };
281
282 this._element = document.createElement('div');
283 this._element.setAttribute('class', 'vc');
284
285 // Initialize root
286 this._element.appendChild(this._root.element());
287
288 /*
289 * TODO by Helge Hack! additional div, because statistic button is
290 * removed after choosing and/or/x in vc builder. REMOVE this lines
291 * after solving the problem!!!!
292 */
293 this._element.addE('div');
294 this._element.addE('div');
295 this._element.addE('div');
296
297 // Add panel to display corpus statistic, ...
298 this.addVcInfPanel();
299
300 return this._element;
301 },
302
303 /**
304 * Update the whole object based on the underlying data structure
305 */
306 update : function() {
307 this._root.update();
308 return this;
309 },
310
311 /**
312 * Make the vc persistant by injecting the current timestamp as a
313 * creation date limit criterion.
314 * THIS IS CURRENTLY NOT USED
315 */
316 makePersistant : function() {
317 // this.root().wrapOnRoot('and');
318 var todayStr = KorAP._vcDatePicker.today();
319 var doc = docClass.create();
320 var root = this.root();
321
322 if (root.ldType() === 'docGroup' && root.operation === 'and') {
323 root.append(cond);
324 } else {
325 root.wrapOnRoot('and');
326 root.append(doc);
327 };
328
329 doc.key("creationDate");
330 doc.type("date");
331 doc.matchop("leq");
332 doc.value(todayStr);
333
334 /*
335 * { "@type" : "koral:doc", "key" : "creationDate", "type" :
336 * "type:date", "match" : "match:leq", "value" : todayStr }
337 * this.root().append(cond);
338 */
339 this.update();
340 },
341
342 /**
343 * Get the generated json string
344 */
345 toJson : function() {
346 return this._root.toJson();
347 },
348
349 /**
350 * Get the generated query string
351 */
352 toQuery : function() {
353 return this._root.toQuery();
354 },
355
356
357 /*
358 * Add panel to display virtual corpus information
359 */
360 addVcInfPanel : function() {
361
362 var dv = this._element.addE('div');
363 var panel = panelClass.create([ 'vcinfo' ]);
364 dv.appendChild(panel.element());
365
366 var that = this;
367 var actions = panel.actions;
368 var statView;
369
370 actions.add(loc.SHOW_STAT, [ 'statistic' ], function() {
371 if (statView === undefined || !statView.shown()) {
372 statView = corpStatVClass.create(that);
373 panel.add(statView);
374 }
375 });
376 }
377 };
378});