blob: 082ede0a531b60bf1766393bdb4630dc225e2e3c [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
hebastaa79d69d2018-07-24 12:13:02 +020047define([ 'vc/unspecified', 'vc/doc', 'vc/docgroup', 'vc/menu', 'vc/statistic',
48 'datepicker', 'buttongroup', 'panel', 'view/corpstatv', 'util', ],
49 function(unspecDocClass, docClass, docGroupClass, menuClass, statClass,
50 dpClass, buttonGrClass, panelClass, corpStatVClass) {
51 "use strict";
Nils Diewald1fcb2ad2015-04-20 19:19:18 +000052
hebastaa79d69d2018-07-24 12:13:02 +020053 KorAP._validUnspecMatchRE = new RegExp(
54 "^(?:eq|ne|contains(?:not)?|excludes)$");
55 KorAP._validStringMatchRE = new RegExp("^(?:eq|ne)$");
56 KorAP._validTextMatchRE = KorAP._validUnspecMatchRE;
57 KorAP._validTextOnlyMatchRE = new RegExp(
58 "^(?:contains(?:not)?|excludes)$");
59 KorAP._overrideStyles = false;
60 // KorAP._validDateMatchRE is defined in datepicker.js!
Nils Diewaldd0770492014-12-19 03:55:00 +000061
hebastaa79d69d2018-07-24 12:13:02 +020062 const loc = KorAP.Locale;
63 loc.SHOW_STAT = loc.SHOW_STAT || 'Statistics';
64 loc.VERB_SHOWSTAT = loc.VERB_SHOWSTAT || 'Corpus Statistics';
Nils Diewald3a2d8022014-12-16 02:45:41 +000065
hebastaa79d69d2018-07-24 12:13:02 +020066 KorAP._vcKeyMenu = undefined;
67 KorAP._vcDatePicker = dpClass.create();
Nils Diewald3a2d8022014-12-16 02:45:41 +000068
hebastaa79d69d2018-07-24 12:13:02 +020069 // Create match menus ....
70 KorAP._vcMatchopMenu = {
71 'string' : menuClass.create([ [ 'eq', null ], [ 'ne', null ] ]),
72 'text' : menuClass.create([ [ 'eq', null ], // Requires exact match
73 [ 'ne', null ], [ 'contains', null ], // Requires token sequence match
74 [ 'containsnot', null ] ]),
75 'date' : menuClass.create([ [ 'eq', null ], [ 'ne', null ],
76 [ 'geq', null ], [ 'leq', null ] ]),
77 'regex' : menuClass.create([ [ 'eq', null ], [ 'ne', null ] ])
Nils Diewald359a72c2015-04-20 17:40:29 +000078 };
79
hebastaa79d69d2018-07-24 12:13:02 +020080 /**
81 * Virtual Collection
82 */
83 return {
Akron712733a2018-04-05 18:17:47 +020084
hebastaa79d69d2018-07-24 12:13:02 +020085 /**
86 * The JSON-LD type of the virtual collection
87 */
88 ldType : function() {
89 return null;
90 },
Nils Diewald359a72c2015-04-20 17:40:29 +000091
hebastaa79d69d2018-07-24 12:13:02 +020092 // Initialize virtual collection
93 _init : function(keyList) {
Nils Diewaldd599d542015-01-08 20:41:34 +000094
hebastaa79d69d2018-07-24 12:13:02 +020095 // Inject localized css styles
96 if (!KorAP._overrideStyles) {
97 var sheet = KorAP.newStyleSheet();
Nils Diewald7148c6f2015-05-04 15:07:53 +000098
hebastaa79d69d2018-07-24 12:13:02 +020099 // Add css rule for OR operations
100 sheet.insertRule('.vc .docGroup[data-operation=or] > .doc::before,'
101 + '.vc .docGroup[data-operation=or] > .docGroup::before '
102 + '{ content: "' + loc.OR + '" }', 0);
Nils Diewaldd0770492014-12-19 03:55:00 +0000103
hebastaa79d69d2018-07-24 12:13:02 +0200104 // Add css rule for AND operations
105 sheet.insertRule(
106 '.vc .docGroup[data-operation=and] > .doc::before,'
107 + '.vc .docGroup[data-operation=and] > .docGroup::before '
108 + '{ content: "' + loc.AND + '" }', 1);
Nils Diewaldd0770492014-12-19 03:55:00 +0000109
hebastaa79d69d2018-07-24 12:13:02 +0200110 KorAP._overrideStyles = true;
111 }
112 ;
Nils Diewaldd0770492014-12-19 03:55:00 +0000113
hebastaa79d69d2018-07-24 12:13:02 +0200114 // Create key menu
115 KorAP._vcKeyMenu = menuClass.create(keyList);
116 KorAP._vcKeyMenu.limit(6);
Nils Diewald6283d692015-04-23 20:32:53 +0000117
hebastaa79d69d2018-07-24 12:13:02 +0200118 return this;
119 },
Nils Diewald7148c6f2015-05-04 15:07:53 +0000120
hebastaa79d69d2018-07-24 12:13:02 +0200121 /**
122 * Create a new virtual collection.
123 */
124 create : function(keyList) {
125 var obj = Object.create(this)._init(keyList);
126 obj._root = unspecDocClass.create(obj);
127 return obj;
128 },
Akron04671e72017-05-11 20:47:32 +0200129
hebastaa79d69d2018-07-24 12:13:02 +0200130 /**
131 * Create and render a new virtual collection based on a KoralQuery
132 * collection document
133 */
134 fromJson : function(json) {
135 if (json !== undefined) {
136 // Parse root document
137 if (json['@type'] == 'koral:doc') {
138 this._root = docClass.create(this, json);
139 }
140 // parse root group
141 else if (json['@type'] == 'koral:docGroup') {
142 this._root = docGroupClass.create(this, json);
143 }
144 // Unknown collection type
145 else {
146 KorAP.log(813, "Collection type is not supported");
147 return;
148 }
149 ;
150 }
Akron04671e72017-05-11 20:47:32 +0200151
hebastaa79d69d2018-07-24 12:13:02 +0200152 else {
153 // Add unspecified object
154 this._root = unspecDocClass.create(this);
155 }
156 ;
Akron04671e72017-05-11 20:47:32 +0200157
hebastaa79d69d2018-07-24 12:13:02 +0200158 // Init element and update
159 this.update();
Akron04671e72017-05-11 20:47:32 +0200160
hebastaa79d69d2018-07-24 12:13:02 +0200161 return this;
162 },
163
164 // Check if the virtual corpus contains a rewrite
165 // This is a class method
166 checkRewrite : function(json) {
167
168 // There is a rewrite attribute
169 if (json['rewrites'] !== undefined) {
Akron04671e72017-05-11 20:47:32 +0200170 return true;
hebastaa79d69d2018-07-24 12:13:02 +0200171 }
Akron04671e72017-05-11 20:47:32 +0200172
hebastaa79d69d2018-07-24 12:13:02 +0200173 // There is a group to check for rewrites
174 else if (json['@type'] === 'koral:docGroup') {
175 var ops = json['operands'];
176 if (ops === undefined)
177 return false;
Nils Diewaldd599d542015-01-08 20:41:34 +0000178
hebastaa79d69d2018-07-24 12:13:02 +0200179 for ( var i in ops) {
Nils Diewald7148c6f2015-05-04 15:07:53 +0000180
hebastaa79d69d2018-07-24 12:13:02 +0200181 // "this" is the class
182 if (this.checkRewrite(ops[i])) {
183 return true;
184 }
185 ;
186 }
187 ;
188 }
189 ;
190 return false;
191 },
Nils Diewald845282c2015-05-14 07:53:03 +0000192
hebastaa79d69d2018-07-24 12:13:02 +0200193 /**
194 * Clean the virtual document to uspecified doc.
195 */
196 clean : function() {
197 if (this._root.ldType() !== "non") {
198 this._root.destroy();
199 this.root(unspecDocClass.create(this));
200 }
201 ;
202 return this;
203 },
Nils Diewald8f6b6102015-01-08 18:25:33 +0000204
hebastaa79d69d2018-07-24 12:13:02 +0200205 /**
206 * Get or set the root object of the virtual collection.
207 */
208 root : function(obj) {
209 if (arguments.length === 1) {
210 var e = this.element();
Nils Diewald8f6b6102015-01-08 18:25:33 +0000211
hebastaa79d69d2018-07-24 12:13:02 +0200212 if (e.firstChild !== null) {
213 if (e.firstChild !== obj.element()) {
214 e.replaceChild(obj.element(), e.firstChild);
215 }
216 ;
217 }
Nils Diewald8f6b6102015-01-08 18:25:33 +0000218
hebastaa79d69d2018-07-24 12:13:02 +0200219 // Append root element
220 else {
221 e.appendChild(obj.element());
222 }
223 ;
Nils Diewald8f6b6102015-01-08 18:25:33 +0000224
hebastaa79d69d2018-07-24 12:13:02 +0200225 // Update parent child relations
226 this._root = obj;
227 obj.parent(this);
Nils Diewaldd0770492014-12-19 03:55:00 +0000228
hebastaa79d69d2018-07-24 12:13:02 +0200229 this.update();
230 }
231 ;
232 return this._root;
233 },
Nils Diewald8e7182e2015-01-08 15:02:07 +0000234
hebastaa79d69d2018-07-24 12:13:02 +0200235 /**
236 * Get the element associated with the virtual collection
237 */
238 element : function() {
239
240
241 if (this._element !== undefined) {
242 return this._element;
243 }
244 ;
245
246 this._element = document.createElement('div');
247 this._element.setAttribute('class', 'vc');
248
249 // Initialize root
250 this._element.appendChild(this._root.element());
251
252 /*
253 * TODO by Helge Hack! additional div, because statistic button is
254 * removed after choosing and/or/x in vc builder. REMOVE this lines
255 * after solving the problem!!!!
256 */
257 this._element.addE('div');
258 this._element.addE('div');
259 this._element.addE('div');
260
261 // Add panel to display corpus statistic, ...
262 this.addVcInfPanel();
Nils Diewald845282c2015-05-14 07:53:03 +0000263
hebastaa79d69d2018-07-24 12:13:02 +0200264 return this._element;
265 },
266
267 /**
268 * Update the whole object based on the underlying data structure
269 */
270 update : function() {
271 this._root.update();
272 return this;
273 },
274
275 /**
276 * Make the vc persistant by injecting the current timestamp as a
277 * creation date limit criterion.
278 */
279 makePersistant : function() {
280 // this.root().wrapOnRoot('and');
281 var todayStr = KorAP._vcDatePicker.today();
282 var doc = docClass.create();
283 var root = this.root();
284
285 if (root.ldType() === 'docGroup' && root.operation === 'and') {
286 root.append(cond);
287 } else {
288 root.wrapOnRoot('and');
289 root.append(doc);
290 }
291 ;
292
293 doc.key("creationDate");
294 doc.type("date");
295 doc.matchop("leq");
296 doc.value(todayStr);
297
298 /*
299 * { "@type" : "koral:doc", "key" : "creationDate", "type" :
300 * "type:date", "match" : "match:leq", "value" : todayStr }
301 * this.root().append(cond);
302 */
303 this.update();
304 },
305
306 /**
307 * Get the generated json string
308 */
309 toJson : function() {
310 return this._root.toJson();
311 },
312
313 /**
314 * Get the generated query string
315 */
316 toQuery : function() {
317 return this._root.toQuery();
318 },
319
320
321 /*
322 * Add panel to display virtual corpus information
323 */
324 addVcInfPanel : function() {
325
326 var dv = this._element.addE('div');
327 var panel = panelClass.create([ 'vcinfo' ]);
328 dv.appendChild(panel.element());
hebasta4c92eec2018-06-29 10:11:15 +0200329
hebastaa79d69d2018-07-24 12:13:02 +0200330 var that = this;
331 var actions = panel.actions;
332 var statView;
Akron1bdf5272018-07-24 18:51:30 +0200333
hebastaa79d69d2018-07-24 12:13:02 +0200334 actions.add(loc.SHOW_STAT, [ 'statistic' ], function() {
335 if (statView === undefined || !statView.shown()) {
hebastaa79d69d2018-07-24 12:13:02 +0200336 statView = corpStatVClass.create(that);
337 panel.add(statView);
338 }
339 });
340 }
Nils Diewald845282c2015-05-14 07:53:03 +0000341 };
hebastaa79d69d2018-07-24 12:13:02 +0200342 });