aboutsummaryrefslogtreecommitdiff
path: root/src/new_fields/Doc.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/new_fields/Doc.ts')
-rw-r--r--src/new_fields/Doc.ts220
1 files changed, 143 insertions, 77 deletions
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 6aad4a6be..e0ab5d97c 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -1,4 +1,4 @@
-import { observable, ObservableMap, runInAction, action } from "mobx";
+import { observable, ObservableMap, runInAction, action, untracked } from "mobx";
import { alias, map, serializable } from "serializr";
import { DocServer } from "../client/DocServer";
import { DocumentType } from "../client/documents/DocumentTypes";
@@ -10,17 +10,18 @@ import { ObjectField } from "./ObjectField";
import { PrefetchProxy, ProxyField } from "./Proxy";
import { FieldId, RefField } from "./RefField";
import { listSpec } from "./Schema";
-import { ComputedField } from "./ScriptField";
+import { ComputedField, ScriptField } from "./ScriptField";
import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast, ToConstructor } from "./Types";
import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util";
import { intersectRect } from "../Utils";
import { UndoManager } from "../client/util/UndoManager";
+import { computedFn } from "mobx-utils";
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
const onDelegate = Object.keys(doc).includes(key);
- let field = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+ const field = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
if (Field.IsField(field)) {
return (onDelegate ? "=" : "") + (field instanceof ComputedField ? `:=${field.script.originalScript}` : Field.toScriptString(field));
}
@@ -254,9 +255,9 @@ export namespace Doc {
return GetT(doc, "isPrototype", "boolean", true);
}
export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) {
- let hasProto = doc.proto instanceof Doc;
- let onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
- let onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1;
+ const hasProto = doc.proto instanceof Doc;
+ const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
+ const onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1;
if (onDeleg || !hasProto || (!onProto && !defaultProto)) {
doc[key] = value;
} else doc.proto![key] = value;
@@ -289,14 +290,13 @@ export namespace Doc {
* @param fields the fields to project onto the target. Its type signature defines a mapping from some string key
* to a potentially undefined field, where each entry in this mapping is optional.
*/
- export function assign<K extends string>(doc: Doc, fields: Partial<Record<K, Opt<Field>>>) {
+ export function assign<K extends string>(doc: Doc, fields: Partial<Record<K, Opt<Field>>>, skipUndefineds: boolean = false) {
for (const key in fields) {
if (fields.hasOwnProperty(key)) {
const value = fields[key];
- // Do we want to filter out undefineds?
- // if (value !== undefined) {
- doc[key] = value;
- // }
+ if (!skipUndefineds || value !== undefined) { // Do we want to filter out undefineds?
+ doc[key] = value;
+ }
}
}
return doc;
@@ -305,10 +305,10 @@ export namespace Doc {
// compare whether documents or their protos match
export function AreProtosEqual(doc?: Doc, other?: Doc) {
if (!doc || !other) return false;
- let r = (doc === other);
- let r2 = (Doc.GetProto(doc) === other);
- let r3 = (Doc.GetProto(other) === doc);
- let r4 = (Doc.GetProto(doc) === Doc.GetProto(other) && Doc.GetProto(other) !== undefined);
+ const r = (doc === other);
+ const r2 = (Doc.GetProto(doc) === other);
+ const r3 = (Doc.GetProto(other) === doc);
+ const r4 = (Doc.GetProto(doc) === Doc.GetProto(other) && Doc.GetProto(other) !== undefined);
return r || r2 || r3 || r4;
}
@@ -317,7 +317,7 @@ export namespace Doc {
return doc && (Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc));
}
export function GetDataDoc(doc: Doc): Doc {
- let proto = Doc.GetProto(doc);
+ const proto = Doc.GetProto(doc);
return proto === doc ? proto : Doc.GetDataDoc(proto);
}
@@ -342,9 +342,9 @@ export namespace Doc {
if (listDoc[key] === undefined) {
Doc.GetProto(listDoc)[key] = new List<Doc>();
}
- let list = Cast(listDoc[key], listSpec(Doc));
+ const list = Cast(listDoc[key], listSpec(Doc));
if (list) {
- let ind = list.indexOf(doc);
+ const ind = list.indexOf(doc);
if (ind !== -1) {
list.splice(ind, 1);
return true;
@@ -356,10 +356,10 @@ export namespace Doc {
if (listDoc[key] === undefined) {
Doc.GetProto(listDoc)[key] = new List<Doc>();
}
- let list = Cast(listDoc[key], listSpec(Doc));
+ const list = Cast(listDoc[key], listSpec(Doc));
if (list) {
if (allowDuplicates !== true) {
- let pind = list.reduce((l, d, i) => d instanceof Doc && d[Id] === doc[Id] ? i : l, -1);
+ const pind = list.reduce((l, d, i) => d instanceof Doc && d[Id] === doc[Id] ? i : l, -1);
if (pind !== -1) {
list.splice(pind, 1);
}
@@ -368,7 +368,7 @@ export namespace Doc {
list.splice(0, 0, doc);
}
else {
- let ind = relativeTo ? list.indexOf(relativeTo) : -1;
+ const ind = relativeTo ? list.indexOf(relativeTo) : -1;
if (ind === -1) {
if (reversed) list.splice(0, 0, doc);
else list.push(doc);
@@ -387,9 +387,9 @@ export namespace Doc {
// Computes the bounds of the contents of a set of documents.
//
export function ComputeContentBounds(docList: Doc[]) {
- let bounds = docList.reduce((bounds, doc) => {
- var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
- let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
+ const bounds = docList.reduce((bounds, doc) => {
+ const [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
+ const [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
return {
x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
@@ -399,16 +399,17 @@ export namespace Doc {
}
export function MakeTitled(title: string) {
- let doc = new Doc();
+ const doc = new Doc();
doc.title = title;
return doc;
}
export function MakeAlias(doc: Doc) {
- let alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc) : Doc.MakeDelegate(doc);
- if (alias.layout instanceof Doc) {
- alias.layout = Doc.MakeAlias(alias.layout);
+ const alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc) : Doc.MakeDelegate(doc);
+ const layout = Doc.Layout(alias);
+ if (layout instanceof Doc && layout !== alias) {
+ Doc.SetLayout(alias, Doc.MakeAlias(layout));
}
- let aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
+ const aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
alias.title = ComputedField.MakeFunction(`renameAlias(this, ${aliasNumber})`);
return alias;
}
@@ -436,8 +437,8 @@ export namespace Doc {
// ... which means we change the layout to be an expanded view of the template layout.
// This allows the view override the template's properties and be referenceable as its own document.
- let expandedLayoutFieldKey = "Layout[" + templateLayoutDoc[Id] + "]";
- let expandedTemplateLayout = dataDoc[expandedLayoutFieldKey];
+ const expandedLayoutFieldKey = "Layout[" + templateLayoutDoc[Id] + "]";
+ const expandedTemplateLayout = dataDoc[expandedLayoutFieldKey];
if (expandedTemplateLayout instanceof Doc) {
return expandedTemplateLayout;
}
@@ -450,10 +451,11 @@ export namespace Doc {
export function GetLayoutDataDocPair(doc: Doc, dataDoc: Doc | undefined, fieldKey: string, childDocLayout: Doc) {
let layoutDoc: Doc | undefined = childDocLayout;
- let resolvedDataDoc = !doc.isTemplateField && dataDoc !== doc && dataDoc ? Doc.GetDataDoc(dataDoc) : undefined;
+ const resolvedDataDoc = !doc.isTemplateField && dataDoc !== doc && dataDoc ? Doc.GetDataDoc(dataDoc) : undefined;
if (resolvedDataDoc && Doc.WillExpandTemplateLayout(childDocLayout, resolvedDataDoc)) {
- let extensionDoc = fieldExtensionDoc(resolvedDataDoc, StrCast(childDocLayout.templateField, StrCast(childDocLayout.title)));
+ const extensionDoc = fieldExtensionDoc(resolvedDataDoc, StrCast(childDocLayout.templateField, StrCast(childDocLayout.title)));
layoutDoc = Doc.expandTemplateLayout(childDocLayout, extensionDoc !== resolvedDataDoc ? extensionDoc : undefined);
+ setTimeout(() => layoutDoc && (layoutDoc.resolvedDataDoc = resolvedDataDoc), 0);
} else layoutDoc = childDocLayout;
return { layout: layoutDoc, data: resolvedDataDoc };
}
@@ -466,15 +468,21 @@ export namespace Doc {
// to store annotations, ink, and other data.
//
export function fieldExtensionDoc(doc: Doc, fieldKey: string) {
- let extension = doc[fieldKey + "_ext"] as Doc;
- (extension === undefined) && setTimeout(() => CreateDocumentExtensionForField(doc, fieldKey), 0);
- return extension ? extension : undefined;
+ const extension = doc[fieldKey + "_ext"];
+ if (doc instanceof Doc && extension === undefined) {
+ setTimeout(() => CreateDocumentExtensionForField(doc, fieldKey), 0);
+ }
+ return extension ? extension as Doc : undefined;
+ }
+ export function fieldExtensionDocSync(doc: Doc, fieldKey: string) {
+ return (doc[fieldKey + "_ext"] as Doc) || CreateDocumentExtensionForField(doc, fieldKey);
}
export function CreateDocumentExtensionForField(doc: Doc, fieldKey: string) {
- let docExtensionForField = new Doc(doc[Id] + fieldKey, true);
- docExtensionForField.title = fieldKey + ".ext";
+ const docExtensionForField = new Doc(doc[Id] + fieldKey, true);
+ docExtensionForField.title = fieldKey + ".ext"; // courtesy field--- shouldn't be needed except maybe for debugging
docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends.
+ docExtensionForField.extendsField = fieldKey; // this can be used by search to map matches on the extension doc back to the field that was extended.
docExtensionForField.type = DocumentType.EXTENSION;
let proto: Doc | undefined = doc;
while (proto && !Doc.IsPrototype(proto) && proto.proto) {
@@ -510,7 +518,7 @@ export namespace Doc {
export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string): Doc {
const copy = new Doc(copyProtoId, true);
Object.keys(doc).forEach(key => {
- let cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+ const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
const field = ProxyField.WithoutProxy(() => doc[key]);
if (key === "proto" && copyProto) {
if (doc[key] instanceof Doc) {
@@ -549,7 +557,7 @@ export namespace Doc {
let _applyCount: number = 0;
export function ApplyTemplate(templateDoc: Doc) {
if (templateDoc) {
- let applied = ApplyTemplateTo(templateDoc, Doc.MakeDelegate(new Doc()), "layoutCustom", templateDoc.title + "(..." + _applyCount++ + ")");
+ const applied = ApplyTemplateTo(templateDoc, Doc.MakeDelegate(new Doc()), "layoutCustom", templateDoc.title + "(..." + _applyCount++ + ")");
applied && (Doc.GetProto(applied).layout = applied.layout);
return applied;
}
@@ -565,20 +573,22 @@ export namespace Doc {
return;
}
- let layoutCustomLayout = Doc.MakeDelegate(templateDoc);
+ if ((target[targetKey] as Doc)?.proto !== templateDoc) {
+ const layoutCustomLayout = Doc.MakeDelegate(templateDoc);
- titleTarget && (Doc.GetProto(target).title = titleTarget);
- target.type = DocumentType.TEMPLATE;
- target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy]();
+ titleTarget && (Doc.GetProto(target).title = titleTarget);
+ Doc.GetProto(target).type = DocumentType.TEMPLATE;
+ target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy]();
- Doc.GetProto(target)[targetKey] = layoutCustomLayout;
+ Doc.GetProto(target)[targetKey] = layoutCustomLayout;
+ }
target.layoutKey = targetKey;
return target;
}
export function MakeMetadataFieldTemplate(fieldTemplate: Doc, templateDataDoc: Doc, suppressTitle: boolean = false): boolean {
// move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
- let metadataFieldName = StrCast(fieldTemplate.title).replace(/^-/, "");
+ const metadataFieldName = StrCast(fieldTemplate.title).replace(/^-/, "");
let fieldLayoutDoc = fieldTemplate;
if (fieldTemplate.layout instanceof Doc) {
fieldLayoutDoc = Doc.MakeDelegate(fieldTemplate.layout);
@@ -599,30 +609,30 @@ export namespace Doc {
fieldTemplate.panY = 0;
fieldTemplate.scale = 1;
fieldTemplate.showTitle = suppressTitle ? undefined : "title";
- let data = fieldTemplate.data;
- setTimeout(action(() => {
- !templateDataDoc[metadataFieldName] && data instanceof ObjectField && (Doc.GetProto(templateDataDoc)[metadataFieldName] = ObjectField.MakeCopy(data));
- let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metadataFieldName}"}`);
- let layoutDelegate = Doc.Layout(fieldTemplate);
- layoutDelegate.layout = layout;
- fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;
- if (fieldTemplate.backgroundColor !== templateDataDoc.defaultBackgroundColor) fieldTemplate.defaultBackgroundColor = fieldTemplate.backgroundColor;
- fieldTemplate.proto = templateDataDoc;
- }), 0);
+ const data = fieldTemplate.data;
+ // setTimeout(action(() => {
+ !templateDataDoc[metadataFieldName] && data instanceof ObjectField && (Doc.GetProto(templateDataDoc)[metadataFieldName] = ObjectField.MakeCopy(data));
+ const layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldName}'}`);
+ const layoutDelegate = Doc.Layout(fieldTemplate);
+ layoutDelegate.layout = layout;
+ fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;
+ if (fieldTemplate.backgroundColor !== templateDataDoc.defaultBackgroundColor) fieldTemplate.defaultBackgroundColor = fieldTemplate.backgroundColor;
+ fieldTemplate.proto = templateDataDoc;
+ // }), 0);
return true;
}
export function overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) {
- let doc2Layout = Doc.Layout(doc2);
- let doc1Layout = Doc.Layout(doc1);
- var x2 = NumCast(doc2.x) - clusterDistance;
- var y2 = NumCast(doc2.y) - clusterDistance;
- var w2 = NumCast(doc2Layout.width) + clusterDistance;
- var h2 = NumCast(doc2Layout.height) + clusterDistance;
- var x = NumCast(doc1.x) - clusterDistance;
- var y = NumCast(doc1.y) - clusterDistance;
- var w = NumCast(doc1Layout.width) + clusterDistance;
- var h = NumCast(doc1Layout.height) + clusterDistance;
+ const doc2Layout = Doc.Layout(doc2);
+ const doc1Layout = Doc.Layout(doc1);
+ const x2 = NumCast(doc2.x) - clusterDistance;
+ const y2 = NumCast(doc2.y) - clusterDistance;
+ const w2 = NumCast(doc2Layout.width) + clusterDistance;
+ const h2 = NumCast(doc2Layout.height) + clusterDistance;
+ const x = NumCast(doc1.x) - clusterDistance;
+ const y = NumCast(doc1.y) - clusterDistance;
+ const w = NumCast(doc1Layout.width) + clusterDistance;
+ const h = NumCast(doc1Layout.height) + clusterDistance;
return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 });
}
@@ -636,27 +646,38 @@ export namespace Doc {
}
export class DocBrush {
- @observable BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
+ BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
}
const brushManager = new DocBrush();
export class DocData {
@observable _user_doc: Doc = undefined!;
- @observable BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
+ @observable _searchQuery: string = "";
}
// the document containing the view layout information - will be the Document itself unless the Document has
// a layout field. In that case, all layout information comes from there unless overriden by Document
export function Layout(doc: Doc) { return Doc.LayoutField(doc) instanceof Doc ? doc[StrCast(doc.layoutKey, "layout")] as Doc : doc; }
+ export function SetLayout(doc: Doc, layout: Doc | string) { doc[StrCast(doc.layoutKey, "layout")] = layout; }
export function LayoutField(doc: Doc) { return doc[StrCast(doc.layoutKey, "layout")]; }
const manager = new DocData();
+ export function SearchQuery(): string { return manager._searchQuery; }
+ export function SetSearchQuery(query: string) { runInAction(() => manager._searchQuery = query); }
export function UserDoc(): Doc { return manager._user_doc; }
export function SetUserDoc(doc: Doc) { manager._user_doc = doc; }
export function IsBrushed(doc: Doc) {
- return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetDataDoc(doc));
+ return computedFn(function IsBrushed(doc: Doc) {
+ return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetDataDoc(doc));
+ })(doc);
+ }
+ // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
+ export function IsBrushedDegreeUnmemoized(doc: Doc) {
+ return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetDataDoc(doc)) ? 1 : 0;
}
export function IsBrushedDegree(doc: Doc) {
- return brushManager.BrushedDoc.has(Doc.GetDataDoc(doc)) ? 2 : brushManager.BrushedDoc.has(doc) ? 1 : 0;
+ return computedFn(function IsBrushDegree(doc: Doc) {
+ return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetDataDoc(doc)) ? 1 : 0;
+ })(doc);
}
export function BrushDoc(doc: Doc) {
brushManager.BrushedDoc.set(doc, true);
@@ -669,6 +690,8 @@ export namespace Doc {
return doc;
}
+
+ export function LinkOtherAnchor(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? Cast(linkDoc.anchor2, Doc) as Doc : Cast(linkDoc.anchor1, Doc) as Doc; }
export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "layoutKey1" : "layoutKey2"; }
export function linkFollowUnhighlight() {
@@ -682,7 +705,7 @@ export namespace Doc {
Doc.HighlightDoc(destDoc);
document.removeEventListener("pointerdown", linkFollowUnhighlight);
document.addEventListener("pointerdown", linkFollowUnhighlight);
- let x = dt = Date.now();
+ const x = dt = Date.now();
window.setTimeout(() => dt === x && linkFollowUnhighlight(), 5000);
}
@@ -691,8 +714,7 @@ export namespace Doc {
}
const highlightManager = new HighlightBrush();
export function IsHighlighted(doc: Doc) {
- let IsHighlighted = highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetDataDoc(doc));
- return IsHighlighted;
+ return highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetDataDoc(doc));
}
export function HighlightDoc(doc: Doc) {
runInAction(() => {
@@ -707,10 +729,10 @@ export namespace Doc {
});
}
export function UnhighlightAll() {
- let mapEntries = highlightManager.HighlightedDoc.keys();
+ const mapEntries = highlightManager.HighlightedDoc.keys();
let docEntry: IteratorResult<Doc>;
while (!(docEntry = mapEntries.next()).done) {
- let targetDoc = docEntry.value;
+ const targetDoc = docEntry.value;
targetDoc && Doc.UnHighlightDoc(targetDoc);
}
@@ -724,8 +746,19 @@ export namespace Doc {
source.dragFactory instanceof Doc && source.dragFactory.isTemplateDoc ? source.dragFactory :
source && source.layout instanceof Doc && source.layout.isTemplateDoc ? source.layout : undefined;
}
-}
+ export function MakeDocFilter(docFilters: string[]) {
+ let docFilterText = "";
+ for (let i = 0; i < docFilters.length; i += 3) {
+ const key = docFilters[i];
+ const value = docFilters[i + 1];
+ const modifiers = docFilters[i + 2];
+ const scriptText = `${modifiers === "x" ? "!" : ""}matchFieldValue(doc, "${key}", "${value}")`;
+ docFilterText = docFilterText ? docFilterText + " || " + scriptText : scriptText;
+ }
+ return docFilterText ? "(" + docFilterText + ")" : "";
+ }
+}
Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; });
Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); });
@@ -737,4 +770,37 @@ Scripting.addGlobal(function aliasDocs(field: any) { return new List<Doc>(field.
Scripting.addGlobal(function docList(field: any) { return DocListCast(field); });
Scripting.addGlobal(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); });
Scripting.addGlobal(function undo() { return UndoManager.Undo(); });
-Scripting.addGlobal(function redo() { return UndoManager.Redo(); }); \ No newline at end of file
+Scripting.addGlobal(function redo() { return UndoManager.Redo(); });
+Scripting.addGlobal(function selectDoc(doc: any) { Doc.UserDoc().SelectedDocs = new List([doc]); });
+Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
+ const docs = DocListCast(Doc.UserDoc().SelectedDocs).filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCUMENT && d.type !== DocumentType.KVP && (!excludeCollections || !Cast(d.data, listSpec(Doc), null)));
+ return docs.length ? new List(docs) : prevValue;
+});
+Scripting.addGlobal(function matchFieldValue(doc: Doc, key: string, value: any) {
+ const fieldVal = doc[key] ? doc[key] : doc[key + "_ext"];
+ if (StrCast(fieldVal, null) !== undefined) return StrCast(fieldVal) === value;
+ if (NumCast(fieldVal, null) !== undefined) return NumCast(fieldVal) === value;
+ if (Cast(fieldVal, listSpec("string"), []).length) {
+ const vals = Cast(fieldVal, listSpec("string"), []);
+ return vals.some(v => v === value);
+ }
+ return false;
+});
+Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers: string) {
+ const docFilters = Cast(container.docFilter, listSpec("string"), []);
+ let found = false;
+ for (let i = 0; i < docFilters.length && !found; i += 3) {
+ if (docFilters[i] === key && docFilters[i + 1] === value) {
+ found = true;
+ docFilters.splice(i, 3);
+ }
+ }
+ if (!found || modifiers !== "none") {
+ docFilters.push(key);
+ docFilters.push(value);
+ docFilters.push(modifiers);
+ container.docFilter = new List<string>(docFilters);
+ }
+ const docFilterText = Doc.MakeDocFilter(docFilters);
+ container.viewSpecScript = docFilterText ? ScriptField.MakeFunction(docFilterText, { doc: Doc.name }) : undefined;
+}); \ No newline at end of file