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
|
import { ObservableMap } from 'mobx';
import { Doc, DocListCast, Field, FieldType, Opt } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
import { StrCast } from '../../fields/Types';
import { DocumentType } from '../documents/DocumentTypes';
import { DocOptions, FInfo } from '../documents/Documents';
export namespace SearchUtil {
export type HighlightingResult = { [id: string]: { [key: string]: string[] } };
export function SearchCollection(collectionDoc: Opt<Doc>, queryIn: string, matchKeyNames: boolean, onlyKeys?: string[]) {
const blockedTypes = [DocumentType.PRESSLIDE, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
const blockedKeys = matchKeyNames
? []
: Object.entries(DocOptions)
.filter(([, info]: [string, FInfo]) => !info?.searchable())
.map(([key]) => key);
const exact = queryIn.startsWith('=');
const query = queryIn.toLowerCase().split('=').lastElement();
const results = new ObservableMap<Doc, string[]>();
if (collectionDoc) {
const docs = DocListCast(collectionDoc[Doc.LayoutDataKey(collectionDoc)]);
// eslint-disable-next-line @typescript-eslint/ban-types
const docIDs: String[] = [];
SearchUtil.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => {
const dtype = StrCast(doc.type) as DocumentType;
if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth >= 0) {
const hlights = new Set<string>();
(onlyKeys ?? SearchUtil.documentKeys(doc)).forEach(
key =>
(val => (exact ? val === query.toLowerCase() : val.includes(query.toLowerCase())))(
matchKeyNames ? key : Field.toString(doc[key] as FieldType))
&& hlights.add(key)
); // prettier-ignore
blockedKeys.forEach(key => hlights.delete(key));
if (Array.from(hlights.keys()).length > 0) {
results.set(doc, Array.from(hlights.keys()));
}
}
docIDs.push(doc[Id]);
});
}
return results;
}
/**
* @param {Doc} doc - doc for which keys are returned
*
* This method returns a list of a document doc's keys.
*/
export function documentKeys(doc: Doc) {
const keys: { [key: string]: boolean } = {};
Doc.GetAllPrototypes(doc).map(proto =>
Object.keys(proto).forEach(key => {
keys[key] = false;
})
);
return Array.from(Object.keys(keys));
}
/**
* @param {Doc[]} docs - docs to be searched through recursively
* @param {number, Doc => void} func - function to be called on each doc
*
* This method iterates through an array of docs and all docs within those docs, calling
* the function func on each doc.
*/
export function foreachRecursiveDoc(docsIn: Doc[], func: (depth: number, doc: Doc) => void) {
let docs = docsIn;
let newarray: Doc[] = [];
let depth = 0;
const visited: Doc[] = [];
while (docs.length > 0) {
newarray = [];
// eslint-disable-next-line no-loop-func
docs.filter(d => d && !visited.includes(d)).forEach(d => {
visited.push(d);
const fieldKey = Doc.LayoutDataKey(d);
const annos = !Field.toString(Doc.LayoutField(d) as FieldType).includes('CollectionView');
const data = d[annos ? fieldKey + '_annotations' : fieldKey];
data && newarray.push(...DocListCast(data));
const sidebar = d[fieldKey + '_sidebar'];
sidebar && newarray.push(...DocListCast(sidebar));
func(depth, d);
});
docs = newarray;
depth++;
}
}
}
|