1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
/* ldgallery - A static generator which turns a collection of tagged
-- pictures into a searchable web gallery.
--
-- Copyright (C) 2019-2020 Guillaume FOUET
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Affero General Public License as
-- published by the Free Software Foundation, either version 3 of the
-- License, or (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Affero General Public License for more details.
--
-- You should have received a copy of the GNU Affero General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Operation } from '@/@types/Operation';
import Navigation from '@/services/navigation';
export default class IndexFactory {
public static generateTags(root: Gallery.Item | null): Tag.Index {
let tagsIndex: Tag.Index = {};
if (root) IndexFactory.pushTagsForItem(tagsIndex, root);
return tagsIndex;
}
// Pushes all tags for a root item (and its children) to the index
private static pushTagsForItem(tagsIndex: Tag.Index, item: Gallery.Item): void {
if (item.properties.type === "directory") {
item.properties.items.forEach(item => this.pushTagsForItem(tagsIndex, item));
return; // Directories are not indexed
}
for (const tag of item.tags) {
const parts = tag.split(':');
let lastPart: string | null = null;
for (const part of parts) {
tagsIndex[part] = IndexFactory.pushPartToIndex(tagsIndex[part], part, item);
if (lastPart) {
const children = tagsIndex[lastPart].children;
children[part] = IndexFactory.pushPartToIndex(children[part], part, item);
}
lastPart = part;
}
}
}
private static pushPartToIndex(index: Tag.Node, part: string, item: Gallery.Item): Tag.Node {
if (!index) index = { tag: part, tagfiltered: Navigation.normalize(part), items: [], children: {} };
if (!index.items.includes(item)) index.items.push(item);
return index;
}
// ---
public static searchTags(tagsIndex: Tag.Index, filter: string, strict: boolean): Tag.Search[] {
let search: Tag.Search[] = [];
if (tagsIndex && filter) {
const operation = IndexFactory.extractOperation(filter);
if (operation !== Operation.INTERSECTION) filter = filter.slice(1);
if (filter.includes(":")) {
const filterParts = filter.split(":");
search = this.searchTagsFromFilterWithCategory(tagsIndex, operation, filterParts[0], filterParts[1], strict);
} else {
search = this.searchTagsFromFilter(tagsIndex, operation, filter, strict);
}
}
return search;
}
private static extractOperation(filter: string): Operation {
const first = filter.slice(0, 1);
switch (first) {
case Operation.ADDITION:
case Operation.SUBSTRACTION:
return first;
default:
return Operation.INTERSECTION;
}
}
private static searchTagsFromFilterWithCategory(
tagsIndex: Tag.Index,
operation: Operation,
category: string,
disambiguation: string,
strict: boolean
): Tag.Search[] {
category = Navigation.normalize(category);
disambiguation = Navigation.normalize(disambiguation);
return Object.values(tagsIndex)
.filter(node => strict || node.tagfiltered.includes(category))
.filter(node => !strict || node.tagfiltered === category)
.flatMap(node =>
Object.values(node.children)
.filter(child => strict || child.tagfiltered.includes(disambiguation))
.filter(child => !strict || child.tagfiltered === disambiguation)
.map(child => ({ ...child, parent: node, operation, display: `${operation}${node.tag}:${child.tag}` }))
);
}
private static searchTagsFromFilter(tagsIndex: Tag.Index, operation: Operation, filter: string, strict: boolean): Tag.Search[] {
filter = Navigation.normalize(filter);
return Object.values(tagsIndex)
.filter(node => strict || node.tagfiltered.includes(filter))
.filter(node => !strict || node.tagfiltered === filter)
.map(node => ({ ...node, operation, display: `${operation}${node.tag}` }));
}
}
|