import { Doc, DocListCast, Field, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { StrCast } from '../../fields/Types'; import { DocumentType } from '../documents/DocumentTypes'; export namespace SearchUtil { export type HighlightingResult = { [id: string]: { [key: string]: string[] } }; export function SearchCollection(collectionDoc: Opt, query: string) { const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; const blockedKeys = [ 'x', 'y', 'proto', 'width', 'layout_autoHeight', 'acl-Override', 'acl-Guest', 'embedContainer', 'zIndex', 'height', 'text_scrollHeight', 'text_height', 'cloneFieldFilter', 'isDataDoc', 'text_annotations', 'dragFactory_count', 'text_noTemplate', 'proto_embeddings', 'isSystem', 'layout_fieldKey', 'isBaseProto', 'xMargin', 'yMargin', 'links', 'layout', 'layout_keyValue', 'layout_fitWidth', 'type_collection', 'title_custom', 'freeform_panX', 'freeform_panY', 'freeform_scale', ]; query = query.toLowerCase(); const results = new Map(); if (collectionDoc) { const docs = DocListCast(collectionDoc[Doc.LayoutFieldKey(collectionDoc)]); 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(); SearchUtil.documentKeys(doc).forEach( key => (Field.toString(doc[key] as Field) + Field.toScriptString(doc[key] as Field)) .toLowerCase() // .includes(query) && hlights.add(key) ); 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(docs: Doc[], func: (depth: number, doc: Doc) => void) { let newarray: Doc[] = []; var depth = 0; const visited: Doc[] = []; while (docs.length > 0) { newarray = []; docs.filter(d => d && !visited.includes(d)).forEach(d => { visited.push(d); const fieldKey = Doc.LayoutFieldKey(d); const annos = !Field.toString(Doc.LayoutField(d) as Field).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++; } } }