accrete 0.0.23__py3-none-any.whl → 0.0.25__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,358 +1,596 @@
1
- const queryBlock = document.getElementById('query-block');
2
- const queryInput = document.getElementById('query-input');
3
- const queryInputControl = document.getElementById('query-input-control');
4
- const queryInputSelect = document.getElementById('query-input-select');
5
- const queryInputSelectControl = document.getElementById('query-input-select-control');
6
- const queryInputApply = document.getElementById('query-input-apply');
7
- const queryParamsDropdown = document.getElementById('query-params-dropdown');
8
- const queryParamElements = document.querySelectorAll('.query-param');
9
- const queryParamsElements = document.querySelectorAll('.query-params');
10
- const queryTags = document.getElementById('query-tags');
11
- const modalFilterButton = document.getElementById('modal-filter-button');
12
- const filterPanel = document.getElementById('filter-panel');
13
- const filterModal = document.getElementById('filter-modal');
14
- const modalContent = document.getElementById('modal-content');
15
- const queryTextElement = document.getElementById('query-text');
16
- const queryTextNotificationParameterElement = document.getElementById('query-notification-parameter');
17
- let activeInput = queryInput;
18
-
19
- queryInput.addEventListener('focus', showQueryParams);
20
- queryInput.addEventListener('keyup', navigateQueryInput);
21
- queryInputApply.addEventListener('click', applyQueryInput);
22
- queryParamElements.forEach((element) => {
23
- element.addEventListener('keyup', navigateQueryParams);
24
- })
25
- queryParamElements.forEach((element) => {
26
- element.addEventListener('mouseup', clickQueryParams);
27
- })
28
- window.onclick = function(event) {
29
- const dontHide = [
30
- '#query-block', '#query-input', '#query-params-dropdown', '#param-input',
31
- '.query-param', '.query-params', '.query-param > p', '#query-operation',
32
- '#query-operation > span', '#query-operation-select',
33
- '#query-operation-select > option', '#query-params-dropdown > p > label',
34
- '#query-params-dropdown > p', '#query-params-dropdown > p > label > input',
35
- '#query-input-select', '#query-input-select > option'
36
- ];
37
- if (!event.target.matches(dontHide)) {
38
- queryParamsElements.forEach((element) => {
39
- element.classList.add('is-hidden');
40
- })
41
- if (!filterModal.classList.contains('is-active')) {
42
- queryParamsDropdown.classList.add('is-hidden');
43
- }
44
- }
45
- }
46
-
47
- modalFilterButton.onclick = function () {
48
- modalContent.appendChild(queryBlock);
49
- filterModal.classList.add('is-active');
50
- queryParamsDropdown.classList.remove('is-hidden');
51
- activeInput.select()
1
+ window.addEventListener('click', showHide);
2
+ buildQueryTagsFromUrl();
3
+ resetInput();
4
+
5
+ function buildQueryTagsFromUrl() {
6
+ const queryTags = document.getElementById('query-tags');
7
+ removeQueryTagsChildren();
8
+ const queryGroup = createQueryGroup();
9
+ queryTags.appendChild(queryGroup)
10
+ buildQueryFromQueryString(normalizeQueryString(), queryGroup, '&');
52
11
  }
53
12
 
54
- buildActiveQuery();
55
- setDefaultTerm();
56
-
57
-
58
- function closeFilterModal() {
59
- buildActiveQuery();
60
- filterModal.classList.remove('is-active');
61
- filterPanel.appendChild(queryBlock);
62
- }
63
-
64
-
65
- function setDefaultTerm() {
66
- const defaultTerm = queryInput.getAttribute('data-default-term');
67
- if (defaultTerm) {
68
- const element = getParamElement(defaultTerm);
69
- setParam(element);
70
- } else {
71
- activeInput.select();
13
+ function removeQueryTagsChildren() {
14
+ const queryTags = document.getElementById('query-tags');
15
+ while (queryTags.firstElementChild) {
16
+ queryTags.firstElementChild.remove();
72
17
  }
73
18
  }
74
19
 
75
- function buildActiveQuery() {
76
- const url = new URL(window.location.href);
77
- const query = url.searchParams.get('q');
78
- removeQueryTags();
79
- if (!query) {
80
- return
81
- }
82
- for(let block of JSON.parse(query)) {
83
- const tagBlock = buildTagBlock();
84
- for (let param of block) {
85
- if (Object.prototype.toString.apply(param) === '[object Object]') {
86
- const tag = buildActiveQueryTag(param);
87
- tagBlock.appendChild(tag);
88
- }
89
- if (Object.prototype.toString.apply(param) === '[object Array]') {
90
- const tagGroup = buildTagGroup();
91
- for (let orParam of param) {
92
- const tag = buildActiveQueryTag(orParam);
93
- tagGroup.appendChild(tag);
94
- tagBlock.appendChild(tagGroup);
95
- }
20
+ function buildQueryFromQueryString(jsonQuery, queryGroup, operator='&') {
21
+ console.log(jsonQuery)
22
+ for (let item of jsonQuery) {
23
+ if (isString(item)) {
24
+ operator = item
25
+ }
26
+ else if (isObject(item)) {
27
+ operator = queryGroup.firstElementChild && operator || null;
28
+ for (let term in item) {
29
+ queryGroup.appendChild(createQueryTag(term, operator, item[term]));
30
+ operator = '&';
96
31
  }
97
32
  }
98
- if (queryTags.childElementCount > 0) {
99
- const blockLabel = buildTagBlockLabel();
100
- queryTags.appendChild(blockLabel);
33
+ else if (isArray(item)) {
34
+ const subQueryGroup = createQueryGroup(
35
+ queryGroup.firstElementChild && operator || null
36
+ );
37
+ queryGroup.appendChild(createOperatorSelect(operator));
38
+ queryGroup.appendChild(subQueryGroup);
39
+ buildQueryFromQueryString(item, subQueryGroup, operator);
101
40
  }
102
- queryTags.appendChild(tagBlock)
103
41
  }
104
- if (queryTags.firstElementChild) {
105
- modalFilterButton.classList.add('is-success')
106
- } else {modalFilterButton.classList.remove('is-success')}
107
42
  }
108
43
 
109
- function removeQueryTags() {
110
- while (queryTags.firstElementChild) {
111
- queryTags.firstElementChild.remove();
112
- }
44
+ function queryStringFromJsonQuery() {
45
+ return JSON.stringify(buildJsonQueryFromHtml())
113
46
  }
114
47
 
115
- function buildActiveQueryTag(param) {
116
- let tag = null;
117
- let displayValue = '';
118
- let value = null;
119
- for (let p in param) {
120
- if (typeof param[p] == 'boolean') {
121
- value = param[p]
122
- }
123
- const element = getParamElement(p, value);
124
- const dataType = element.getAttribute('data-type');
125
- const text = getCompleteQueryText(element);
126
- if (dataType === 'bool') {
127
- displayValue = element.firstElementChild.text;
48
+ function buildJsonQueryFromHtml(tagGroup=null) {
49
+ const queryTags = (
50
+ tagGroup || document.getElementById('query-tags').firstElementChild);
51
+ let query = []
52
+ if (!queryTags) {
53
+ return query
54
+ }
55
+ for (let tag of queryTags.children) {
56
+ if (tag.classList.contains('query-tag-operator')) {
57
+ const operator = tag.firstElementChild.value
58
+ if (operator !== '&') {query.push(operator);}
128
59
  }
129
- else if (dataType === 'selection') {
130
- const option = element.querySelector(
131
- `.param-options option[value="${param[p]}"]`
132
- );
133
- displayValue = option.textContent;
60
+ else if (tag.classList.contains('query-group-operator')) {
61
+ const operator = tag.firstElementChild.firstElementChild.value;
62
+ if (operator !== '&') {query.push(operator);}
134
63
  }
135
- else {
136
- displayValue = param[p];
64
+ else if (tag.classList.contains('query-group')) {
65
+ query.push(buildJsonQueryFromHtml(tag))
137
66
  }
138
- tag = buildTag(text, p, param[p], displayValue);
139
- }
140
- return tag
141
- }
142
-
143
- function apply() {
144
- const tagBlocks = document.querySelectorAll('.query-tag-block');
145
- let url = new URL(window.location)
146
- let completeQuery = [];
147
- tagBlocks.forEach((tagBlock) => {
148
- let query = [];
149
- for (let tag of tagBlock.children) {
150
- if (tag.classList.contains('query-tag')) {
151
- const param = tag.getAttribute('data-param');
152
- let value = tag.getAttribute('data-value');
153
- if (tag.getAttribute('data-is-bool') === 'true') {
154
- value = value === 'true'
155
- }
156
- query.push({[param]: value})
67
+ else if (tag.classList.contains('query-tag-container')) {
68
+ if (tag.firstElementChild.classList.contains('query-tag-operator')) {
69
+ const operator = tag.firstElementChild.firstElementChild.value
70
+ if (operator !== '&') {query.push(operator);}
157
71
  }
158
- else if (tag.classList.contains('query-tag-group')) {
159
- let orQuery = [];
160
- for (let subTag of tag.children) {
161
- if (subTag.classList.contains('query-tag')) {
162
- const param = subTag.getAttribute('data-param');
163
- let value = subTag.getAttribute('data-value');
164
- if (subTag.getAttribute('data-is-bool') === 'true') {
165
- value = value === 'true'
166
- }
167
- orQuery.push({[param]: value});
72
+ if (tag.lastElementChild.classList.contains('query-tag')) {
73
+ const key = tag.lastElementChild.getAttribute(
74
+ 'data-param'
75
+ );
76
+ let value = tag.lastElementChild.getAttribute(
77
+ 'data-value'
78
+ );
79
+ if (typeof value === 'string') {
80
+ if (value.toLowerCase() === 'true') {
81
+ value = true
82
+ }
83
+ else if (value.toLowerCase() === 'false') {
84
+ value = false
168
85
  }
169
86
  }
170
- if (orQuery.length) {
171
- query.push(orQuery);
172
- }
87
+ query.push({[key]: value})
173
88
  }
174
89
  }
175
- completeQuery.push(query);
176
- })
177
- url.searchParams.set('q', JSON.stringify(completeQuery));
178
- let queryApply = document.getElementById('query-apply');
179
- let queryApplyEvent = new Event('click');
90
+ }
91
+ return query
92
+ }
93
+
94
+ function setQueryParam(queryParam) {
95
+ const queryLabel = getCompleteQueryText(queryParam);
96
+ const queryNotificationLabel = document.getElementById('query-label');
97
+ queryNotificationLabel.innerText = queryLabel;
98
+ queryNotificationLabel.classList.remove('is-hidden');
99
+ setInputType(queryParam);
100
+ if (queryParam.getAttribute('data-type') === 'bool') {
101
+ getActiveInput().value = queryParam.getAttribute('data-value')
102
+ applyInput();
103
+ resetInput();
104
+ }
105
+ }
106
+
107
+ function setInputType(queryParam) {
108
+ const queryInput = document.getElementById('query-input');
109
+ const query = getQueryFromElement(queryParam);
110
+ const dataType = queryParam.getAttribute('data-type');
111
+ const queryLabel = getCompleteQueryText(queryParam);
112
+ const step = queryParam.getAttribute('data-step') || null;
113
+ const inputControl = document.getElementById('query-input-control');
114
+ const selectControl = document.getElementById('query-input-select-control');
115
+ queryInput.setAttribute('data-param', query);
116
+ queryInput.setAttribute('data-text', queryLabel);
117
+ if (dataType === 'selection') {
118
+ const selectInput = document.getElementById('query-input-select');
119
+ const options = queryParam.querySelector('.param-options');
120
+ selectInput.innerHTML = options.innerHTML
121
+ selectControl.classList.remove('is-hidden');
122
+ inputControl.classList.add('is-hidden');
123
+ selectInput.setAttribute('data-param', query);
124
+ selectInput.setAttribute('data-text', queryLabel);
125
+ return
126
+ }
127
+ selectControl.classList.add('is-hidden');
128
+ inputControl.classList.remove('is-hidden');
129
+ queryInput.setAttribute('type', dataType);
130
+ if (step) {queryInput.step = step;}
131
+ queryInput.value = null;
132
+ queryInput.select()
133
+ }
134
+
135
+ function inputConfirm() {
136
+ if (this.event.key === 'Enter') {
137
+ applyInput();
138
+ }
139
+ }
140
+
141
+ function applyInput() {
142
+ const input = getActiveInput();
143
+ if (!input.reportValidity()) {
144
+ return
145
+ }
146
+ const edit = document.querySelector('.selected-query-edit');
147
+ const tag = document.querySelector('.selected-query-tag');
148
+ let group = (
149
+ document.querySelector('.selected-query-group')
150
+ || document.getElementById('query-tags').firstElementChild
151
+ );
152
+ if (!group) {
153
+ group = document.getElementById('query-tags').appendChild(createQueryGroup());
154
+ }
155
+ if (edit) {
156
+ editQueryTag(edit.closest('.query-tag-container'));
157
+ }
158
+ else if (tag) {
159
+ const container = tag.closest('.query-tag-container');
160
+ const previous = container.previousElementSibling;
161
+ if (previous) {
162
+ appendToTag(container);
163
+ }
164
+ else {
165
+ appendAfterTag(container);
166
+ }
167
+ }
168
+ else {
169
+ const operator = (
170
+ group.childElementCount
171
+ && document.getElementById('query-operation-select').value
172
+ || null
173
+ );
174
+ group.appendChild(createQueryTag(
175
+ input.getAttribute('data-param'),
176
+ operator,
177
+ input.value
178
+ ));
179
+ }
180
+ fetchQuery();
181
+ resetInput();
182
+ }
183
+
184
+ function editQueryTag(queryTagContainer) {
185
+ const input = getActiveInput();
186
+ const param = input.getAttribute('data-param');
187
+ const operator = (
188
+ queryTagContainer.previousElementSibling
189
+ && document.getElementById('query-operation-select').value
190
+ || null
191
+ );
192
+ const queryTag = createQueryTag(param, operator, input.value);
193
+ queryTagContainer.replaceWith(queryTag);
194
+ }
195
+
196
+ function appendToTag(queryTagContainer) {
197
+ const input = getActiveInput();
198
+ const operator = (
199
+ queryTagContainer.firstElementChild.classList.contains('query-tag-operator')
200
+ && queryTagContainer.firstElementChild.firstElementChild.value
201
+ || '&'
202
+ );
203
+ const operatorSelect = createOperatorSelect(operator);
204
+ const queryGroup = createQueryGroup();
205
+ queryTagContainer.after(operatorSelect);
206
+ if (queryTagContainer.firstElementChild.classList.contains('query-tag-operator')) {
207
+ queryTagContainer.firstElementChild.remove();
208
+ }
209
+ operatorSelect.after(queryGroup);
210
+
211
+ queryGroup.appendChild(queryTagContainer);
212
+ queryGroup.appendChild(createQueryTag(
213
+ input.getAttribute('data-param'),
214
+ document.getElementById('query-operation-select').value,
215
+ input.value)
216
+ );
217
+
218
+ }
219
+
220
+ function appendAfterTag(queryTagContainer) {
221
+ const input = getActiveInput();
222
+ const operator = document.getElementById('query-operation-select').value;
223
+ const tag = createQueryTag(
224
+ input.getAttribute('data-param'), operator, input.value
225
+ );
226
+ queryTagContainer.after(tag);
227
+ }
228
+
229
+ function resetInput() {
230
+ const input = document.getElementById('query-input');
231
+ const queryParam = getParamElement(input.getAttribute('data-default-term'))
232
+ const queryLabel = getCompleteQueryText(queryParam);
233
+ const queryNotificationLabel = document.getElementById('query-label');
234
+ queryNotificationLabel.innerText = queryLabel;
235
+ queryNotificationLabel.classList.remove('is-hidden');
236
+ setInputType(queryParam);
237
+ }
238
+
239
+ function fetchQuery(queryString=null) {
240
+ const queryApply = document.getElementById('query-apply');
241
+ const queryApplyEvent = new Event('click');
242
+ const url = new URL(window.location)
243
+ url.searchParams.set('q', queryString ?? queryStringFromJsonQuery());
180
244
  queryApply.setAttribute('hx-get', url.toString());
181
245
  htmx.process(queryApply);
182
246
  queryApply.dispatchEvent(queryApplyEvent);
183
247
  }
184
248
 
249
+ //-----------------------------------------------------------------------------
250
+ // Events
251
+ //-----------------------------------------------------------------------------
252
+
253
+ function showHide() {
254
+ const target = this.event.target;
255
+ const queryInput = document.getElementById('query-input-fieldset');
256
+ const queryParamDropDown = document.getElementById('query-params-dropdown');
257
+ const operation = document.getElementById('query-operation');
258
+ if (queryInput.contains(target)
259
+ || queryParamDropDown.contains(target)
260
+ || operation.contains(target)
261
+ ) {
262
+ queryParamDropDown.classList.remove('is-hidden');
263
+ return
264
+ }
265
+ queryParamDropDown.classList.add('is-hidden');
266
+ }
185
267
 
186
- function resetFilter() {
187
- const baseURl = window.location.href.split('?')[0];
188
- window.location.assign(baseURl);
268
+ function showFilterModal() {
269
+ const filterModal = document.getElementById('filter-modal');
270
+ const modalContent = document.getElementById('modal-content');
271
+ modalContent.appendChild(document.getElementById('query-block'));
272
+ filterModal.classList.add('is-active');
189
273
  }
190
274
 
191
- function applyQueryInput(event) {
192
- event.stopPropagation();
193
- const text = queryInput.getAttribute('data-text');
194
- const param = queryInput.getAttribute('data-param');
195
- const value = activeInput.value;
196
- const valid = activeInput.reportValidity();
197
- if (!valid) {return}
198
- let displayValue = value;
199
- if (activeInput === queryInputSelect) {
200
- displayValue = activeInput.selectedOptions[0].textContent;
201
- }
202
- if (value && param) {
203
- addQueryTag(text, param, value, displayValue);
204
- setDefaultTerm();
205
- } else if (value && !param) {
206
- queryTextElement.classList.add('is-hidden');
207
- queryTextNotificationParameterElement.classList.remove('is-hidden');
208
- resetQueryInput();
209
- } else if (!value && param) {
210
- activeInput.select();
211
- } else if (!value && !param) {
212
- resetQueryInput();
213
- }
214
- }
215
-
216
- function showQueryParams() {
217
- document.getElementById('query-params-dropdown').classList.remove(
218
- 'is-hidden'
219
- );
275
+ function hideFilterModal() {
276
+ const filterModal = document.getElementById('filter-modal');
277
+ const panel = document.getElementById('filter-panel');
278
+ panel.appendChild(document.getElementById('query-block'));
279
+ filterModal.classList.remove('is-active');
220
280
  }
221
281
 
222
- function navigateQueryInput(event) {
223
- if (event.key === 'Enter') {
224
- applyQueryInput(event);
282
+ function toggleParams() {
283
+ const target = this.event.target
284
+ const nextSibling = target.nextElementSibling;
285
+ if (!nextSibling) {
286
+ return
287
+ }
288
+ if (nextSibling.classList.contains('query-params')) {
289
+ nextSibling.classList.toggle('is-hidden');
225
290
  }
226
- if (event.key === 'Escape') {
227
- queryParamsDropdown.classList.add('is-hidden');
291
+ if (nextSibling.classList.contains('param-options')) {
292
+ setQueryParam(nextSibling.parentElement);
228
293
  }
229
- if (event.key === 'ArrowDown') {
230
- if (queryParamsDropdown.classList.contains('is-hidden')) {
231
- queryParamsDropdown.classList.remove('is-hidden');
294
+ }
295
+
296
+ function deleteQueryTag(event) {
297
+ const tagContainer = event.currentTarget.closest('.query-tag-container');
298
+ const previousTagContainer = tagContainer.previousElementSibling;
299
+ const nextTagContainer = tagContainer.nextElementSibling;
300
+ const queryGroup = tagContainer.parentElement;
301
+
302
+ if (
303
+ previousTagContainer
304
+ && nextTagContainer
305
+ && tagContainer.firstElementChild.classList.contains('query-tag-operator')
306
+ ) {
307
+ if (nextTagContainer.childElementCount === 1) {
308
+ nextTagContainer.prepend(tagContainer.firstElementChild)
309
+ }
310
+ }
311
+ tagContainer.remove();
312
+ if (
313
+ !previousTagContainer
314
+ && nextTagContainer
315
+ && nextTagContainer.firstElementChild.classList.contains('query-tag-operator')
316
+ ) {
317
+ nextTagContainer.firstElementChild.remove();
318
+ }
319
+ if (!queryGroup.childElementCount) {
320
+ queryGroup.previousElementSibling && queryGroup.previousElementSibling.remove();
321
+ queryGroup.remove();
322
+ }
323
+ else if (
324
+ queryGroup.firstElementChild
325
+ && queryGroup.firstElementChild.classList.contains('query-group-operator')
326
+ ) {
327
+ if (!isFirstQueryGroup(queryGroup)) {
328
+ queryGroup.previousElementSibling && queryGroup.previousElementSibling.remove();
329
+ while (queryGroup.childElementCount) {
330
+ queryGroup.after(queryGroup.lastElementChild);
331
+ }
332
+ queryGroup.remove();
232
333
  }
233
- if (!queryInput.type in ['date', 'datetime-local', 'number']) {
234
- queryParamsDropdown.firstElementChild.focus({ focusVisible: true });
334
+ else {
335
+ queryGroup.firstElementChild.remove();
336
+ const subQueryGroup = queryGroup.firstElementChild;
337
+ while (subQueryGroup.childElementCount) {
338
+ subQueryGroup.before(subQueryGroup.firstElementChild);
339
+ }
340
+ subQueryGroup.remove();
235
341
  }
342
+ }
343
+ fetchQuery();
344
+ }
236
345
 
346
+ function setSelectedTag(event) {
347
+ event.stopPropagation();
348
+ const toDeactivate = event.currentTarget.classList.contains(
349
+ 'selected-query-tag'
350
+ );
351
+ unselectAll();
352
+ if (!toDeactivate) {
353
+ event.currentTarget.classList.add('selected-query-tag');
237
354
  }
238
355
  }
239
356
 
240
- function clickQueryParams(event) {
357
+ function setSelectedQueryGroup(event) {
241
358
  event.stopPropagation();
242
- const queryParam = event.target.parentElement;
243
- const queryParams = queryParam.querySelector(
244
- '.query-params'
359
+ const toDeactivate = event.currentTarget.classList.contains(
360
+ 'selected-query-group-selector'
245
361
  );
246
- if (queryParams && queryParams.classList.contains('is-hidden')) {
247
- queryParams.classList.remove('is-hidden');
362
+ unselectAll();
363
+ if (!toDeactivate) {
364
+ event.currentTarget.classList.add('selected-query-group-selector');
365
+ const parent = event.currentTarget.closest('.query-group-operator');
366
+ parent.nextElementSibling.classList.add('selected-query-group');
248
367
  }
249
- else {
250
- const subQueryParams = queryParam.querySelectorAll(
251
- '.query-params'
252
- );
253
- subQueryParams.forEach((element) => {
254
- element.classList.add('is-hidden');
255
- })
368
+ }
369
+
370
+ function setSelectedEdit(event) {
371
+ event.stopPropagation();
372
+ const toDeactivate = event.currentTarget.classList.contains(
373
+ 'selected-query-edit'
374
+ );
375
+ unselectAll();
376
+ if (toDeactivate) {
377
+ return
256
378
  }
257
- if (!queryParam.querySelector('.query-params')) {
258
- setParam(queryParam)
379
+ const tag = event.currentTarget.closest('.query-tag');
380
+ event.currentTarget.classList.add('selected-query-edit');
381
+ if (tag.getAttribute('data-is-bool') !== 'true') {
382
+ setQueryParam(getParamElement(
383
+ tag.getAttribute('data-param'),
384
+ tag.getAttribute('data-value')
385
+ ));
259
386
  }
387
+
260
388
  }
261
389
 
262
- function setParam(queryParam) {
263
- const completeParam = getCompleteQueryParam(queryParam);
264
- const dataType = queryParam.getAttribute('data-type');
265
- const text = getCompleteQueryText(queryParam);
266
- resetQueryInput();
267
- setInputType(dataType, completeParam, text,
268
- queryParam.getAttribute('data-step') || null
390
+ function unselectAll() {
391
+ unselectAllQueryGroups();
392
+ unselectAllQueryTags();
393
+ unselectAllQueryEdits();
394
+ }
395
+
396
+ function unselectAllQueryTags() {
397
+ document.querySelectorAll('.selected-query-tag').forEach(
398
+ (tag) => {
399
+ tag.classList.remove('selected-query-tag')
400
+ }
401
+ );
402
+ }
403
+
404
+ function unselectAllQueryGroups() {
405
+ document.querySelectorAll(
406
+ '.selected-query-group, .selected-query-group-selector'
407
+ ).forEach(
408
+ (tag) => {
409
+ tag.classList.remove(
410
+ 'selected-query-group', 'selected-query-group-selector'
411
+ )
412
+ }
413
+ );
414
+ }
415
+
416
+ function unselectAllQueryEdits() {
417
+ document.querySelectorAll('.selected-query-edit').forEach(
418
+ (tag) => {tag.classList.remove('selected-query-edit')}
269
419
  );
270
- queryInput.scrollIntoView({behavior: 'smooth', block: 'end'});
271
- queryInput.select();
420
+ }
421
+
422
+ function changeOperator(event) {
423
+ fetchQuery();
424
+ }
425
+
426
+ //-----------------------------------------------------------------------------
427
+ // Elements
428
+ //-----------------------------------------------------------------------------
429
+
430
+ function createQueryGroup(operator=null) {
431
+ const queryGroup = document.createElement('div');
432
+ queryGroup.classList.add('query-group');
433
+ return queryGroup
434
+ }
435
+
436
+ function createQueryTag(term, operator, value=null) {
437
+ let displayValue;
438
+ const element = getParamElement(term, value);
439
+ const dataType = element.getAttribute('data-type');
440
+ const text = getCompleteQueryText(element);
272
441
  if (dataType === 'bool') {
273
- let value = queryParam.getAttribute('data-value');
274
- value = value === 'True';
275
- addQueryTag(text, completeParam, value, queryParam.firstElementChild.text);
276
- setInputType('text', '', '');
277
- queryTextNotificationParameterElement.classList.add('is-hidden')
278
- queryTextElement.innerText = '';
279
- queryTextElement.classList.add('is-hidden')
442
+ displayValue = element.firstElementChild.text;
280
443
  }
281
444
  else if (dataType === 'selection') {
282
- const options = queryParam.querySelector('.param-options');
283
- activeInput = queryInputSelect;
284
- queryInputSelect.innerHTML = options.innerHTML;
285
- queryInputSelectControl.classList.remove('is-hidden');
286
- queryInputControl.classList.add('is-hidden');
287
- queryInput.setAttribute('data-param', completeParam);
288
- queryInput.setAttribute('data-text', text);
289
- queryTextNotificationParameterElement.classList.add('is-hidden')
290
- queryTextElement.innerText = text;
291
- queryTextElement.classList.remove('is-hidden')
292
-
445
+ const option = element.querySelector(
446
+ `.param-options > option[value="${value}"]`
447
+ );
448
+ displayValue = option.textContent;
293
449
  }
450
+ else {
451
+ displayValue = value;
452
+ }
453
+ return createTag(text, term, value, displayValue, operator);
294
454
  }
295
455
 
296
- function navigateQueryParams(event) {
297
- event.stopPropagation();
298
- let elementToFocus = null;
299
- const isSubQuery = event.target.parentElement.classList.contains(
300
- 'query-params'
456
+ function createTag(text, param, value, displayValue, operator) {
457
+ const tagContainer = document.createElement('div');
458
+ const tag = document.createElement('div');
459
+ const tagEditDiv = document.createElement('div');
460
+ const tagEditIcon = document.createElement('div');
461
+ const tagText = document.createElement('span');
462
+ const tagDeleteDiv = document.createElement('div');
463
+ const tagDeleteButton = document.createElement('button');
464
+ tagEditDiv.classList.add('query-tag-edit')
465
+ tagEditDiv.addEventListener('click', setSelectedEdit);
466
+ tagEditDiv.appendChild(tagEditIcon);
467
+ tagEditIcon.classList.add('icon-edit', 'query-tag-edit-icon');
468
+ tagText.classList.add('query-tag-text', 'is-size-7', 'is-unselectable');
469
+ tagText.innerText = text.concat(displayValue && `: ${displayValue}` || '');
470
+ tagDeleteDiv.classList.add('query-tag-delete')
471
+ tagDeleteButton.classList.add('delete', 'mx-1');
472
+ tagDeleteDiv.appendChild(tagDeleteButton);
473
+ tagDeleteButton.addEventListener('click', deleteQueryTag);
474
+ tag.addEventListener('click', setSelectedTag);
475
+ tag.classList.add('query-tag');
476
+ tag.tabIndex = -1;
477
+ tag.setAttribute('data-param', param);
478
+ tag.setAttribute('data-value', value);
479
+ tag.setAttribute('data-is-bool', String(isBoolean(value)));
480
+ tag.appendChild(tagEditDiv);
481
+ tag.appendChild(tagText);
482
+ tag.appendChild(tagDeleteDiv);
483
+ if (operator && operator !== '&') {
484
+ tagContainer.appendChild(createOperatorDiv(operator));
485
+ }
486
+ tagContainer.appendChild(tag);
487
+ tagContainer.classList.add('query-tag-container');
488
+ return tagContainer
489
+ }
490
+
491
+ function createOperatorDiv(operator) {
492
+ const operatorContainer = document.createElement('div');
493
+ const operatorDiv = document.createElement('div');
494
+ operatorContainer.classList.add('query-tag-operator');
495
+ operatorDiv.innerText = getOperatorLabel(operator);
496
+ operatorDiv.value = operator;
497
+ operatorDiv.setAttribute('value', operator);
498
+ operatorContainer.appendChild(operatorDiv);
499
+ return operatorContainer
500
+ }
501
+
502
+ function createOperatorSelect(operator) {
503
+ const selectContainer = document.createElement('div');
504
+ const selectDiv = document.createElement('div');
505
+ const select = document.createElement('select');
506
+ const andOption = document.createElement('option');
507
+ const orOption = document.createElement('option');
508
+ const xorOption = document.createElement('option');
509
+ const groupSelectorDiv = document.createElement('div');
510
+ const groupSelector = document.createElement('button');
511
+ groupSelectorDiv.classList.add('query-group-selector');
512
+ groupSelector.classList.add('icon-select');
513
+ groupSelectorDiv.appendChild(groupSelector);
514
+ andOption.value = '&';
515
+ andOption.innerText = getOperatorLabel('&');
516
+ orOption.innerText = getOperatorLabel('|');
517
+ xorOption.innerText = getOperatorLabel('^');
518
+ orOption.value = '|';
519
+ xorOption.value = '^';
520
+ if (operator === '&') {andOption.setAttribute('selected', '')}
521
+ else if (operator === '|') {orOption.setAttribute('selected', '')}
522
+ else if (operator === '^') {xorOption.setAttribute('selected', '')}
523
+ select.appendChild(andOption);
524
+ select.appendChild(orOption);
525
+ select.addEventListener('change', changeOperator);
526
+ selectDiv.classList.add('select')
527
+ selectDiv.appendChild(select);
528
+ if (operator === '^') {select.appendChild(xorOption);}
529
+ selectContainer.classList.add(
530
+ 'query-group-operator', 'is-size-7', 'mr-1', 'mb-1'
301
531
  );
302
- const hasSubQuery= event.target.querySelectorAll('.query-params');
532
+ selectContainer.appendChild(selectDiv);
533
+ selectContainer.appendChild(groupSelectorDiv);
534
+ groupSelector.addEventListener('click', setSelectedQueryGroup);
535
+ return selectContainer
536
+ }
537
+
538
+ //-----------------------------------------------------------------------------
539
+ // Helper
540
+ //-----------------------------------------------------------------------------
303
541
 
304
- if (event.key === 'ArrowUp') {
305
- if (event.target.previousElementSibling) {
306
- elementToFocus = event.target.previousElementSibling;
542
+ function normalizeQueryString(queryString=null, left=null) {
543
+ queryString = queryString || JSON.parse(
544
+ new URL(window.location.href).searchParams.get('q') || '[]'
545
+ );
546
+ let normalizedQueryString = []
547
+ for (let item of queryString) {
548
+ if (isString(item)) {
549
+ normalizedQueryString.push(item)
550
+ left = item
307
551
  }
308
- else if (event.target.parentElement.id === 'query-params-dropdown') {
309
- elementToFocus = queryInput;
310
- elementToFocus.select();
552
+ else if (isObject(item)) {
553
+ for (let k in item) {
554
+ if (left && !isString(left)) {
555
+ normalizedQueryString.push('&')
556
+ }
557
+ normalizedQueryString.push({[k]: item[k]})
558
+ left = item
559
+ }
311
560
  }
312
- else if (event.target.parentElement.classList.contains('query-param')) {}
313
- }
314
- else if (event.key === 'ArrowDown') {
315
- elementToFocus = event.target.nextElementSibling;
316
- }
317
- else if (event.key === 'Escape') {
318
- elementToFocus = queryInput;
319
- elementToFocus.select();
320
- queryParamsElements.forEach((element) => {
321
- element.classList.add('is-hidden');
322
- })
323
-
324
- }
325
- else if (event.key === 'ArrowRight') {
326
- if (hasSubQuery) {
327
- const queryParams= event.target.querySelector('.query-params');
328
- if (queryParams) {
329
- queryParams.classList.remove('is-hidden');
330
- elementToFocus = queryParams.firstElementChild;
561
+ else if (isArray(item)) {
562
+ if (left && !isString(left)) {
563
+ normalizedQueryString.push('&')
331
564
  }
565
+ normalizedQueryString.push(normalizeQueryString(item))
566
+ left = item
332
567
  }
333
568
  }
334
- else if (event.key === 'ArrowLeft') {
335
- if (isSubQuery) {
336
- elementToFocus = event.target.parentElement.parentElement;
337
- event.target.parentElement.classList.add('is-hidden');
338
- }
569
+ return normalizedQueryString
570
+ }
571
+
572
+ function getOperatorLabel(operator) {
573
+ const queryTags = document.getElementById('query-tags');
574
+ if (operator === '&') {
575
+ return queryTags.getAttribute('data-and-label')
339
576
  }
340
- if (elementToFocus) {
341
- elementToFocus.focus();
577
+ else if (operator === '|') {
578
+ return queryTags.getAttribute('data-or-label')
342
579
  }
343
-
344
- if (event.key === 'Enter') {
345
- const param = event.target.getAttribute('data-param');
346
- const value = queryInput.value;
347
- const text = getCompleteQueryText(event.target)
348
- addQueryTag(text, param, value);
580
+ else if (operator === '^') {
581
+ return queryTags.getAttribute('data-xor-label')
349
582
  }
350
583
  }
351
584
 
352
- function getParamElement(param, value=null) {
585
+ function getParamElement(param, value=null){
586
+ const queryParamsDropdown = document.getElementById('query-params-dropdown');
353
587
  const invert = param.startsWith('~');
354
- if (invert) {
355
- param = param.slice(1);
588
+ param = invert && param.slice(1) || param
589
+ if (value === 'True') {
590
+ value = true
591
+ }
592
+ if (value === 'False') {
593
+ value = false
356
594
  }
357
595
  const parts = param.split('__');
358
596
  let paramElement = queryParamsDropdown.querySelector(
@@ -364,14 +602,12 @@ function getParamElement(param, value=null) {
364
602
  if (invert) {
365
603
  selector += `[data-param-invert="true"]`;
366
604
  }
367
- if (typeof value == 'boolean') {
605
+ if (typeof value === 'boolean') {
368
606
  if (value) {
369
607
  selector += `[data-value="True"]`;
370
608
  } else {
371
609
  selector += `[data-value="False"]`;
372
610
  }
373
- } else if (value) {
374
- selector += `[data-value="${value}"]`;
375
611
  }
376
612
  }
377
613
  paramElement = paramElement.querySelector(selector);
@@ -379,8 +615,13 @@ function getParamElement(param, value=null) {
379
615
  return paramElement
380
616
  }
381
617
 
382
- function getQueryTextParts(element) {
383
- let currentElement = element;
618
+ function getCompleteQueryText(paramElement) {
619
+ let names = getQueryTextParts(paramElement);
620
+ return names.join(' \u2192 ')
621
+ }
622
+
623
+ function getQueryTextParts(paramElement) {
624
+ let currentElement = paramElement;
384
625
  let names = [];
385
626
  names.push(currentElement.firstElementChild.innerText);
386
627
  while (currentElement.parentElement.parentElement.classList.contains('query-param')) {
@@ -390,13 +631,8 @@ function getQueryTextParts(element) {
390
631
  return names.reverse()
391
632
  }
392
633
 
393
- function getCompleteQueryText(element) {
394
- let names = getQueryTextParts(element);
395
- return names.join(' \u2192 ')
396
- }
397
-
398
- function getCompleteQueryParam(element) {
399
- let currentElement = element;
634
+ function getQueryFromElement(queryParam) {
635
+ let currentElement = queryParam;
400
636
  const invert = currentElement.getAttribute('data-param-invert');
401
637
  let params = [];
402
638
  params.push(currentElement.getAttribute('data-param'));
@@ -412,188 +648,39 @@ function getCompleteQueryParam(element) {
412
648
  return paramString
413
649
  }
414
650
 
415
- function addQueryTag(text, param, value, displayValue) {
416
- const operation = document.getElementById('query-operation-select').value;
417
- const selectedTag= queryTags.querySelector('.selected-query-tag');
418
- if (!queryTags.childElementCount) {
419
- let tagBlock = buildTagBlock();
420
- let tag = buildTag(text, param, value, displayValue);
421
- tagBlock.appendChild(tag);
422
- queryTags.appendChild(tagBlock);
423
- }
424
- else if (operation === 'and' && selectedTag) {
425
- selectedTag.parentElement.appendChild(buildTag(text, param, value, displayValue));
426
- }
427
- else if (operation === 'and' && !selectedTag) {
428
- queryTags.lastElementChild.appendChild(buildTag(text, param, value, displayValue));
429
- }
430
- else if (operation === 'or' && selectedTag) {
431
- if (selectedTag.classList.contains('query-tag-group')) {
432
- let tag = buildTag(text, param, value, displayValue);
433
- selectedTag.appendChild(tag);
434
- }
435
- else {
436
- let tagGroup = buildTagGroup();
437
- let tag = buildTag(text, param, value, displayValue);
438
- selectedTag.parentElement.insertBefore(tagGroup, selectedTag);
439
- tagGroup.appendChild(selectedTag);
440
- tagGroup.appendChild(tag);
441
- selectedTag.classList.remove('selected-query-tag');
442
- tagGroup.classList.add('selected-query-tag');
443
- }
444
- }
445
- else if (operation === 'or' && !selectedTag) {
446
- const tagBlock = buildTagBlock();
447
- const tag = buildTag(text, param, value, displayValue)
448
- const tagBlockLabel = buildTagBlockLabel();
449
- tagBlock.appendChild(tag);
450
- queryTags.appendChild(tagBlockLabel);
451
- queryTags.appendChild(tagBlock);
452
- }
453
- // if (!filterModal.classList.contains('is-active')) {
454
- // apply();
455
- // }
456
- apply();
457
- }
458
-
459
- function unselectSingleObject(pk) {
460
- const tag = buildActiveQueryTag(JSON.parse(`{"~id__exact": ${pk}}`));
461
- if (!queryTags.childElementCount) {
462
- const tagBlock = buildTagBlock();
463
- tagBlock.appendChild(tag);
464
- queryTags.appendChild(tagBlock);
465
- } else {
466
- queryTags.lastElementChild.appendChild(tag);
467
- }
468
- // if (!filterModal.classList.contains('is-active')) {
469
- // apply();
470
- // }
471
- apply();
472
- }
473
-
474
- function buildTagBlock() {
475
- let container = document.createElement('div');
476
- container.classList.add('query-tag-block');
477
- return container
478
- }
479
-
480
- function buildTagBlockLabel() {
481
- const blockLabel = document.createElement('span');
482
- blockLabel.innerText = queryTags.getAttribute('data-or-label');
483
- blockLabel.classList.add('query-or-label');
484
- return blockLabel
485
- }
486
-
487
- function buildTagGroup() {
488
- let container= document.createElement('div');
489
- container.classList.add('query-tag-group');
490
- container.addEventListener('click', setSelectedTag);
491
- return container
492
- }
493
-
494
- function buildTag(text, param, value, displayValue) {
495
- let container = document.createElement('div');
496
- let textSpan = document.createElement('span');
497
- let deleteSpan = document.createElement('span');
498
- let deleteButton = document.createElement('button');
499
- container.classList.add('query-tag');
500
- container.tabIndex = -1;
501
- textSpan.classList.add('query-tag-text', 'is-size-6', 'is-unselectable');
502
- if (displayValue) {
503
- text = text.concat(': ', displayValue);
504
- }
505
- textSpan.innerText = text;
506
- deleteSpan.classList.add(
507
- 'is-flex', 'is-flex-direction-column',
508
- 'is-justify-content-space-around', 'ml-1'
509
- );
510
- deleteButton.classList.add('delete');
511
- deleteSpan.appendChild(deleteButton);
512
- container.appendChild(textSpan);
513
- container.appendChild(deleteSpan);
514
- container.addEventListener('click', setSelectedTag);
515
- deleteButton.addEventListener('click', deleteTag);
516
- container.setAttribute('data-param', param);
517
- container.setAttribute('data-value', value);
518
- container.setAttribute('data-is-bool', String(typeof value === 'boolean'));
519
- return container
651
+ function getActiveInput() {
652
+ const input = document.getElementById('query-input');
653
+ return (
654
+ input.parentElement.classList.contains('is-hidden')
655
+ && document.getElementById('query-input-select')
656
+ || input
657
+ )
520
658
  }
521
659
 
522
- function setSelectedTag(event) {
523
- event.stopPropagation();
524
- let target = event.currentTarget
525
- if (target.parentElement.classList.contains('query-tag-group')) {
526
- target = event.currentTarget.parentElement;
527
- }
528
- const toDeactivate = target.classList.contains('selected-query-tag');
529
- const selectedTags= queryBlock.querySelectorAll('.selected-query-tag');
530
- selectedTags.forEach((element) => {
531
- element.classList.remove('selected-query-tag');
532
- })
533
- if (!toDeactivate) {
534
- target.classList.add('selected-query-tag');
535
- }
660
+ function isObject(item) {
661
+ return Object.prototype.toString.apply(item) === '[object Object]'
536
662
  }
537
663
 
538
- function deleteTag(event) {
539
- event.stopPropagation();
540
- const tag = event.target.closest('.query-tag');
541
- let tagGroup = tag.parentElement;
542
- tagGroup.removeChild(tag);
543
- while (tagGroup.classList.contains('query-tag-group')) {
544
- if (!tagGroup.childElementCount) {
545
- let parent = tagGroup.parentElement;
546
- parent.removeChild(tagGroup);
547
- tagGroup = parent;
548
- }
549
- else if (tagGroup.childElementCount === 1) {
550
- let parent = tagGroup.parentElement
551
- let child = tagGroup.firstElementChild
552
- if (tagGroup.classList.contains('selected-query-tag')) {
553
- child.classList.add('selected-query-tag');
554
- }
555
- parent.appendChild(child);
556
- parent.removeChild(tagGroup);
557
- tagGroup = parent.parentElement;
558
- }
559
- else {
560
- break
561
- }
562
664
 
563
- }
564
- if (tagGroup.classList.contains('query-tag-block') && !tagGroup.childElementCount) {
565
- const previousSibling = tagGroup.previousElementSibling;
566
- const nextSibling = tagGroup.nextElementSibling;
567
- if (previousSibling && previousSibling.classList.contains('query-or-label')) {
568
- tagGroup.parentElement.removeChild(previousSibling);
569
- }
570
- if (nextSibling && nextSibling.classList.contains('query-or-label')) {
571
- nextSibling.remove();
572
- }
573
- tagGroup.parentElement.removeChild(tagGroup);
574
- }
575
- // if (!filterModal.classList.contains('is-active')) {
576
- // apply();
577
- // }
578
- apply()
665
+ function isArray(item) {
666
+ return Object.prototype.toString.apply(item) === '[object Array]'
579
667
  }
580
668
 
581
- function setInputType(inputType, param, text, step=null) {
582
- queryInput.setAttribute('type', inputType);
583
- queryInput.setAttribute('data-param', param);
584
- queryInput.setAttribute('data-text', text);
585
- queryTextNotificationParameterElement.classList.add('is-hidden');
586
- queryTextElement.classList.remove('is-hidden');
587
- queryTextElement.innerText = text;
588
- if (step) {
589
- queryInput.step = step
590
- }
669
+
670
+ function isString(item) {
671
+ return Object.prototype.toString.apply(item) === '[object String]'
591
672
  }
592
673
 
593
- function resetQueryInput() {
594
- queryInput.setAttribute('type', 'text');
595
- queryInputSelectControl.classList.add('is-hidden');
596
- queryInputControl.classList.remove('is-hidden');
597
- activeInput = queryInput;
598
- queryInput.select();
674
+ function isFirstQueryGroup(queryGroup) {
675
+ return queryGroup.parentElement.id === 'query-tags'
599
676
  }
677
+
678
+ function isBoolean(value) {
679
+ if (typeof value === 'boolean') {
680
+ return true
681
+ }
682
+ if (typeof value === 'string') {
683
+ return value.toLowerCase() === 'false' || value.toLowerCase() === 'true'
684
+ }
685
+ return false
686
+ }