blob: 6941341c45b70560a1a6477da224deb4fe8c3772 [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;
hebastac8fa0782018-06-20 16:00:57 +020068 loc.VC_SHOWSTAT = loc.VC_SHOWSTAT || 'Corpus Statistic';
69
Nils Diewald1fcb2ad2015-04-20 19:19:18 +000070 KorAP._vcKeyMenu = undefined;
Nils Diewald87507832015-05-01 23:36:41 +000071 KorAP._vcDatePicker = dpClass.create();
Nils Diewald1fcb2ad2015-04-20 19:19:18 +000072
Akron712733a2018-04-05 18:17:47 +020073 // Create match menus ....
74 KorAP._vcMatchopMenu = {
75 'string' : menuClass.create([
76 ['eq', null],
77 ['ne', null]
78 ]),
79 'text' : menuClass.create([
80 ['eq', null], // Requires exact match
81 ['ne', null],
82 ['contains', null], // Requires token sequence match
83 ['containsnot', null]
84 ]),
85 'date' : menuClass.create([
86 ['eq', null],
Akron31d89942018-04-06 16:44:51 +020087 ['ne', null],
Akron712733a2018-04-05 18:17:47 +020088 ['geq', null],
89 ['leq', null]
90 ]),
91 'regex' : menuClass.create([
92 ['eq', null],
93 ['ne', null]
94 ])
95 };
96
Nils Diewaldd599d542015-01-08 20:41:34 +000097 /**
98 * Virtual Collection
99 */
Nils Diewald0e6992a2015-04-14 20:13:52 +0000100 return {
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000101
102 /**
103 * The JSON-LD type of the virtual collection
104 */
Nils Diewaldf219eb82015-01-07 20:15:42 +0000105 ldType : function () {
106 return null;
107 },
Nils Diewaldd599d542015-01-08 20:41:34 +0000108
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000109 // Initialize virtual collection
110 _init : function (keyList) {
111
112 // Inject localized css styles
Nils Diewald359a72c2015-04-20 17:40:29 +0000113 if (!KorAP._overrideStyles) {
Akrone4961b12017-05-10 21:04:46 +0200114 var sheet = KorAP.newStyleSheet();
Nils Diewald359a72c2015-04-20 17:40:29 +0000115
Akrone4961b12017-05-10 21:04:46 +0200116 // Add css rule for OR operations
117 sheet.insertRule(
118 '.vc .docGroup[data-operation=or] > .doc::before,' +
119 '.vc .docGroup[data-operation=or] > .docGroup::before ' +
120 '{ content: "' + loc.OR + '" }',
121 0
122 );
Nils Diewald359a72c2015-04-20 17:40:29 +0000123
Akrone4961b12017-05-10 21:04:46 +0200124 // Add css rule for AND operations
125 sheet.insertRule(
126 '.vc .docGroup[data-operation=and] > .doc::before,' +
127 '.vc .docGroup[data-operation=and] > .docGroup::before ' +
128 '{ content: "' + loc.AND + '" }',
129 1
130 );
Nils Diewald359a72c2015-04-20 17:40:29 +0000131
Akrone4961b12017-05-10 21:04:46 +0200132 KorAP._overrideStyles = true;
Nils Diewald359a72c2015-04-20 17:40:29 +0000133 };
134
Akron712733a2018-04-05 18:17:47 +0200135 // Create key menu
136 KorAP._vcKeyMenu = menuClass.create(keyList);
137 KorAP._vcKeyMenu.limit(6);
138
Nils Diewald359a72c2015-04-20 17:40:29 +0000139 return this;
140 },
141
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000142 /**
143 * Create a new virtual collection.
144 */
145 create : function (keyList) {
Nils Diewald6283d692015-04-23 20:32:53 +0000146 var obj = Object.create(this)._init(keyList);
147 obj._root = unspecDocClass.create(obj);
148 return obj;
Nils Diewald4019bd22015-01-08 19:57:50 +0000149 },
Nils Diewaldd599d542015-01-08 20:41:34 +0000150
Nils Diewald7148c6f2015-05-04 15:07:53 +0000151
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000152 /**
153 * Create and render a new virtual collection
154 * based on a KoralQuery collection document
155 */
Nils Diewald6283d692015-04-23 20:32:53 +0000156 fromJson : function (json) {
Nils Diewaldd0770492014-12-19 03:55:00 +0000157 if (json !== undefined) {
Akrone4961b12017-05-10 21:04:46 +0200158 // Parse root document
159 if (json['@type'] == 'koral:doc') {
160 this._root = docClass.create(this, json);
161 }
162 // parse root group
163 else if (json['@type'] == 'koral:docGroup') {
164 this._root = docGroupClass.create(this, json);
165 }
166 // Unknown collection type
167 else {
168 KorAP.log(813, "Collection type is not supported");
169 return;
170 };
Nils Diewaldd0770492014-12-19 03:55:00 +0000171 }
172
173 else {
Akrone4961b12017-05-10 21:04:46 +0200174 // Add unspecified object
175 this._root = unspecDocClass.create(this);
Nils Diewaldd0770492014-12-19 03:55:00 +0000176 };
177
Nils Diewald8e7182e2015-01-08 15:02:07 +0000178 // Init element and update
Nils Diewald6283d692015-04-23 20:32:53 +0000179 this.update();
Nils Diewaldd0770492014-12-19 03:55:00 +0000180
Nils Diewald6283d692015-04-23 20:32:53 +0000181 return this;
182 },
183
Nils Diewald7148c6f2015-05-04 15:07:53 +0000184
Akron04671e72017-05-11 20:47:32 +0200185 // Check if the virtual corpus contains a rewrite
186 // This is a class method
187 checkRewrite : function (json) {
188
189 // There is a rewrite attribute
190 if (json['rewrites'] !== undefined) {
191 return true;
192 }
193
194 // There is a group to check for rewrites
195 else if (json['@type'] === 'koral:docGroup') {
196 var ops = json['operands'];
197 if (ops === undefined)
198 return false;
199
200 for (var i in ops) {
201
202 // "this" is the class
203 if (this.checkRewrite(ops[i])) {
204 return true;
205 };
206 };
207 };
208 return false;
209 },
210
Nils Diewald7148c6f2015-05-04 15:07:53 +0000211 /**
212 * Clean the virtual document to uspecified doc.
213 */
Nils Diewald6283d692015-04-23 20:32:53 +0000214 clean : function () {
215 if (this._root.ldType() !== "non") {
Akron04671e72017-05-11 20:47:32 +0200216 this._root.destroy();
217 this.root(unspecDocClass.create(this));
Nils Diewald6283d692015-04-23 20:32:53 +0000218 };
219 return this;
Nils Diewald3a2d8022014-12-16 02:45:41 +0000220 },
Nils Diewaldd599d542015-01-08 20:41:34 +0000221
Nils Diewald7148c6f2015-05-04 15:07:53 +0000222
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000223 /**
224 * Get or set the root object of the
225 * virtual collection.
226 */
Nils Diewaldf219eb82015-01-07 20:15:42 +0000227 root : function (obj) {
Nils Diewald8e7182e2015-01-08 15:02:07 +0000228 if (arguments.length === 1) {
Akrone4961b12017-05-10 21:04:46 +0200229 var e = this.element();
Nils Diewald845282c2015-05-14 07:53:03 +0000230
Akrone4961b12017-05-10 21:04:46 +0200231 if (e.firstChild !== null) {
232 if (e.firstChild !== obj.element()) {
233 e.replaceChild(obj.element(), e.firstChild);
234 };
235 }
Nils Diewald8f6b6102015-01-08 18:25:33 +0000236
Akrone4961b12017-05-10 21:04:46 +0200237 // Append root element
238 else {
239 e.appendChild(obj.element());
240 };
Nils Diewald8f6b6102015-01-08 18:25:33 +0000241
Akrone4961b12017-05-10 21:04:46 +0200242 // Update parent child relations
243 this._root = obj;
244 obj.parent(this);
Nils Diewald8f6b6102015-01-08 18:25:33 +0000245
Akrone4961b12017-05-10 21:04:46 +0200246 this.update();
Nils Diewald8e7182e2015-01-08 15:02:07 +0000247 };
Nils Diewaldd0770492014-12-19 03:55:00 +0000248 return this._root;
Nils Diewald3a2d8022014-12-16 02:45:41 +0000249 },
Nils Diewald8f6b6102015-01-08 18:25:33 +0000250
Nils Diewald7148c6f2015-05-04 15:07:53 +0000251
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000252 /**
253 * Get the element associated with the virtual collection
254 */
Nils Diewaldd0770492014-12-19 03:55:00 +0000255 element : function () {
Akron12971a02018-01-03 16:38:10 +0100256 if (this._element !== undefined) {
257 return this._element;
258 };
Nils Diewaldd0770492014-12-19 03:55:00 +0000259
260 this._element = document.createElement('div');
261 this._element.setAttribute('class', 'vc');
Nils Diewald8e7182e2015-01-08 15:02:07 +0000262
Nils Diewald8f6b6102015-01-08 18:25:33 +0000263 // Initialize root
264 this._element.appendChild(this._root.element());
Nils Diewald845282c2015-05-14 07:53:03 +0000265
hebastac8fa0782018-06-20 16:00:57 +0200266 //Add corpus statistic button
267 if(!(this._root.element().classList.contains('unspecified'))){
268 this.addStatBut();
269 }
270
Nils Diewaldd0770492014-12-19 03:55:00 +0000271 return this._element;
Nils Diewaldf219eb82015-01-07 20:15:42 +0000272 },
Nils Diewaldd599d542015-01-08 20:41:34 +0000273
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000274
275 /**
276 * Update the whole object based on the underlying
277 * data structure
278 */
Nils Diewaldd599d542015-01-08 20:41:34 +0000279 update : function () {
280 this._root.update();
281 return this;
282 },
283
Nils Diewald845282c2015-05-14 07:53:03 +0000284 /**
285 * Make the vc persistant by injecting the current timestamp
286 * as a creation date limit criterion.
287 */
288 makePersistant : function () {
Akron12971a02018-01-03 16:38:10 +0100289 // this.root().wrapOnRoot('and');
Nils Diewald845282c2015-05-14 07:53:03 +0000290 var todayStr = KorAP._vcDatePicker.today();
291 var doc = docClass.create();
292 var root = this.root();
293
294 if (root.ldType() === 'docGroup' &&
Akron12971a02018-01-03 16:38:10 +0100295 root.operation === 'and') {
296 root.append(cond);
Nils Diewald845282c2015-05-14 07:53:03 +0000297 }
298 else {
Akron12971a02018-01-03 16:38:10 +0100299 root.wrapOnRoot('and');
300 root.append(doc);
Nils Diewald845282c2015-05-14 07:53:03 +0000301 };
302
303 doc.key("creationDate");
304 doc.type("date");
305 doc.matchop("leq");
306 doc.value(todayStr);
307
308/*
309 {
310 "@type" : "koral:doc",
311 "key" : "creationDate",
312 "type" : "type:date",
313 "match" : "match:leq",
314 "value" : todayStr
315 }
316 this.root().append(cond);
317*/
318 this.update();
319 },
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000320
321 /**
322 * Get the generated json string
323 */
Nils Diewaldf219eb82015-01-07 20:15:42 +0000324 toJson : function () {
325 return this._root.toJson();
326 },
Nils Diewaldd599d542015-01-08 20:41:34 +0000327
Nils Diewald1fcb2ad2015-04-20 19:19:18 +0000328
329 /**
330 * Get the generated query string
331 */
Nils Diewaldd599d542015-01-08 20:41:34 +0000332 toQuery : function () {
333 return this._root.toQuery();
hebasta1c3ce0f2018-06-05 17:02:16 +0200334 },
335
hebastac8fa0782018-06-20 16:00:57 +0200336
337 /**
338 * Adds Corpus Statistic Button
339 **/
340 addStatBut : function(){
hebasta1c3ce0f2018-06-05 17:02:16 +0200341
hebastac8fa0782018-06-20 16:00:57 +0200342 //add div element 'corpStat' with id 'dCorpStat'
343 var dv = this._element.addE('div');
344 dv.id = "dCorpStat";
345
346 //add button
347 var bu = dv.addE('div');
348 bu.classList.add('bottom', 'button-single');
349 var stat = bu.addE('span');
350 stat.addT('Statistics');
351 //localize the buttons title attribute
352 stat.setAttribute('title', loc.VC_SHOWSTAT);
353 stat.classList.add('statistic');
354
355 /*
356 * In ECMAScript Language Specification this is set incorrectly for inner functions,
357 * therefore thatelement and that is defined.
358 */
359 var thatelement = this._element;
360 var that = this;
361
362 //show corpus statistic if button is clicked
363 stat.addEventListener('click', function(e){
364 e.halt();
365 statClass.showCorpStat(thatelement, that);
366 });
367
368 }
369
Nils Diewald3a2d8022014-12-16 02:45:41 +0000370 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000371});