blob: 05e48ad97a994d265d18580f81769a4c897ec90b [file] [log] [blame]
Nils Diewald86dad5b2015-01-28 15:09:07 +00001/**
2 * Create virtual collections with a visual user interface.
Nils Diewald4c221252015-04-21 20:19:25 +00003 * This resembles the collection type objects of a KoralQuery
4 * "collection" object.
5 *
6 * KoralQuery v0.3 is expected.
Nils Diewald86dad5b2015-01-28 15:09:07 +00007 *
8 * @author Nils Diewald
9 */
Nils Diewald2fe12e12015-03-06 16:47:06 +000010/*
Nils Diewald1fcb2ad2015-04-20 19:19:18 +000011 * This replaces a previous version written by Mengfei Zhou
Nils Diewald2fe12e12015-03-06 16:47:06 +000012 */
Nils Diewald2fe12e12015-03-06 16:47:06 +000013
Nils Diewaldd0770492014-12-19 03:55:00 +000014/*
Nils Diewald86dad5b2015-01-28 15:09:07 +000015 TODO: Disable "and" or "or" in case it's followed
16 by an unspecified document
Akron0a6768f2016-07-13 18:00:43 +020017 TODO: Add "and"-method to root to add further constraints based on match-
18 input (like clicking on a pubDate timestamp in a match)
Nils Diewald1fcb2ad2015-04-20 19:19:18 +000019 TODO: Implement "persistence"-Option,
Nils Diewald6e43ffd2015-03-25 18:55:39 +000020 injecting the current creation date stamp
Nils Diewald709f52f2015-05-21 18:32:58 +000021 TODO: Implement vec-Type for document-id vectors
22 like docID in [1,2,3,4 ...]
Nils Diewald86dad5b2015-01-28 15:09:07 +000023
Nils Diewaldd599d542015-01-08 20:41:34 +000024 Error codes:
Nils Diewaldd0770492014-12-19 03:55:00 +000025 701: "JSON-LD group has no @type attribute"
26 704: "Operation needs operand list"
Nils Diewald3a2d8022014-12-16 02:45:41 +000027 802: "Match type is not supported by value type"
28 804: "Unknown value type"
29 805: "Value is invalid"
30 806: "Value is not a valid date string"
31 807: "Value is not a valid regular expression"
Nils Diewald3a2d8022014-12-16 02:45:41 +000032 810: "Unknown document group operation" (like 711)
33 811: "Document group expects operation" (like 703)
34 812: "Operand not supported in document group" (like 744)
Nils Diewaldd0770492014-12-19 03:55:00 +000035 813: "Collection type is not supported" (like 713)
Nils Diewald86dad5b2015-01-28 15:09:07 +000036 814: "Unknown rewrite operation"
37 815: "Rewrite expects source"
Nils Diewald1fcb2ad2015-04-20 19:19:18 +000038
39 Localization strings:
40 KorAP.Locale = {
41 EMPTY : '...',
42 AND : 'and',
43 OR : 'or',
44 DELETE : 'x'
45 }
Nils Diewald4c221252015-04-21 20:19:25 +000046 and various field names with the prefix 'VC_'
Nils Diewaldd0770492014-12-19 03:55:00 +000047*/
48
Nils Diewald0e6992a2015-04-14 20:13:52 +000049define([
50 'vc/unspecified',
51 'vc/doc',
52 'vc/docgroup',
Nils Diewald1fcb2ad2015-04-20 19:19:18 +000053 'vc/menu',
hebasta1c3ce0f2018-06-05 17:02:16 +020054 'vc/statistic',
Nils Diewald87507832015-05-01 23:36:41 +000055 'datepicker',
hebasta1c3ce0f2018-06-05 17:02:16 +020056 'util',
57], function (unspecDocClass, docClass, docGroupClass, menuClass, statClass, dpClass) {
Nils Diewald3a2d8022014-12-16 02:45:41 +000058 "use strict";
59
Akron31d89942018-04-06 16:44:51 +020060 KorAP._validUnspecMatchRE = new RegExp("^(?:eq|ne|contains(?:not)?|excludes)$");
61 KorAP._validStringMatchRE = new RegExp("^(?:eq|ne)$");
62 KorAP._validTextMatchRE = KorAP._validUnspecMatchRE;
63 KorAP._validTextOnlyMatchRE = new RegExp("^(?:contains(?:not)?|excludes)$");
Nils Diewald359a72c2015-04-20 17:40:29 +000064 KorAP._overrideStyles = false;
Akron31d89942018-04-06 16:44:51 +020065 // KorAP._validDateMatchRE is defined in datepicker.js!
Nils Diewald3a2d8022014-12-16 02:45:41 +000066
Akron0b489ad2018-02-02 16:49:32 +010067 const loc = KorAP.Locale;
hebasta0f0fd332018-06-29 10:11:15 +020068 loc.SHOWSTAT = loc.SHOWSTAT || 'Statistics';
69 loc.VERB_SHOWSTAT = loc.VERB_SHOWSTAT || 'Corpus Statistics';
hebastac8fa0782018-06-20 16:00:57 +020070
Nils Diewald1fcb2ad2015-04-20 19:19:18 +000071 KorAP._vcKeyMenu = undefined;
Nils Diewald87507832015-05-01 23:36:41 +000072 KorAP._vcDatePicker = dpClass.create();
Nils Diewald1fcb2ad2015-04-20 19:19:18 +000073
Akron712733a2018-04-05 18:17:47 +020074 // Create match menus ....
75 KorAP._vcMatchopMenu = {
76 'string' : menuClass.create([
77 ['eq', null],
78 ['ne', null]
79 ]),
80 'text' : menuClass.create([
81 ['eq', null], // Requires exact match
82 ['ne', null],
83 ['contains', null], // Requires token sequence match
84 ['containsnot', null]
85 ]),
86 'date' : menuClass.create([
87 ['eq', null],
Akron31d89942018-04-06 16:44:51 +020088 ['ne', null],
Akron712733a2018-04-05 18:17:47 +020089 ['geq', null],
90 ['leq', null]
91 ]),
92 'regex' : menuClass.create([
93 ['eq', null],
94 ['ne', null]
95 ])
96 };
97
Nils Diewaldd599d542015-01-08 20:41:34 +000098 /**
99 * Virtual Collection
100 */
Nils Diewald0e6992a2015-04-14 20:13:52 +0000101 return {
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000102
103 /**
104 * The JSON-LD type of the virtual collection
105 */
Nils Diewaldf219eb82015-01-07 20:15:42 +0000106 ldType : function () {
107 return null;
108 },
Nils Diewaldd599d542015-01-08 20:41:34 +0000109
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000110 // Initialize virtual collection
111 _init : function (keyList) {
112
113 // Inject localized css styles
Nils Diewald359a72c2015-04-20 17:40:29 +0000114 if (!KorAP._overrideStyles) {
Akrone4961b12017-05-10 21:04:46 +0200115 var sheet = KorAP.newStyleSheet();
Nils Diewald359a72c2015-04-20 17:40:29 +0000116
Akrone4961b12017-05-10 21:04:46 +0200117 // Add css rule for OR operations
118 sheet.insertRule(
119 '.vc .docGroup[data-operation=or] > .doc::before,' +
120 '.vc .docGroup[data-operation=or] > .docGroup::before ' +
121 '{ content: "' + loc.OR + '" }',
122 0
123 );
Nils Diewald359a72c2015-04-20 17:40:29 +0000124
Akrone4961b12017-05-10 21:04:46 +0200125 // Add css rule for AND operations
126 sheet.insertRule(
127 '.vc .docGroup[data-operation=and] > .doc::before,' +
128 '.vc .docGroup[data-operation=and] > .docGroup::before ' +
129 '{ content: "' + loc.AND + '" }',
130 1
131 );
Nils Diewald359a72c2015-04-20 17:40:29 +0000132
Akrone4961b12017-05-10 21:04:46 +0200133 KorAP._overrideStyles = true;
Nils Diewald359a72c2015-04-20 17:40:29 +0000134 };
135
Akron712733a2018-04-05 18:17:47 +0200136 // Create key menu
137 KorAP._vcKeyMenu = menuClass.create(keyList);
138 KorAP._vcKeyMenu.limit(6);
139
Nils Diewald359a72c2015-04-20 17:40:29 +0000140 return this;
141 },
142
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000143 /**
144 * Create a new virtual collection.
145 */
146 create : function (keyList) {
Nils Diewald6283d692015-04-23 20:32:53 +0000147 var obj = Object.create(this)._init(keyList);
148 obj._root = unspecDocClass.create(obj);
149 return obj;
Nils Diewald4019bd22015-01-08 19:57:50 +0000150 },
Nils Diewaldd599d542015-01-08 20:41:34 +0000151
Nils Diewald7148c6f2015-05-04 15:07:53 +0000152
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000153 /**
154 * Create and render a new virtual collection
155 * based on a KoralQuery collection document
156 */
Nils Diewald6283d692015-04-23 20:32:53 +0000157 fromJson : function (json) {
Nils Diewaldd0770492014-12-19 03:55:00 +0000158 if (json !== undefined) {
Akrone4961b12017-05-10 21:04:46 +0200159 // Parse root document
160 if (json['@type'] == 'koral:doc') {
161 this._root = docClass.create(this, json);
162 }
163 // parse root group
164 else if (json['@type'] == 'koral:docGroup') {
165 this._root = docGroupClass.create(this, json);
166 }
167 // Unknown collection type
168 else {
169 KorAP.log(813, "Collection type is not supported");
170 return;
171 };
Nils Diewaldd0770492014-12-19 03:55:00 +0000172 }
173
174 else {
Akrone4961b12017-05-10 21:04:46 +0200175 // Add unspecified object
176 this._root = unspecDocClass.create(this);
Nils Diewaldd0770492014-12-19 03:55:00 +0000177 };
178
Nils Diewald8e7182e2015-01-08 15:02:07 +0000179 // Init element and update
Nils Diewald6283d692015-04-23 20:32:53 +0000180 this.update();
Nils Diewaldd0770492014-12-19 03:55:00 +0000181
Nils Diewald6283d692015-04-23 20:32:53 +0000182 return this;
183 },
184
Nils Diewald7148c6f2015-05-04 15:07:53 +0000185
Akron04671e72017-05-11 20:47:32 +0200186 // Check if the virtual corpus contains a rewrite
187 // This is a class method
188 checkRewrite : function (json) {
189
190 // There is a rewrite attribute
191 if (json['rewrites'] !== undefined) {
192 return true;
193 }
194
195 // There is a group to check for rewrites
196 else if (json['@type'] === 'koral:docGroup') {
197 var ops = json['operands'];
198 if (ops === undefined)
199 return false;
200
201 for (var i in ops) {
202
203 // "this" is the class
204 if (this.checkRewrite(ops[i])) {
205 return true;
206 };
207 };
208 };
209 return false;
210 },
211
Nils Diewald7148c6f2015-05-04 15:07:53 +0000212 /**
213 * Clean the virtual document to uspecified doc.
214 */
Nils Diewald6283d692015-04-23 20:32:53 +0000215 clean : function () {
216 if (this._root.ldType() !== "non") {
Akron04671e72017-05-11 20:47:32 +0200217 this._root.destroy();
218 this.root(unspecDocClass.create(this));
Nils Diewald6283d692015-04-23 20:32:53 +0000219 };
220 return this;
Nils Diewald3a2d8022014-12-16 02:45:41 +0000221 },
Nils Diewaldd599d542015-01-08 20:41:34 +0000222
Nils Diewald7148c6f2015-05-04 15:07:53 +0000223
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000224 /**
225 * Get or set the root object of the
226 * virtual collection.
227 */
Nils Diewaldf219eb82015-01-07 20:15:42 +0000228 root : function (obj) {
Nils Diewald8e7182e2015-01-08 15:02:07 +0000229 if (arguments.length === 1) {
Akrone4961b12017-05-10 21:04:46 +0200230 var e = this.element();
Nils Diewald845282c2015-05-14 07:53:03 +0000231
Akrone4961b12017-05-10 21:04:46 +0200232 if (e.firstChild !== null) {
233 if (e.firstChild !== obj.element()) {
234 e.replaceChild(obj.element(), e.firstChild);
235 };
236 }
Nils Diewald8f6b6102015-01-08 18:25:33 +0000237
Akrone4961b12017-05-10 21:04:46 +0200238 // Append root element
239 else {
240 e.appendChild(obj.element());
241 };
Nils Diewald8f6b6102015-01-08 18:25:33 +0000242
Akrone4961b12017-05-10 21:04:46 +0200243 // Update parent child relations
244 this._root = obj;
245 obj.parent(this);
Nils Diewald8f6b6102015-01-08 18:25:33 +0000246
Akrone4961b12017-05-10 21:04:46 +0200247 this.update();
Nils Diewald8e7182e2015-01-08 15:02:07 +0000248 };
Nils Diewaldd0770492014-12-19 03:55:00 +0000249 return this._root;
Nils Diewald3a2d8022014-12-16 02:45:41 +0000250 },
Nils Diewald8f6b6102015-01-08 18:25:33 +0000251
Nils Diewald7148c6f2015-05-04 15:07:53 +0000252
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000253 /**
254 * Get the element associated with the virtual collection
255 */
Nils Diewaldd0770492014-12-19 03:55:00 +0000256 element : function () {
Akron12971a02018-01-03 16:38:10 +0100257 if (this._element !== undefined) {
258 return this._element;
259 };
Nils Diewaldd0770492014-12-19 03:55:00 +0000260
261 this._element = document.createElement('div');
262 this._element.setAttribute('class', 'vc');
Nils Diewald8e7182e2015-01-08 15:02:07 +0000263
Nils Diewald8f6b6102015-01-08 18:25:33 +0000264 // Initialize root
265 this._element.appendChild(this._root.element());
Nils Diewald845282c2015-05-14 07:53:03 +0000266
hebastac8fa0782018-06-20 16:00:57 +0200267 //Add corpus statistic button
hebastac8fa0782018-06-20 16:00:57 +0200268 this.addStatBut();
hebasta0f0fd332018-06-29 10:11:15 +0200269
Nils Diewaldd0770492014-12-19 03:55:00 +0000270 return this._element;
Nils Diewaldf219eb82015-01-07 20:15:42 +0000271 },
Nils Diewaldd599d542015-01-08 20:41:34 +0000272
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000273
274 /**
275 * Update the whole object based on the underlying
276 * data structure
277 */
Nils Diewaldd599d542015-01-08 20:41:34 +0000278 update : function () {
279 this._root.update();
280 return this;
281 },
282
Nils Diewald845282c2015-05-14 07:53:03 +0000283 /**
284 * Make the vc persistant by injecting the current timestamp
285 * as a creation date limit criterion.
286 */
287 makePersistant : function () {
Akron12971a02018-01-03 16:38:10 +0100288 // this.root().wrapOnRoot('and');
Nils Diewald845282c2015-05-14 07:53:03 +0000289 var todayStr = KorAP._vcDatePicker.today();
290 var doc = docClass.create();
291 var root = this.root();
292
293 if (root.ldType() === 'docGroup' &&
Akron12971a02018-01-03 16:38:10 +0100294 root.operation === 'and') {
295 root.append(cond);
Nils Diewald845282c2015-05-14 07:53:03 +0000296 }
297 else {
Akron12971a02018-01-03 16:38:10 +0100298 root.wrapOnRoot('and');
299 root.append(doc);
Nils Diewald845282c2015-05-14 07:53:03 +0000300 };
301
302 doc.key("creationDate");
303 doc.type("date");
304 doc.matchop("leq");
305 doc.value(todayStr);
306
307/*
308 {
309 "@type" : "koral:doc",
310 "key" : "creationDate",
311 "type" : "type:date",
312 "match" : "match:leq",
313 "value" : todayStr
314 }
315 this.root().append(cond);
316*/
317 this.update();
318 },
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000319
320 /**
321 * Get the generated json string
322 */
Nils Diewaldf219eb82015-01-07 20:15:42 +0000323 toJson : function () {
324 return this._root.toJson();
325 },
Nils Diewaldd599d542015-01-08 20:41:34 +0000326
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000327
328 /**
329 * Get the generated query string
330 */
Nils Diewaldd599d542015-01-08 20:41:34 +0000331 toQuery : function () {
332 return this._root.toQuery();
hebasta1c3ce0f2018-06-05 17:02:16 +0200333 },
334
hebastac8fa0782018-06-20 16:00:57 +0200335
336 /**
337 * Adds Corpus Statistic Button
338 **/
339 addStatBut : function(){
hebasta1c3ce0f2018-06-05 17:02:16 +0200340
hebastac8fa0782018-06-20 16:00:57 +0200341 //add div element 'corpStat' with id 'dCorpStat'
342 var dv = this._element.addE('div');
343 dv.id = "dCorpStat";
344
345 //add button
346 var bu = dv.addE('div');
hebasta0f0fd332018-06-29 10:11:15 +0200347 bu.classList.add('bottom', 'button-stat');
hebastac8fa0782018-06-20 16:00:57 +0200348 var stat = bu.addE('span');
hebasta0f0fd332018-06-29 10:11:15 +0200349 stat.addT(loc.SHOWSTAT);
hebastac8fa0782018-06-20 16:00:57 +0200350 //localize the buttons title attribute
hebasta0f0fd332018-06-29 10:11:15 +0200351 //stat.setAttribute('title', loc.SHOWSTAT);
hebastac8fa0782018-06-20 16:00:57 +0200352 stat.classList.add('statistic');
353
354 /*
355 * In ECMAScript Language Specification this is set incorrectly for inner functions,
356 * therefore thatelement and that is defined.
357 */
358 var thatelement = this._element;
359 var that = this;
360
361 //show corpus statistic if button is clicked
362 stat.addEventListener('click', function(e){
363 e.halt();
364 statClass.showCorpStat(thatelement, that);
365 });
366
367 }
368
Nils Diewald3a2d8022014-12-16 02:45:41 +0000369 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000370});