aboutsummaryrefslogtreecommitdiff
path: root/src/fields
diff options
context:
space:
mode:
Diffstat (limited to 'src/fields')
-rw-r--r--src/fields/Doc.ts189
-rw-r--r--src/fields/DocSymbols.ts2
-rw-r--r--src/fields/List.ts104
-rw-r--r--src/fields/ObjectField.ts27
-rw-r--r--src/fields/Proxy.ts31
-rw-r--r--src/fields/RefField.ts2
-rw-r--r--src/fields/RichTextUtils.ts60
-rw-r--r--src/fields/ScriptField.ts28
-rw-r--r--src/fields/util.ts148
9 files changed, 319 insertions, 272 deletions
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 2792f3aba..7abba7679 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1,6 +1,3 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
-/* eslint-disable @typescript-eslint/no-namespace */
-/* eslint-disable default-param-last */
/* eslint-disable no-use-before-define */
import { action, computed, makeObservable, observable, ObservableMap, ObservableSet, runInAction } from 'mobx';
import { computedFn } from 'mobx-utils';
@@ -19,7 +16,7 @@ import {
import { Copy, FieldChanged, HandleUpdate, Id, Parent, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
import { InkTool } from './InkField';
import { List } from './List';
-import { ObjectField } from './ObjectField';
+import { ObjectField, serverOpType } from './ObjectField';
import { PrefetchProxy, ProxyField } from './Proxy';
import { FieldId, RefField } from './RefField';
import { RichTextField } from './RichTextField';
@@ -28,6 +25,15 @@ import { ComputedField, ScriptField } from './ScriptField';
import { BoolCast, Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor, toList } from './Types';
import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, setter, SharingPermissions } from './util';
+export let ObjGetRefField: (id: string, force?: boolean) => Promise<Doc | undefined>;
+export let ObjGetRefFields: (ids: string[]) => Promise<Map<string, Doc | undefined>>;
+
+export function SetObjGetRefField(func: (id: string, force?: boolean) => Promise<Doc | undefined>) {
+ ObjGetRefField = func;
+}
+export function SetObjGetRefFields(func: (ids: string[]) => Promise<Map<string, Doc | undefined>>) {
+ ObjGetRefFields = func;
+}
export const LinkedTo = '-linkedTo';
export namespace Field {
/**
@@ -91,18 +97,19 @@ export namespace Field {
});
return script;
}
- export function toString(field: FieldType) {
+ export function toString(fieldIn: unknown) {
+ const field = fieldIn as FieldType;
if (typeof field === 'string' || typeof field === 'number' || typeof field === 'boolean') return String(field);
return field?.[ToString]?.() || '';
}
- export function IsField(field: any): field is FieldType;
- export function IsField(field: any, includeUndefined: true): field is FieldType | undefined;
- export function IsField(field: any, includeUndefined: boolean = false): field is FieldType | undefined {
+ export function IsField(field: unknown): field is FieldType;
+ export function IsField(field: unknown, includeUndefined: true): field is FieldType | undefined;
+ export function IsField(field: unknown, includeUndefined: boolean = false): field is FieldType | undefined {
return ['string', 'number', 'boolean'].includes(typeof field) || field instanceof ObjectField || field instanceof RefField || (includeUndefined && field === undefined);
}
// eslint-disable-next-line @typescript-eslint/no-shadow
- export function Copy(field: any) {
- return field instanceof ObjectField ? ObjectField.MakeCopy(field) : field;
+ export function Copy(field: unknown) {
+ return field instanceof ObjectField ? ObjectField.MakeCopy(field) : (field as FieldType);
}
UndoManager.SetFieldPrinter(toString);
}
@@ -118,9 +125,7 @@ export type FieldResult<T extends FieldType = FieldType> = Opt<T> | FieldWaiting
* If no default value is given, and the returned value is not undefined, it can be safely modified.
*/
export function DocListCastAsync(field: FieldResult): Promise<Doc[] | undefined>;
-// eslint-disable-next-line no-redeclare
export function DocListCastAsync(field: FieldResult, defaultValue: Doc[]): Promise<Doc[]>;
-// eslint-disable-next-line no-redeclare
export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) {
const list = Cast(field, listSpec(Doc));
return list ? Promise.all(list).then(() => list) : Promise.resolve(defaultValue);
@@ -157,7 +162,7 @@ export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol; im
// this recursively updates all protos as well.
export function updateCachedAcls(doc: Doc) {
if (doc) {
- const target = (doc as any)?.__fieldTuples ?? doc;
+ const target = doc[FieldTuples] ?? doc;
const permissions: { [key: string]: symbol } = !target.author || target.author === ClientUtils.CurrentUserEmail() ? { acl_Me: AclAdmin } : {};
Object.keys(target).forEach(key => {
key.startsWith('acl_') && (permissions[key] = ReverseHierarchyMap.get(StrCast(target[key]))!.acl);
@@ -177,12 +182,11 @@ export function updateCachedAcls(doc: Doc) {
}
@scriptingGlobal
-@Deserializable('Doc', updateCachedAcls, ['id'])
+@Deserializable('Doc', (obj: unknown) => updateCachedAcls(obj as Doc), ['id'])
export class Doc extends RefField {
@observable public static RecordingEvent = 0;
@observable public static GuestDashboard: Doc | undefined = undefined;
@observable public static GuestTarget: Doc | undefined = undefined;
- @observable public static GuestMobile: Doc | undefined = undefined;
@observable.shallow public static CurrentlyLoading: Doc[] = observable([]);
// DocServer api
public static FindDocByTitle(title: string) {
@@ -219,6 +223,8 @@ export class Doc extends RefField {
public static get MyUserDocView() { return DocCast(Doc.UserDoc().myUserDocView); } // prettier-ignore
public static get MyDockedBtns() { return DocCast(Doc.UserDoc().myDockedBtns); } // prettier-ignore
public static get MySearcher() { return DocCast(Doc.UserDoc().mySearcher); } // prettier-ignore
+ public static get MyImageGrouper() { return DocCast(Doc.UserDoc().myImageGrouper); } //prettier-ignore
+ public static get MyFaceCollection() { return DocCast(Doc.UserDoc().myFaceCollection); } //prettier-ignore
public static get MyHeaderBar() { return DocCast(Doc.UserDoc().myHeaderBar); } // prettier-ignore
public static get MyLeftSidebarMenu() { return DocCast(Doc.UserDoc().myLeftSidebarMenu); } // prettier-ignore
public static get MyLeftSidebarPanel() { return DocCast(Doc.UserDoc().myLeftSidebarPanel); } // prettier-ignore
@@ -252,16 +258,16 @@ export class Doc extends RefField {
public static set ActiveDashboard(val: Opt<Doc>) { Doc.UserDoc().activeDashboard = val; } // prettier-ignore
public static IsInMyOverlay(doc: Doc) { return Doc.MyOverlayDocs.includes(doc); } // prettier-ignore
- public static AddToMyOverlay(doc: Doc) { return Doc.ActiveDashboard?.myOverlayDocs ? Doc.AddDocToList(Doc.ActiveDashboard, 'myOverlayDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
- public static RemFromMyOverlay(doc: Doc) { return Doc.ActiveDashboard?.myOverlayDocs ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myOverlayDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
+ public static AddToMyOverlay(doc: Doc) { return Doc.ActiveDashboard ? Doc.AddDocToList(Doc.ActiveDashboard, 'myOverlayDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
+ public static RemFromMyOverlay(doc: Doc) { return Doc.ActiveDashboard ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myOverlayDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
public static AddToMyPublished(doc: Doc) {
doc[DocData].title_custom = true;
doc[DocData].layout_showTitle = 'title';
- Doc.ActiveDashboard?.myPublishedDocs ? Doc.AddDocToList(Doc.ActiveDashboard, 'myPublishedDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
+ Doc.ActiveDashboard ? Doc.AddDocToList(Doc.ActiveDashboard, 'myPublishedDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
public static RemFromMyPublished(doc: Doc){
doc[DocData].title_custom = false;
doc[DocData].layout_showTitle = undefined;
- Doc.ActiveDashboard?.myPublishedDocs ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myPublishedDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
+ Doc.ActiveDashboard ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myPublishedDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
public static IsComicStyle(doc?: Doc) { return doc && Doc.ActiveDashboard && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === 'comic' ; } // prettier-ignore
constructor(id?: FieldId, forceSave?: boolean) {
@@ -321,19 +327,22 @@ export class Doc extends RefField {
});
this[SelfProxy] = docProxy;
if (!id || forceSave) {
- DocServer.CreateField(docProxy);
+ DocServer.CreateDocField(docProxy);
}
// eslint-disable-next-line no-constructor-return
return docProxy; // need to return the proxy from the constructor so that all our added fields will get called
}
[key: string]: FieldResult;
+ [key2: symbol]: unknown;
@serializable(alias('fields', map(autoObject(), { afterDeserialize: afterDocDeserialize })))
- private get __fieldTuples() {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ get __fieldTuples(): any {
+ // __fieldTuples does not follow the index signature pattern which requires a FieldResult return value -- so this hack suppresses the error
return this[FieldTuples];
}
- private set __fieldTuples(value) {
+ set __fieldTuples(value) {
// called by deserializer to set all fields in one shot
this[FieldTuples] = value;
Object.keys(value).forEach(key => {
@@ -348,33 +357,33 @@ export class Doc extends RefField {
});
}
- @observable private [FieldTuples]: any = {};
- @observable private [FieldKeys]: any = {};
+ @observable private [FieldTuples]: { [key: string]: FieldResult } = {};
+ @observable private [FieldKeys]: { [key: string]: boolean } = {};
/// all of the raw acl's that have been set on this document. Use GetEffectiveAcl to determine the actual ACL of the doc for editing
@observable public [DocAcl]: { [key: string]: symbol } = {};
@observable public [DocCss]: number = 0; // incrementer denoting a change to CSS layout
@observable public [DirectLinks] = new ObservableSet<Doc>();
- @observable public [AudioPlay]: any = undefined; // meant to store sound object from Howl
+ @observable public [AudioPlay]: unknown = undefined; // meant to store sound object from Howl
@observable public [Animation]: Opt<Doc> = undefined;
@observable public [Highlight]: boolean = false;
@observable public [Brushed]: boolean = false;
- @observable public [DocViews] = new ObservableSet<any /* DocumentView */>();
+ @observable public [DocViews] = new ObservableSet<unknown /* DocumentView */>();
private [Self] = this;
- private [SelfProxy]: any;
+ private [SelfProxy]: Doc;
private [UpdatingFromServer]: boolean = false;
private [ForceServerWrite]: boolean = false;
- private [CachedUpdates]: { [key: string]: () => void | Promise<any> } = {};
+ private [CachedUpdates]: { [key: string]: () => void | Promise<void> } = {};
public [Initializing]: boolean = false;
- public [FieldChanged] = (diff: undefined | { op: '$addToSet' | '$remFromSet' | '$set'; items: FieldType[] | undefined; length: number | undefined; hint?: any }, serverOp: any) => {
+ public [FieldChanged] = (diff: { op: '$addToSet' | '$remFromSet' | '$set'; items: FieldType[] | undefined; length: number | undefined; hint?: { start: number; deleteCount: number } } | undefined, serverOp: serverOpType) => {
if (!this[UpdatingFromServer] || this[ForceServerWrite]) {
DocServer.UpdateField(this[Id], serverOp);
}
};
public [Width] = () => NumCast(this[SelfProxy]._width);
public [Height] = () => NumCast(this[SelfProxy]._height);
- public [TransitionTimer]: any = undefined;
+ public [TransitionTimer]: NodeJS.Timeout | undefined = undefined;
public [ToJavascriptString] = () => `idToDoc("${this[Self][Id]}")`; // what should go here?
public [ToScriptString] = () => `idToDoc("${this[Self][Id]}")`;
public [ToString] = () => `Doc(${GetEffectiveAcl(this[SelfProxy]) === AclPrivate ? '-inaccessible-' : this[SelfProxy].title})`;
@@ -387,7 +396,7 @@ export class Doc extends RefField {
const self = this[SelfProxy];
const templateLayoutDoc = Cast(Doc.LayoutField(self), Doc, null);
if (templateLayoutDoc) {
- let renderFieldKey: any;
+ let renderFieldKey: string = '';
const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layout_fieldKey, 'layout')];
if (typeof layoutField === 'string') {
[renderFieldKey] = layoutField.split("fieldKey={'")[1].split("'"); // layoutField.split("'")[1];
@@ -399,16 +408,17 @@ export class Doc extends RefField {
return undefined;
}
- public async [HandleUpdate](diff: any) {
- const set = diff.$set;
+ public async [HandleUpdate](diff: { $set: { [key: string]: FieldType } } | { $unset?: unknown }) {
+ const $set = '$set' in diff ? diff.$set : undefined;
+ const $unset = '$unset' in diff ? diff.$unset : undefined;
const sameAuthor = this.author === ClientUtils.CurrentUserEmail();
const fprefix = 'fields.';
- Object.keys(set ?? {})
+ Object.keys($set ?? {})
.filter(key => key.startsWith(fprefix))
.forEach(async key => {
const fKey = key.substring(fprefix.length);
const fn = async () => {
- const value = await SerializationHelper.Deserialize(set[key]);
+ const value = (await SerializationHelper.Deserialize($set?.[key])) as FieldType;
const prev = GetEffectiveAcl(this);
this[UpdatingFromServer] = true;
this[fKey] = value;
@@ -423,14 +433,12 @@ export class Doc extends RefField {
const writeMode = DocServer.getFieldWriteMode(fKey);
if (fKey.startsWith('acl_') || writeMode !== DocServer.WriteMode.Playground) {
delete this[CachedUpdates][fKey];
- // eslint-disable-next-line no-await-in-loop
await fn();
} else {
this[CachedUpdates][fKey] = fn;
}
});
- const unset = diff.$unset;
- Object.keys(unset ?? {})
+ Object.keys($unset ?? {})
.filter(key => key.startsWith(fprefix))
.forEach(async key => {
const fKey = key.substring(7);
@@ -451,12 +459,10 @@ export class Doc extends RefField {
// eslint-disable-next-line no-redeclare
export namespace Doc {
- // eslint-disable-next-line import/no-mutable-exports
export let SelectOnLoad: Doc | undefined;
export function SetSelectOnLoad(doc: Doc | undefined) {
SelectOnLoad = doc;
}
- // eslint-disable-next-line import/no-mutable-exports
export let DocDragDataName: string = '';
export function SetDocDragDataName(name: string) {
DocDragDataName = name;
@@ -474,7 +480,7 @@ export namespace Doc {
delete doc[CachedUpdates][field];
}
}
- export function AddCachedUpdate(doc: Doc, field: string, oldValue: any) {
+ export function AddCachedUpdate(doc: Doc, field: string, oldValue: FieldType) {
const val = oldValue;
doc[CachedUpdates][field] = () => {
doc[UpdatingFromServer] = true;
@@ -493,7 +499,7 @@ export namespace Doc {
export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldResult {
try {
- return getField(doc[Self], key, ignoreProto);
+ return getField(doc[Self], key, ignoreProto) as FieldResult;
} catch {
return doc;
}
@@ -558,9 +564,12 @@ export namespace Doc {
export function assign<K extends string>(doc: Doc, fields: Partial<Record<K, Opt<FieldType>>>, skipUndefineds: boolean = false, isInitializing = false) {
isInitializing && (doc[Initializing] = true);
Object.keys(fields).forEach(key => {
- const value = (fields as any)[key];
+ const value = (fields as { [key: string]: Opt<FieldType> })[key];
if (!skipUndefineds || value !== undefined) {
// Do we want to filter out undefineds?
+ if (typeof value === 'object' && 'values' in value) {
+ console.log(value);
+ }
doc[key] = value;
}
});
@@ -712,7 +721,7 @@ export namespace Doc {
await Promise.all(
Object.keys(doc).map(async key => {
if (filter.includes(key)) return;
- const assignKey = (val: any) => {
+ const assignKey = (val: Opt<FieldType>) => {
copy[key] = val;
};
const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
@@ -735,7 +744,7 @@ export namespace Doc {
.trim()
);
const results = docids && (await DocServer.GetRefFields(docids));
- const rdocs = results && Array.from(Object.keys(results)).map(rkey => DocCast(results[rkey]));
+ const rdocs = results && Array.from(Object.keys(results)).map(rkey => DocCast(results.get(rkey)));
rdocs?.map(d => d && Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
rtfs.push({ copy, key, field: objField });
}
@@ -782,9 +791,9 @@ export namespace Doc {
linkMap.set(link[Id], await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
}
});
- if (Doc.Get(copy, 'title', true)) copy.title = '>:' + doc.title;
- // Doc.SetInPlace(copy, 'title', '>:' + doc.title, true);
copy.cloneOf = doc;
+ const cfield = ComputedField.WithoutComputed(() => FieldValue(doc.title));
+ if (Doc.Get(copy, 'title', true) && !(cfield instanceof ComputedField)) copy.title = '>:' + doc.title;
cloneMap.set(doc[Id], copy);
return copy;
@@ -818,11 +827,11 @@ export namespace Doc {
const linkedDocs = Array.from(linkMap.values());
linkedDocs.forEach(link => Doc.AddLink?.(link, true));
rtfMap.forEach(({ copy, key, field }) => {
- const replacer = (match: any, attr: string, id: string /* , offset: any, string: any */) => {
+ const replacer = (match: string, attr: string, id: string /* , offset: any, string: any */) => {
const mapped = cloneMap.get(id);
return attr + '"' + (mapped ? mapped[Id] : id) + '"';
};
- const replacer2 = (match: any, href: string, id: string /* , offset: any, string: any */) => {
+ const replacer2 = (match: string, href: string, id: string /* , offset: any, string: any */) => {
const mapped = cloneMap.get(id);
return href + (mapped ? mapped[Id] : id);
};
@@ -874,7 +883,7 @@ export namespace Doc {
newLayoutDoc.embedContainer = targetDoc;
newLayoutDoc.resolvedDataDoc = dataDoc;
newLayoutDoc.acl_Guest = SharingPermissions.Edit;
- if (dataDoc[templateField] === undefined && (templateLayoutDoc[templateField] as any)?.length) {
+ if (dataDoc[templateField] === undefined && (templateLayoutDoc[templateField] as List<Doc>)?.length) {
dataDoc[templateField] = ObjectField.MakeCopy(templateLayoutDoc[templateField] as List<Doc>);
// ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"])`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc });
}
@@ -901,7 +910,7 @@ export namespace Doc {
return { layout: Doc.expandTemplateLayout(childDoc, templateRoot), data: resolvedDataDoc };
}
- export function FindReferences(infield: Doc | List<any>, references: Set<Doc>, system: boolean | undefined) {
+ export function FindReferences(infield: Doc | List<Doc>, references: Set<Doc>, system: boolean | undefined) {
if (infield instanceof Promise) return;
if (!(infield instanceof Doc)) {
infield?.forEach(val => (val instanceof Doc || val instanceof List) && FindReferences(val, references, system));
@@ -976,9 +985,10 @@ export namespace Doc {
} else if (cfield instanceof ComputedField) {
copy[key] = cfield[Copy](); // ComputedField.MakeFunction(cfield.script.originalScript);
} else if (field instanceof ObjectField) {
+ const docAtKey = doc[key];
copy[key] =
- doc[key] instanceof Doc && key.includes('layout[')
- ? new ProxyField(Doc.MakeCopy(doc[key] as any)) // copy the expanded render template
+ docAtKey instanceof Doc && key.includes('layout[')
+ ? new ProxyField(Doc.MakeCopy(docAtKey)) // copy the expanded render template
: ObjectField.MakeCopy(field);
} else if (field instanceof Promise) {
// eslint-disable-next-line no-debugger
@@ -1235,7 +1245,7 @@ export namespace Doc {
}
const UnhighlightWatchers: (() => void)[] = [];
- let UnhighlightTimer: any;
+ let UnhighlightTimer: NodeJS.Timeout | undefined;
export function IsUnhighlightTimerSet() { return UnhighlightTimer; } // prettier-ignore
export function AddUnHighlightWatcher(watcher: () => void) {
if (UnhighlightTimer) {
@@ -1244,7 +1254,7 @@ export namespace Doc {
}
export function linkFollowUnhighlight() {
clearTimeout(UnhighlightTimer);
- UnhighlightTimer = 0;
+ UnhighlightTimer = undefined;
UnhighlightWatchers.forEach(watcher => watcher());
UnhighlightWatchers.length = 0;
highlightedDocs.forEach(doc => Doc.UnHighlightDoc(doc));
@@ -1258,10 +1268,7 @@ export namespace Doc {
if (UnhighlightTimer) clearTimeout(UnhighlightTimer);
const presTransition = Number(presentationEffect?.presentation_transition);
const duration = isNaN(presTransition) ? 5000 : presTransition;
- UnhighlightTimer = window.setTimeout(() => {
- linkFollowUnhighlight();
- UnhighlightTimer = 0;
- }, duration);
+ UnhighlightTimer = setTimeout(linkFollowUnhighlight, duration);
}
export const highlightedDocs = new ObservableSet<Doc>();
@@ -1319,7 +1326,7 @@ export namespace Doc {
StrCast(doc.layout_fieldKey).split('_')[1] === 'icon' && setNativeView(doc);
}
- export function setNativeView(doc: any) {
+ export function setNativeView(doc: Doc) {
const prevLayout = StrCast(doc.layout_fieldKey).split('_')[1];
const deiconify = prevLayout === 'icon' && StrCast(doc.deiconifyLayout) ? 'layout_' + StrCast(doc.deiconifyLayout) : '';
prevLayout === 'icon' && (doc.deiconifyLayout = undefined);
@@ -1356,15 +1363,15 @@ export namespace Doc {
// filters document in a container collection:
// all documents with the specified value for the specified key are included/excluded
// based on the modifiers :"check", "x", undefined
- export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) {
+ export function setDocFilter(container: Opt<Doc>, key: string, value: FieldType | undefined, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) {
if (!container) return;
const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'childFilters';
const childFilters = StrListCast(container[filterField]);
runInAction(() => {
for (let i = 0; i < childFilters.length; i++) {
const fields = childFilters[i].split(FilterSep); // split key:value:modifier
- if (fields[0] === key && (fields[1] === value.toString() || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) {
- if (fields[2] === modifiers && modifiers && fields[1] === value.toString()) {
+ if (fields[0] === key && (fields[1] === value?.toString() || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) {
+ if (fields[2] === modifiers && modifiers && fields[1] === value?.toString()) {
// eslint-disable-next-line no-param-reassign
if (toggle) modifiers = 'remove';
else return;
@@ -1393,7 +1400,7 @@ export namespace Doc {
return undefined;
}
export function assignDocToField(doc: Doc, field: string, id: string) {
- DocServer.GetRefField(id).then(layout => {
+ DocServer.GetRefField(id)?.then(layout => {
layout instanceof Doc && (doc[field] = layout);
});
return id;
@@ -1417,7 +1424,7 @@ export namespace Doc {
export function Paste(docids: string[], clone: boolean, addDocument: (doc: Doc | Doc[]) => boolean, ptx?: number, pty?: number, newPoint?: number[]) {
DocServer.GetRefFields(docids).then(async fieldlist => {
- const list = Array.from(Object.values(fieldlist))
+ const list = Array.from(fieldlist.values())
.map(d => DocCast(d))
.filter(d => d);
const docs = clone ? (await Promise.all(Doc.MakeClones(list, false, false))).map(res => res.clone) : list;
@@ -1460,7 +1467,6 @@ export namespace Doc {
case DocumentType.BUTTON: return 'bolt';
case DocumentType.PRES: return 'route';
case DocumentType.SCRIPTING: return 'terminal';
- case DocumentType.IMPORT: return 'cloud-upload-alt';
case DocumentType.VID: return 'video';
case DocumentType.INK: return 'pen-nib';
case DocumentType.PDF: return 'file-pdf';
@@ -1494,7 +1500,7 @@ export namespace Doc {
const doc = DocCast(await DocServer.GetRefField(json.id));
const links = await DocServer.GetRefFields(json.linkids as string[]);
Array.from(Object.keys(links))
- .map(key => links[key])
+ .map(key => links.get(key))
.forEach(link => link instanceof Doc && Doc.AddLink?.(link));
return doc;
}
@@ -1506,7 +1512,7 @@ export namespace Doc {
const primitives = ['string', 'number', 'boolean'];
export interface JsonConversionOpts {
- data: any;
+ data: unknown;
title?: string;
appendToExisting?: { targetDoc: Doc; fieldKey?: string };
excludeEmptyObjects?: boolean;
@@ -1561,15 +1567,16 @@ export namespace Doc {
if (data === undefined || data === null || ![...primitives, 'object'].includes(typeof data)) {
return undefined;
}
- let resolved: any;
+ let resolved: unknown;
try {
resolved = JSON.parse(typeof data === 'string' ? data : JSON.stringify(data));
} catch (e) {
+ console.error(e);
return undefined;
}
let output: Opt<Doc>;
if (typeof resolved === 'object' && !(resolved instanceof Array)) {
- output = convertObject(resolved, excludeEmptyObjects, title, appendToExisting?.targetDoc);
+ output = convertObject(resolved as { [key: string]: FieldType }, excludeEmptyObjects, title, appendToExisting?.targetDoc);
} else {
// give the proper types to the data extracted from the JSON
const result = toField(resolved, excludeEmptyObjects);
@@ -1590,7 +1597,7 @@ export namespace Doc {
* @returns the object mapped from JSON to field values, where each mapping
* might involve arbitrary recursion (since toField might itself call convertObject)
*/
- const convertObject = (object: any, excludeEmptyObjects: boolean, title?: string, target?: Doc): Opt<Doc> => {
+ const convertObject = (object: { [key: string]: FieldType }, excludeEmptyObjects: boolean, title?: string, target?: Doc): Opt<Doc> => {
const hasEntries = Object.keys(object).length;
if (hasEntries || !excludeEmptyObjects) {
const resolved = target ?? new Doc();
@@ -1618,7 +1625,7 @@ export namespace Doc {
* @returns the list mapped from JSON to field values, where each mapping
* might involve arbitrary recursion (since toField might itself call convertList)
*/
- const convertList = (list: Array<any>, excludeEmptyObjects: boolean): Opt<List<FieldType>> => {
+ const convertList = (list: Array<unknown>, excludeEmptyObjects: boolean): Opt<List<FieldType>> => {
const target = new List();
let result: Opt<FieldType>;
// if excludeEmptyObjects is true, any qualifying conversions from toField will
@@ -1633,29 +1640,33 @@ export namespace Doc {
return undefined;
};
- const toField = (data: any, excludeEmptyObjects: boolean, title?: string): Opt<FieldType> => {
+ const toField = (data: unknown, excludeEmptyObjects: boolean, title?: string): Opt<FieldType> => {
if (data === null || data === undefined) {
return undefined;
}
if (primitives.includes(typeof data)) {
- return data;
+ return data as FieldType;
}
if (typeof data === 'object') {
- return data instanceof Array ? convertList(data, excludeEmptyObjects) : convertObject(data, excludeEmptyObjects, title, undefined);
+ return data instanceof Array ? convertList(data, excludeEmptyObjects) : convertObject(data as { [key: string]: FieldType }, excludeEmptyObjects, title, undefined);
}
throw new Error(`How did ${data} of type ${typeof data} end up in JSON?`);
};
}
}
+export function returnEmptyDoclist() {
+ return [] as Doc[];
+}
+
export function RTFIsFragment(html: string) {
return html.indexOf('data-pm-slice') !== -1;
}
export function GetHrefFromHTML(html: string): string {
const parser = new DOMParser();
const parsedHtml = parser.parseFromString(html, 'text/html');
- if (parsedHtml.body.childNodes.length === 1 && parsedHtml.body.childNodes[0].childNodes.length === 1 && (parsedHtml.body.childNodes[0].childNodes[0] as any).href) {
- return (parsedHtml.body.childNodes[0].childNodes[0] as any).href;
+ if (parsedHtml.body.childNodes.length === 1 && parsedHtml.body.childNodes[0].childNodes.length === 1 && (parsedHtml.body.childNodes[0].childNodes[0] as HTMLAnchorElement).href) {
+ return (parsedHtml.body.childNodes[0].childNodes[0] as HTMLAnchorElement).href;
}
return '';
}
@@ -1675,35 +1686,35 @@ export function IdToDoc(id: string) {
return DocCast(DocServer.GetCachedRefField(id));
}
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function idToDoc(id: string): any {
+ScriptingGlobals.add(function idToDoc(id: string): Doc {
return IdToDoc(id);
});
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function renameEmbedding(doc: any) {
+ScriptingGlobals.add(function renameEmbedding(doc: Doc) {
return StrCast(doc[DocData].title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`;
});
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function getProto(doc: any) {
+ScriptingGlobals.add(function getProto(doc: Doc) {
return Doc.GetProto(doc);
});
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function getDocTemplate(doc?: any) {
+ScriptingGlobals.add(function getDocTemplate(doc?: Doc) {
return Doc.getDocTemplate(doc);
});
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function getEmbedding(doc: any) {
+ScriptingGlobals.add(function getEmbedding(doc: Doc) {
return Doc.MakeEmbedding(doc);
});
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) {
+ScriptingGlobals.add(function getCopy(doc: Doc, copyProto: boolean) {
return doc.isTemplateDoc ? Doc.MakeDelegateWithProto(doc) : Doc.MakeCopy(doc, copyProto);
});
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function copyField(field: any) {
+ScriptingGlobals.add(function copyField(field: FieldResult) {
return Field.Copy(field);
});
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function docList(field: any) {
+ScriptingGlobals.add(function docList(field: FieldResult) {
return DocListCast(field);
});
// eslint-disable-next-line prefer-arrow-callback
@@ -1711,11 +1722,11 @@ ScriptingGlobals.add(function addDocToList(doc: Doc, field: string, added: Doc)
return Doc.AddDocToList(doc, field, added);
});
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) {
+ScriptingGlobals.add(function setInPlace(doc: Doc, field: string, value: string) {
return Doc.SetInPlace(doc, field, value, false);
});
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) {
+ScriptingGlobals.add(function sameDocs(doc1: Doc, doc2: Doc) {
return Doc.AreProtosEqual(doc1, doc2);
});
// eslint-disable-next-line prefer-arrow-callback
@@ -1723,7 +1734,7 @@ ScriptingGlobals.add(function assignDoc(doc: Doc, field: string, id: string) {
return Doc.assignDocToField(doc, field, id);
});
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function docCastAsync(doc: FieldResult): any {
+ScriptingGlobals.add(function docCastAsync(doc: FieldResult): FieldResult<Doc> {
return Cast(doc, Doc);
});
// eslint-disable-next-line prefer-arrow-callback
@@ -1732,7 +1743,7 @@ ScriptingGlobals.add(function activePresentationItem() {
return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
});
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function setDocFilter(container: Doc, key: string, value: any, modifiers: 'match' | 'check' | 'x' | 'remove') {
+ScriptingGlobals.add(function setDocFilter(container: Doc, key: string, value: string, modifiers: 'match' | 'check' | 'x' | 'remove') {
Doc.setDocFilter(container, key, value, modifiers);
});
// eslint-disable-next-line prefer-arrow-callback
diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts
index 837fcc90e..dc18d8638 100644
--- a/src/fields/DocSymbols.ts
+++ b/src/fields/DocSymbols.ts
@@ -1,3 +1,5 @@
+// NOTE: These symbols must be added to Doc.ts constructor !!
+
// Symbols for fundamental Doc operations such as: permissions, field and proxy access and server interactions
export const AclPrivate = Symbol('DocAclOwnerOnly');
export const AclReadonly = Symbol('DocAclReadOnly');
diff --git a/src/fields/List.ts b/src/fields/List.ts
index 38c47d546..22bbcb9ab 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -2,33 +2,33 @@ import { action, computed, makeObservable, observable } from 'mobx';
import { alias, list as serializrList, serializable } from 'serializr';
import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
import { Deserializable, afterDocDeserialize, autoObject } from '../client/util/SerializationHelper';
-import { Field, FieldType, StrListCast } from './Doc';
+import { Doc, Field, FieldType, ObjGetRefFields, StrListCast } from './Doc';
import { FieldTuples, Self, SelfProxy } from './DocSymbols';
import { Copy, FieldChanged, Parent, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
-import { ObjGetRefFields, ObjectField } from './ObjectField';
+import { ObjectField } from './ObjectField';
import { ProxyField } from './Proxy';
import { RefField } from './RefField';
import { containedFieldChangedHandler, deleteProperty, getter, setter } from './util';
function toObjectField(field: FieldType) {
- return field instanceof RefField ? new ProxyField(field) : field;
+ return field instanceof Doc ? new ProxyField(field) : field;
}
-function toRealField(field: FieldType) {
+function toRealField(field: FieldType | undefined) {
return field instanceof ProxyField ? field.value : field;
}
-type StoredType<T extends FieldType> = T extends RefField ? ProxyField<T> : T;
+type StoredType<T extends FieldType> = T extends Doc ? ProxyField<T> : T;
export const ListFieldName = 'fields';
@Deserializable('list')
-class ListImpl<T extends FieldType> extends ObjectField {
- static listHandlers: any = {
+export class ListImpl<T extends FieldType> extends ObjectField {
+ static listHandlers = {
/// Mutator methods
- copyWithin() {
+ copyWithin: function (this: ListImpl<FieldType>) {
throw new Error('copyWithin not supported yet');
},
- fill(value: any, start?: number, end?: number) {
+ fill: function (this: ListImpl<FieldType>, value: FieldType, start?: number, end?: number) {
if (value instanceof RefField) {
throw new Error('fill with RefFields not supported yet');
}
@@ -36,12 +36,12 @@ class ListImpl<T extends FieldType> extends ObjectField {
this[SelfProxy][FieldChanged]?.();
return res;
},
- pop(): any {
+ pop: function (this: ListImpl<FieldType>): FieldType {
const field = toRealField(this[Self].__fieldTuples.pop());
this[SelfProxy][FieldChanged]?.();
return field;
},
- push: action(function (this: ListImpl<any>, ...itemsIn: any[]) {
+ push: action(function (this: ListImpl<FieldType>, ...itemsIn: FieldType[]) {
const items = itemsIn.map(toObjectField);
const list = this[Self];
@@ -58,27 +58,27 @@ class ListImpl<T extends FieldType> extends ObjectField {
this[SelfProxy][FieldChanged]?.({ op: '$addToSet', items, length: length + items.length });
return res;
}),
- reverse() {
+ reverse: function (this: ListImpl<FieldType>) {
const res = this[Self].__fieldTuples.reverse();
this[SelfProxy][FieldChanged]?.();
return res;
},
- shift() {
+ shift: function (this: ListImpl<FieldType>) {
const res = toRealField(this[Self].__fieldTuples.shift());
this[SelfProxy][FieldChanged]?.();
return res;
},
- sort(cmpFunc: any) {
+ sort: function (this: ListImpl<FieldType>, cmpFunc: (first: FieldType | undefined, second: FieldType | undefined) => number) {
this[Self].__realFields; // coerce retrieving entire array
- const res = this[Self].__fieldTuples.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined);
+ const res = this[Self].__fieldTuples.sort(cmpFunc ? (first: FieldType, second: FieldType) => cmpFunc(toRealField(first), toRealField(second)) : undefined);
this[SelfProxy][FieldChanged]?.();
return res;
},
- splice: action(function (this: any, start: number, deleteCount: number, ...itemsIn: any[]) {
+ splice: action(function (this: ListImpl<FieldType>, start: number, deleteCount: number, ...itemsIn: FieldType[]) {
this[Self].__realFields; // coerce retrieving entire array
const items = itemsIn.map(toObjectField);
const list = this[Self];
- const removed = list.__fieldTuples.filter((item: any, i: number) => i >= start && i < start + deleteCount);
+ const removed = list.__fieldTuples.filter((item: FieldType, i: number) => i >= start && i < start + deleteCount);
for (let i = 0; i < items.length; i++) {
const item = items[i];
// TODO Error checking to make sure parent doesn't already exist
@@ -88,7 +88,7 @@ class ListImpl<T extends FieldType> extends ObjectField {
item[FieldChanged] = containedFieldChangedHandler(this, i + start, item);
}
}
- const hintArray: { val: any; index: number }[] = [];
+ const hintArray: { val: FieldType; index: number }[] = [];
for (let i = start; i < start + deleteCount; i++) {
hintArray.push({ val: list.__fieldTuples[i], index: i });
}
@@ -104,7 +104,7 @@ class ListImpl<T extends FieldType> extends ObjectField {
);
return res.map(toRealField);
}),
- unshift(...itemsIn: any[]) {
+ unshift: function (this: ListImpl<FieldType>, ...itemsIn: FieldType[]) {
const items = itemsIn.map(toObjectField);
const list = this[Self];
for (let i = 0; i < items.length; i++) {
@@ -121,108 +121,108 @@ class ListImpl<T extends FieldType> extends ObjectField {
return res;
},
/// Accessor methods
- concat: action(function (this: any, ...items: any[]) {
+ concat: action(function (this: ListImpl<FieldType>, ...items: FieldType[]) {
this[Self].__realFields;
return this[Self].__fieldTuples.map(toRealField).concat(...items);
}),
- includes(valueToFind: any, fromIndex: number) {
+ includes: function (this: ListImpl<FieldType>, valueToFind: FieldType, fromIndex: number) {
if (valueToFind instanceof RefField) {
return this[Self].__realFields.includes(valueToFind, fromIndex);
}
return this[Self].__fieldTuples.includes(valueToFind, fromIndex);
},
- indexOf(valueToFind: any, fromIndex: number) {
+ indexOf: function (this: ListImpl<FieldType>, valueToFind: FieldType, fromIndex: number) {
if (valueToFind instanceof RefField) {
return this[Self].__realFields.indexOf(valueToFind, fromIndex);
}
return this[Self].__fieldTuples.indexOf(valueToFind, fromIndex);
},
- join(separator: any) {
+ join: function (this: ListImpl<FieldType>, separator: string) {
this[Self].__realFields;
return this[Self].__fieldTuples.map(toRealField).join(separator);
},
- lastElement() {
+ lastElement: function (this: ListImpl<FieldType>) {
return this[Self].__realFields.lastElement();
},
- lastIndexOf(valueToFind: any, fromIndex: number) {
+ lastIndexOf: function (this: ListImpl<FieldType>, valueToFind: FieldType, fromIndex: number) {
if (valueToFind instanceof RefField) {
return this[Self].__realFields.lastIndexOf(valueToFind, fromIndex);
}
return this[Self].__fieldTuples.lastIndexOf(valueToFind, fromIndex);
},
- slice(begin: number, end: number) {
+ slice: function (this: ListImpl<FieldType>, begin: number, end: number) {
this[Self].__realFields;
return this[Self].__fieldTuples.slice(begin, end).map(toRealField);
},
/// Iteration methods
- entries() {
+ entries: function (this: ListImpl<FieldType>) {
return this[Self].__realFields.entries();
},
- every(callback: any, thisArg: any) {
+ every: function (this: ListImpl<FieldType>, callback: (value: FieldType, index: number, array: FieldType[]) => unknown, thisArg: unknown) {
return this[Self].__realFields.every(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
- filter(callback: any, thisArg: any) {
+ filter: function (this: ListImpl<FieldType>, callback: (value: FieldType, index: number, array: FieldType[]) => FieldType[], thisArg: unknown) {
return this[Self].__realFields.filter(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
- find(callback: any, thisArg: any) {
+ find: function (this: ListImpl<FieldType>, callback: (value: FieldType, index: number, obj: FieldType[]) => FieldType, thisArg: unknown) {
return this[Self].__realFields.find(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
- findIndex(callback: any, thisArg: any) {
+ findIndex: function (this: ListImpl<FieldType>, callback: (value: FieldType, index: number, obj: FieldType[]) => number, thisArg: unknown) {
return this[Self].__realFields.findIndex(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
- forEach(callback: any, thisArg: any) {
+ forEach: function (this: ListImpl<FieldType>, callback: (value: FieldType, index: number, array: FieldType[]) => void, thisArg: unknown) {
return this[Self].__realFields.forEach(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
- map(callback: any, thisArg: any) {
+ map: function (this: ListImpl<FieldType>, callback: (value: FieldType, index: number, array: FieldType[]) => unknown, thisArg: unknown) {
return this[Self].__realFields.map(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
- reduce(callback: any, initialValue: any) {
+ reduce: function (this: ListImpl<FieldType>, callback: (previousValue: unknown, currentValue: FieldType, currentIndex: number, array: FieldType[]) => unknown, initialValue: unknown) {
return this[Self].__realFields.reduce(callback, initialValue);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
},
- reduceRight(callback: any, initialValue: any) {
+ reduceRight: function (this: ListImpl<FieldType>, callback: (previousValue: unknown, currentValue: FieldType, currentIndex: number, array: FieldType[]) => unknown, initialValue: unknown) {
return this[Self].__realFields.reduceRight(callback, initialValue);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
},
- some(callback: any, thisArg: any) {
+ some: function (this: ListImpl<FieldType>, callback: (value: FieldType, index: number, array: FieldType[]) => boolean, thisArg: unknown) {
return this[Self].__realFields.some(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
- values() {
+ values: function (this: ListImpl<FieldType>) {
return this[Self].__realFields.values();
},
- [Symbol.iterator]() {
+ [Symbol.iterator]: function (this: ListImpl<FieldType>) {
return this[Self].__realFields.values();
},
};
- static listGetter(target: any, prop: string | symbol, receiver: any): any {
+ static listGetter(target: ListImpl<FieldType>, prop: string | symbol, receiver: ListImpl<FieldType>): unknown {
if (Object.prototype.hasOwnProperty.call(ListImpl.listHandlers, prop)) {
- return ListImpl.listHandlers[prop];
+ return (ListImpl.listHandlers as { [key: string | symbol]: unknown })[prop];
}
return getter(target, prop, receiver);
}
@@ -251,7 +251,7 @@ class ListImpl<T extends FieldType> extends ObjectField {
},
});
// eslint-disable-next-line no-use-before-define
- this[SelfProxy] = list as any as List<FieldType>; // bcz: ugh .. don't know how to convince typesecript that list is a List
+ this[SelfProxy] = list as unknown as List<FieldType>; // bcz: ugh .. don't know how to convince typesecript that list is a List
if (fields) {
this[SelfProxy].push(...fields);
}
@@ -260,18 +260,20 @@ class ListImpl<T extends FieldType> extends ObjectField {
}
[key: number]: T | (T extends RefField ? Promise<T> : never);
+ [key2: symbol]: unknown;
+ [key3: string]: unknown;
// this requests all ProxyFields at the same time to avoid the overhead
// of separate network requests and separate updates to the React dom.
@computed private get __realFields() {
- const unrequested = this[FieldTuples].filter(f => f instanceof ProxyField && f.needsRequesting).map(f => f as ProxyField<RefField>);
+ const unrequested = this[FieldTuples].filter(f => f instanceof ProxyField && f.needsRequesting).map(f => f as ProxyField<Doc>);
// if we find any ProxyFields that don't have a current value, then
// start the server request for all of them
if (unrequested.length) {
const batchPromise = ObjGetRefFields(unrequested.map(p => p.fieldId));
// as soon as we get the fields from the server, set all the list values in one
// action to generate one React dom update.
- const allSetPromise = batchPromise.then(action(pfields => unrequested.map(toReq => toReq.setValue(pfields[toReq.fieldId]))));
+ const allSetPromise = batchPromise.then(action(pfields => unrequested.map(toReq => toReq.setValue(pfields.get(toReq.fieldId)))));
// we also have to mark all lists items with this promise so that any calls to them
// will await the batch request and return the requested field value.
unrequested.forEach(p => p.setExternalValuePromise(allSetPromise));
@@ -280,11 +282,11 @@ class ListImpl<T extends FieldType> extends ObjectField {
}
@serializable(alias(ListFieldName, serializrList(autoObject(), { afterDeserialize: afterDocDeserialize })))
- private get __fieldTuples() {
+ get __fieldTuples() {
return this[FieldTuples];
}
- private set __fieldTuples(value) {
+ set __fieldTuples(value) {
this[FieldTuples] = value;
Object.keys(value).forEach(key => {
const item = value[Number(key)];
@@ -297,7 +299,7 @@ class ListImpl<T extends FieldType> extends ObjectField {
[Copy]() {
const copiedData = this[Self].__fieldTuples.map(f => (f instanceof ObjectField ? f[Copy]() : f));
- const deepCopy = new ListImpl<T>(copiedData as any);
+ const deepCopy = new ListImpl<T>(copiedData as T[]);
return deepCopy;
}
@@ -309,19 +311,19 @@ class ListImpl<T extends FieldType> extends ObjectField {
private [SelfProxy]: List<FieldType>; // also used in utils.ts even though it won't be found using find all references
[ToScriptString]() { return `new List(${this[ToJavascriptString]()})`; } // prettier-ignore
- [ToJavascriptString]() { return `[${(this as any).map((field: any) => Field.toScriptString(field))}]`; } // prettier-ignore
- [ToString]() { return `[${(this as any).map((field: any) => Field.toString(field))}]`; } // prettier-ignore
+ [ToJavascriptString]() { return `[${(this[FieldTuples]).map(field => Field.toScriptString(field))}]`; } // prettier-ignore
+ [ToString]() { return `[${(this[FieldTuples]).map(field => Field.toString(field))}]`; } // prettier-ignore
}
// declare List as a type so you can use it in type declarations, e.g., { l: List, ...}
export type List<T extends FieldType> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[];
-// decalre List as a value so you can invoke 'new' on it, e.g., new List<Doc>()
+// decalre List as a value so you can invoke 'new' on it, e.g., new List<Doc>() (since List<T> IS ListImpl<T>, we can safely cast the 'new' return value to return List<T>)
// eslint-disable-next-line no-redeclare
-export const List: { new <T extends FieldType>(fields?: T[]): List<T> } = ListImpl as any;
+export const List: { new <T extends FieldType>(fields?: T[]): List<T> } = ListImpl as unknown as { new <T extends FieldType>(fields?: T[]): List<T> };
ScriptingGlobals.add('List', List);
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function compareLists(l1: any, l2: any) {
+ScriptingGlobals.add(function compareLists(l1: List<FieldType>, l2: List<FieldType>) {
const L1 = StrListCast(l1);
const L2 = StrListCast(l2);
return !L1 && !L2 ? true : L1 && L2 && L1.length === L2.length && L2.reduce((p, v) => p && L1.includes(v), true);
diff --git a/src/fields/ObjectField.ts b/src/fields/ObjectField.ts
index 231086262..c533cb596 100644
--- a/src/fields/ObjectField.ts
+++ b/src/fields/ObjectField.ts
@@ -2,13 +2,27 @@ import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
import { Copy, FieldChanged, Parent, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
import { RefField } from './RefField';
+export type serializedFieldType = { fieldId: string; heading?: string; __type: string };
+export type serializedFieldsType = { [key: string]: { fields: serializedFieldType[] } };
+export interface serializedDoctype {
+ readonly id: string;
+ readonly fields?: serializedFieldsType;
+}
+
+export type serverOpType = {
+ $set?: serializedFieldsType; //
+ $unset?: { [key: string]: unknown };
+ $remFromSet?: { [key: string]: { fields: serializedFieldType[] } | { deleteCount: number; start: number } | number | undefined; length: number; hint: { deleteCount: number; start: number } | undefined };
+ $addToSet?: { [key: string]: { fields: serializedFieldType[] } | number | undefined; length: number };
+};
export abstract class ObjectField {
// prettier-ignore
public [FieldChanged]?: (diff?: { op: '$addToSet' | '$remFromSet' | '$set';
// eslint-disable-next-line no-use-before-define
items: FieldType[] | undefined;
length: number | undefined;
- hint?: any }, serverOp?: any) => void;
+ hint?: { deleteCount: number, start: number} },
+ serverOp?: serverOpType) => void;
// eslint-disable-next-line no-use-before-define
public [Parent]?: RefField | ObjectField;
abstract [Copy](): ObjectField;
@@ -22,15 +36,4 @@ export abstract class ObjectField {
}
export type FieldType = number | string | boolean | ObjectField | RefField; // bcz: hack for now .. must match the type definition in Doc.ts .. put here to avoid import cycles
-// eslint-disable-next-line import/no-mutable-exports
-export let ObjGetRefField: (id: string, force?: boolean) => Promise<RefField | undefined>;
-// eslint-disable-next-line import/no-mutable-exports
-export let ObjGetRefFields: (ids: string[]) => Promise<{ [id: string]: RefField | undefined }>;
-
-export function SetObjGetRefField(func: (id: string, force?: boolean) => Promise<RefField | undefined>) {
- ObjGetRefField = func;
-}
-export function SetObjGetRefFields(func: (ids: string[]) => Promise<{ [id: string]: RefField | undefined }>) {
- ObjGetRefFields = func;
-}
ScriptingGlobals.add(ObjectField);
diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts
index 83b5672b3..48c336e60 100644
--- a/src/fields/Proxy.ts
+++ b/src/fields/Proxy.ts
@@ -3,18 +3,19 @@ import { primitive, serializable } from 'serializr';
import { DocServer } from '../client/DocServer';
import { scriptingGlobal } from '../client/util/ScriptingGlobals';
import { Deserializable } from '../client/util/SerializationHelper';
-import { Field, FieldWaiting, Opt } from './Doc';
+import { Doc, Field, FieldWaiting, Opt } from './Doc';
import { Copy, Id, ToJavascriptString, ToScriptString, ToString, ToValue } from './FieldSymbols';
import { ObjectField } from './ObjectField';
-import { RefField } from './RefField';
-function deserializeProxy(field: any) {
+type serializedProxyType = { cache: { field: unknown; p: undefined | Promise<unknown> }; fieldId: string };
+
+function deserializeProxy(field: serializedProxyType) {
if (!field.cache.field) {
- field.cache = { field: DocServer.GetCachedRefField(field.fieldId) as any, p: undefined };
+ field.cache = { field: DocServer.GetCachedRefField(field.fieldId), p: undefined };
}
}
-@Deserializable('proxy', deserializeProxy)
-export class ProxyField<T extends RefField> extends ObjectField {
+@Deserializable('proxy', (obj: unknown) => deserializeProxy(obj as serializedProxyType))
+export class ProxyField<T extends Doc> extends ObjectField {
constructor();
constructor(value: T);
constructor(fieldId: string);
@@ -39,10 +40,10 @@ export class ProxyField<T extends RefField> extends ObjectField {
}
[ToJavascriptString]() {
- return Field.toScriptString(this[ToValue]()?.value);
+ return Field.toScriptString(this[ToValue]()?.value as T);
}
[ToScriptString]() {
- return Field.toScriptString(this[ToValue]()?.value); // not sure this is quite right since it doesn't recreate a proxy field, but better than 'invalid' ?
+ return Field.toScriptString(this[ToValue]()?.value as T); // not sure this is quite right since it doesn't recreate a proxy field, but better than 'invalid' ?
}
[ToString]() {
return Field.toString(this[ToValue]()?.value);
@@ -83,7 +84,7 @@ export class ProxyField<T extends RefField> extends ObjectField {
return !!(!this.cache.field && !this.failed && !this._cache.p && !DocServer.GetCachedRefField(this.fieldId));
}
- setExternalValuePromise(externalValuePromise: Promise<any>) {
+ setExternalValuePromise(externalValuePromise: Promise<unknown>) {
this.cache.p = externalValuePromise.then(() => this.value) as FieldWaiting<T>;
}
@action
@@ -94,7 +95,7 @@ export class ProxyField<T extends RefField> extends ObjectField {
}
}
-// eslint-disable-next-line no-redeclare
+// eslint-disable-next-line no-redeclare, @typescript-eslint/no-namespace
export namespace ProxyField {
let useProxy = true;
export function DisableProxyFields() {
@@ -114,7 +115,7 @@ export namespace ProxyField {
}
}
- export function toValue(value: any) {
+ export function toValue(value: { value: unknown }) {
if (useProxy) {
return { value: value.value };
}
@@ -123,10 +124,10 @@ export namespace ProxyField {
}
// eslint-disable-next-line no-use-before-define
-function prefetchValue(proxy: PrefetchProxy<RefField>) {
- return proxy.value as any;
+function prefetchValue(proxy: PrefetchProxy<Doc>) {
+ return proxy.value as Promise<Doc>;
}
@scriptingGlobal
-@Deserializable('prefetch_proxy', prefetchValue)
-export class PrefetchProxy<T extends RefField> extends ProxyField<T> {}
+@Deserializable('prefetch_proxy', (obj:unknown) => prefetchValue(obj as PrefetchProxy<Doc>))
+export class PrefetchProxy<T extends Doc> extends ProxyField<T> {}
diff --git a/src/fields/RefField.ts b/src/fields/RefField.ts
index 1ce81368a..4ef2a6748 100644
--- a/src/fields/RefField.ts
+++ b/src/fields/RefField.ts
@@ -14,7 +14,7 @@ export abstract class RefField {
this[Id] = this.__id;
}
- protected [HandleUpdate]?(diff: any): void | Promise<void>;
+ protected [HandleUpdate]?(diff: unknown): void | Promise<void>;
abstract [ToJavascriptString](): string;
abstract [ToScriptString](): string;
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index 3763dcd2c..b3534dde7 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -1,9 +1,10 @@
+/* eslint-disable @typescript-eslint/no-namespace */
/* eslint-disable no-await-in-loop */
/* eslint-disable no-use-before-define */
import { AssertionError } from 'assert';
import * as Color from 'color';
import { docs_v1 as docsV1 } from 'googleapis';
-import { Fragment, Mark, Node } from 'prosemirror-model';
+import { Fragment, Mark, Node, Schema } from 'prosemirror-model';
import { sinkListItem } from 'prosemirror-schema-list';
import { EditorState, TextSelection, Transaction } from 'prosemirror-state';
import { ClientUtils, DashColor } from '../ClientUtils';
@@ -26,7 +27,7 @@ export namespace RichTextUtils {
const joiner = '';
export const Initialize = (initial?: string) => {
- const content: any[] = [];
+ const content: object[] = [];
const state = {
doc: {
type: 'doc',
@@ -80,8 +81,10 @@ export namespace RichTextUtils {
// Preserve the current state, but re-write the content to be the blocks
const parsed = JSON.parse(oldState ? oldState.Data : Initialize());
parsed.doc.content = elements.map(text => {
- const paragraph: any = { type: 'paragraph' };
- text.length && (paragraph.content = [{ type: 'text', marks: [], text }]); // An empty paragraph gets treated as a line break
+ const paragraph: object = {
+ type: 'paragraph',
+ content: text.length ? [{ type: 'text', marks: [], text }] : undefined, // An empty paragraph gets treated as a line break
+ };
return paragraph;
});
@@ -164,7 +167,7 @@ export namespace RichTextUtils {
const inlineObjectMap = await parseInlineObjects(document);
const title = document.title!;
const { text, paragraphs } = GoogleApiClientUtils.Docs.Utils.extractText(document);
- let state = EditorState.create(new FormattedTextBox({} as any).config);
+ let state = EditorState.create(FormattedTextBox.MakeConfig());
const structured = parseLists(paragraphs);
let position = 3;
@@ -253,17 +256,20 @@ export namespace RichTextUtils {
return groups;
};
- const listItem = (lschema: any, runs: docsV1.Schema$TextRun[]): Node => lschema.node('list_item', null, paragraphNode(lschema, runs));
+ const listItem = (lschema: Schema, runs: docsV1.Schema$TextRun[]): Node => lschema.node('list_item', null, paragraphNode(lschema, runs));
- const list = (lschema: any, items: Node[]): Node => lschema.node('ordered_list', { mapStyle: 'bullet' }, items);
+ const list = (lschema: Schema, items: Node[]): Node => lschema.node('ordered_list', { mapStyle: 'bullet' }, items);
- const paragraphNode = (lschema: any, runs: docsV1.Schema$TextRun[]): Node => {
- const children = runs.map(run => textNode(lschema, run)).filter(child => child !== undefined);
+ const paragraphNode = (lschema: Schema, runs: docsV1.Schema$TextRun[]): Node => {
+ const children = runs
+ .map(run => textNode(lschema, run))
+ .filter(child => child !== undefined)
+ .map(child => child!);
const fragment = children.length ? Fragment.from(children) : undefined;
return lschema.node('paragraph', null, fragment);
};
- const imageNode = (lschema: any, image: ImageTemplate, textNote: Doc) => {
+ const imageNode = (lschema: Schema, image: ImageTemplate, textNote: Doc) => {
const { url: src, width, agnostic } = image;
let docId: string;
const guid = Utils.GenerateDeterministicGuid(agnostic);
@@ -279,7 +285,7 @@ export namespace RichTextUtils {
return lschema.node('image', { src, agnostic, width, docId, float: null });
};
- const textNode = (lschema: any, run: docsV1.Schema$TextRun) => {
+ const textNode = (lschema: Schema, run: docsV1.Schema$TextRun) => {
const text = run.content!.removeTrailingNewlines();
return text.length ? lschema.text(text, styleToMarks(lschema, run.textStyle)) : undefined;
};
@@ -291,29 +297,33 @@ export namespace RichTextUtils {
['fontSize', 'pFontSize'],
]);
- const styleToMarks = (lschema: any, textStyle?: docsV1.Schema$TextStyle) => {
+ const styleToMarks = (lschema: Schema, textStyle?: docsV1.Schema$TextStyle) => {
if (!textStyle) {
return undefined;
}
const marks: Mark[] = [];
Object.keys(textStyle).forEach(key => {
const targeted = key as keyof docsV1.Schema$TextStyle;
- const value = textStyle[targeted] as any;
+ const value = textStyle[targeted];
if (value) {
- const attributes: any = {};
+ const attributes: { [key: string]: number | string } = {};
let converted = StyleToMark.get(targeted) || targeted;
- value.url && (attributes.href = value.url);
- if (value.color) {
- const object = value.color.rgbColor;
- attributes.color = Color.rgb(['red', 'green', 'blue'].map(color => object[color] * 255 || 0)).hex();
+ const urlValue = value as docsV1.Schema$Link;
+ urlValue.url && (attributes.href = urlValue.url);
+ const colValue = value as docsV1.Schema$OptionalColor;
+ const object = colValue.color?.rgbColor;
+ if (object) {
+ attributes.color = Color.rgb(['red', 'green', 'blue'].map(color => (object as { [key: string]: number })[color] * 255 || 0)).hex();
}
- if (value.magnitude) {
- attributes.fontSize = value.magnitude;
+ const magValue = value as docsV1.Schema$Dimension;
+ if (magValue.magnitude) {
+ attributes.fontSize = magValue.magnitude;
}
+ const fontValue = value as docsV1.Schema$WeightedFontFamily;
if (converted === 'weightedFontFamily') {
- converted = ImportFontFamilyMapping.get(value.fontFamily) || 'timesNewRoman';
+ converted = (fontValue.fontFamily && ImportFontFamilyMapping.get(fontValue.fontFamily)) || 'timesNewRoman';
}
const mapped = lschema.marks[converted];
@@ -384,13 +394,11 @@ export namespace RichTextUtils {
for (const markName of Object.keys(schema.marks)) {
// eslint-disable-next-line no-cond-assign
if (ignored.includes(markName) || !(mark = markMap[markName])) {
- // eslint-disable-next-line no-continue
continue;
}
let converted = MarkToStyle.get(markName) || (markName as keyof docsV1.Schema$TextStyle);
- let value: any = true;
+ let value: unknown = true;
if (!converted) {
- // eslint-disable-next-line no-continue
continue;
}
// eslint-disable-next-line @typescript-eslint/no-shadow
@@ -402,10 +410,8 @@ export namespace RichTextUtils {
const docDelimeter = '/doc/';
const alreadyShared = '?sharing=true';
if (new RegExp(window.location.origin + docDelimeter).test(url) && !url.endsWith(alreadyShared)) {
- // eslint-disable-next-line no-await-in-loop
const linkDoc = await DocServer.GetRefField(url.split(docDelimeter)[1]);
if (linkDoc instanceof Doc) {
- // eslint-disable-next-line no-await-in-loop
let exported = (await Cast(linkDoc.link_anchor_2, Doc))!;
if (!exported.customLayout) {
exported = Doc.MakeEmbedding(exported);
@@ -436,7 +442,7 @@ export namespace RichTextUtils {
converted = 'fontSize';
value = { magnitude: parseInt(matches[1].replace('px', '')), unit: 'PT' };
}
- textStyle[converted] = value;
+ textStyle[converted] = value as undefined;
}
if (Object.keys(textStyle).length) {
requests.push(EncodeStyleUpdate(information));
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 8fe365ac2..582c09f29 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -1,15 +1,15 @@
import { action, makeObservable, observable } from 'mobx';
import { computedFn } from 'mobx-utils';
import { PropSchema, SKIP, createSimpleSchema, custom, map, object, primitive, serializable } from 'serializr';
-import { numberRange } from '../Utils';
+import { emptyFunction, numberRange } from '../Utils';
import { GPTCallType, gptAPICall } from '../client/apis/gpt/GPT';
import { CompileScript, CompiledScript, ScriptOptions, Transformer } from '../client/util/Scripting';
import { ScriptingGlobals, scriptingGlobal } from '../client/util/ScriptingGlobals';
import { Deserializable, autoObject } from '../client/util/SerializationHelper';
-import { Doc, Field, FieldType, FieldResult, Opt } from './Doc';
+import { Doc, Field, FieldType, FieldResult, ObjGetRefField, Opt } from './Doc';
import { Copy, FieldChanged, Id, ToJavascriptString, ToScriptString, ToString, ToValue } from './FieldSymbols';
import { List } from './List';
-import { ObjGetRefField, ObjectField } from './ObjectField';
+import { ObjectField } from './ObjectField';
import { Cast, StrCast } from './Types';
function optional(propSchema: PropSchema) {
@@ -20,7 +20,7 @@ function optional(propSchema: PropSchema) {
}
return SKIP;
},
- (jsonValue: any, context: any, oldValue: any, callback: (err: any, result: any) => void) => {
+ (jsonValue, context, oldValue, callback) => {
if (jsonValue !== undefined) {
return propSchema.deserializer(jsonValue, callback, context, oldValue);
}
@@ -63,7 +63,7 @@ function finalizeScript(scriptIn: ScriptField) {
async function deserializeScript(scriptIn: ScriptField) {
const script = scriptIn;
if (script.captures) {
- const captured: any = {};
+ const captured: { [key: string]: undefined | string | number | boolean | Doc } = {};
(script.script.options as ScriptOptions).capturedVariables = captured;
Promise.all(
script.captures.map(async capture => {
@@ -85,7 +85,7 @@ async function deserializeScript(scriptIn: ScriptField) {
}
@scriptingGlobal
-@Deserializable('script', deserializeScript)
+@Deserializable('script', (obj: unknown) => deserializeScript(obj as ScriptField))
export class ScriptField extends ObjectField {
@serializable
readonly rawscript: string | undefined;
@@ -114,7 +114,7 @@ export class ScriptField extends ObjectField {
const captured = script?.options?.capturedVariables;
if (captured) {
- this.captures = new List<string>(Object.keys(captured).map(key => key + ':' + (captured[key] instanceof Doc ? 'ID->' + (captured[key] as Doc)[Id] : captured[key].toString())));
+ this.captures = new List<string>(Object.keys(captured).map(key => key + ':' + (captured[key] instanceof Doc ? 'ID->' + (captured[key] as Doc)[Id] : captured[key]?.toString())));
}
this.rawscript = rawscript;
this.setterscript = setterscript;
@@ -186,7 +186,7 @@ export class ScriptField extends ObjectField {
}
@scriptingGlobal
-@Deserializable('computed', deserializeScript)
+@Deserializable('computed', (obj: unknown) => deserializeScript(obj as ComputedField))
export class ComputedField extends ScriptField {
static undefined = '__undefined';
static useComputed = true;
@@ -221,7 +221,7 @@ export class ComputedField extends ScriptField {
_readOnly_: true,
},
console.log
- ).result
+ ).result as FieldResult
)(); // prettier-ignore
return this._lastComputedResult;
};
@@ -239,7 +239,7 @@ export class ComputedField extends ScriptField {
public static MakeInterpolatedNumber(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number, defaultVal: Opt<number>) {
if (!doc[`${fieldKey}_indexed`]) {
- const flist = new List<number>(numberRange(curTimecode + 1).map(() => undefined) as any as number[]);
+ const flist = new List<number>(numberRange(curTimecode + 1).map(emptyFunction) as unknown as number[]);
flist[curTimecode] = Cast(doc[fieldKey], 'number', null);
doc[`${fieldKey}_indexed`] = flist;
}
@@ -249,7 +249,7 @@ export class ComputedField extends ScriptField {
}
public static MakeInterpolatedString(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) {
if (!doc[`${fieldKey}_`]) {
- const flist = new List<string>(numberRange(curTimecode + 1).map(() => undefined) as any as string[]);
+ const flist = new List<string>(numberRange(curTimecode + 1).map(emptyFunction) as unknown as string[]);
flist[curTimecode] = StrCast(doc[fieldKey]);
doc[`${fieldKey}_indexed`] = flist;
}
@@ -260,7 +260,7 @@ export class ComputedField extends ScriptField {
public static MakeInterpolatedDataField(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) {
if (doc[`${fieldKey}`] instanceof List) return undefined;
if (!doc[`${fieldKey}_indexed`]) {
- const flist = new List<FieldType>(numberRange(curTimecode + 1).map(() => undefined) as any as FieldType[]);
+ const flist = new List<FieldType>(numberRange(curTimecode + 1).map(emptyFunction) as unknown as FieldType[]);
flist[curTimecode] = Field.Copy(doc[fieldKey]);
doc[`${fieldKey}_indexed`] = flist;
}
@@ -278,7 +278,7 @@ export class ComputedField extends ScriptField {
ScriptingGlobals.add(
// eslint-disable-next-line prefer-arrow-callback
- function setIndexVal(list: any[], index: number, value: any) {
+ function setIndexVal(list: FieldResult[], index: number, value: FieldType) {
while (list.length <= index) list.push(undefined);
list[index] = value;
},
@@ -288,7 +288,7 @@ ScriptingGlobals.add(
ScriptingGlobals.add(
// eslint-disable-next-line prefer-arrow-callback
- function getIndexVal(list: any[], index: number, defaultVal: Opt<number> = undefined) {
+ function getIndexVal(list: unknown[], index: number, defaultVal: Opt<number> = undefined) {
return list?.reduce((p, x, i) => ((i <= index && x !== undefined) || p === undefined ? x : p), defaultVal);
},
'returns the value at a given index of a list',
diff --git a/src/fields/util.ts b/src/fields/util.ts
index a6499c3e3..60eadcdfd 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -7,8 +7,8 @@ import { UndoManager } from '../client/util/UndoManager';
import { Doc, DocListCast, FieldType, FieldResult, HierarchyMapping, ReverseHierarchyMap, StrListCast, aclLevel, updateCachedAcls } from './Doc';
import { AclAdmin, AclAugment, AclEdit, AclPrivate, DirectLinks, DocAcl, DocData, DocLayout, FieldKeys, ForceServerWrite, Height, Initializing, SelfProxy, UpdatingFromServer, Width } from './DocSymbols';
import { FieldChanged, Id, Parent, ToValue } from './FieldSymbols';
-import { List } from './List';
-import { ObjectField } from './ObjectField';
+import { List, ListImpl } from './List';
+import { ObjectField, serializedFieldType, serverOpType } from './ObjectField';
import { PrefetchProxy, ProxyField } from './Proxy';
import { RefField } from './RefField';
import { RichTextField } from './RichTextField';
@@ -44,15 +44,23 @@ export function TraceMobx() {
tracing && trace();
}
-export const _propSetterCB = new Map<string, ((target: any, value: any) => void) | undefined>();
+export const _propSetterCB = new Map<string, ((target: Doc, value: FieldType) => void) | undefined>();
-const _setterImpl = action((target: any, prop: string | symbol | number, valueIn: any, receiver: any): boolean => {
+const _setterImpl = action((target: Doc | ListImpl<FieldType>, prop: string | symbol | number, valueIn: unknown, receiver: Doc | ListImpl<FieldType>): boolean => {
+ if (target instanceof ListImpl) {
+ if (typeof prop !== 'symbol' && +prop == prop) {
+ target[SelfProxy].splice(+prop, 1, valueIn as FieldType);
+ } else {
+ target[prop] = valueIn as FieldType;
+ }
+ return true;
+ }
if (SerializationHelper.IsSerializing() || typeof prop === 'symbol') {
- target[prop] = valueIn;
+ target[prop] = valueIn as FieldResult;
return true;
}
- let value = valueIn?.[SelfProxy] ?? valueIn; // convert any Doc type values to Proxy's
+ let value = (valueIn as Doc | ListImpl<FieldType>)?.[SelfProxy] ?? valueIn; // convert any Doc type values to Proxy's
const curValue = target.__fieldTuples[prop];
if (curValue === value || (curValue instanceof ProxyField && value instanceof RefField && curValue.fieldId === value[Id])) {
@@ -60,7 +68,7 @@ const _setterImpl = action((target: any, prop: string | symbol | number, valueIn
// curValue should get filled in with value if it isn't already filled in, in case we fetched the referenced field some other way
return true;
}
- if (value instanceof RefField) {
+ if (value instanceof Doc) {
value = new ProxyField(value);
}
@@ -77,7 +85,7 @@ const _setterImpl = action((target: any, prop: string | symbol | number, valueIn
delete curValue[FieldChanged];
}
- if (typeof prop === 'string' && _propSetterCB.has(prop)) _propSetterCB.get(prop)!(target[SelfProxy], value);
+ if (typeof prop === 'string' && _propSetterCB.has(prop)) _propSetterCB.get(prop)!(target[SelfProxy], value as FieldType);
// eslint-disable-next-line no-use-before-define
const effectiveAcl = GetEffectiveAcl(target);
@@ -104,20 +112,21 @@ const _setterImpl = action((target: any, prop: string | symbol | number, valueIn
if (writeToServer) {
// prettier-ignore
- if (value === undefined)
+ if (value === undefined || value === null)
(target as Doc|ObjectField)[FieldChanged]?.(undefined, { $unset: { ['fields.' + prop]: '' } });
- else (target as Doc|ObjectField)[FieldChanged]?.(undefined, { $set: { ['fields.' + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) :value}});
+ else (target as Doc|ObjectField)[FieldChanged]?.(undefined, { $set: { ['fields.' + prop]: (value instanceof ObjectField ? SerializationHelper.Serialize(value) :value) as { fields: serializedFieldType[]}}});
if (prop === 'author' || prop.toString().startsWith('acl_')) updateCachedAcls(target);
- } else {
+ } else if (receiver instanceof Doc) {
DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue);
}
!receiver[Initializing] &&
+ receiver instanceof Doc &&
!StrListCast(receiver.undoIgnoreFields).includes(prop.toString()) &&
(!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) &&
UndoManager.AddEvent(
{
redo: () => {
- receiver[prop] = value;
+ receiver[prop] = value as FieldType;
},
undo: () => {
const wasUpdate = receiver[UpdatingFromServer];
@@ -137,7 +146,7 @@ const _setterImpl = action((target: any, prop: string | symbol | number, valueIn
return true;
});
-let _setter: (target: any, prop: string | symbol | number, value: any, receiver: any) => boolean = _setterImpl;
+let _setter: (target: Doc | ListImpl<FieldType>, prop: string | symbol | number, value: FieldType | undefined, receiver: Doc | ListImpl<FieldType>) => boolean = _setterImpl;
export function makeReadOnly() {
_setter = _readOnlySetter;
@@ -156,18 +165,18 @@ export function denormalizeEmail(email: string) {
// return acl from cache or cache the acl and return.
// eslint-disable-next-line no-use-before-define
-const getEffectiveAclCache = computedFn((target: any, user?: string) => getEffectiveAcl(target, user), true);
+const getEffectiveAclCache = computedFn((target: Doc | ListImpl<FieldType>, user?: string) => getEffectiveAcl(target, user), true);
/**
* Calculates the effective access right to a document for the current user.
*/
-export function GetEffectiveAcl(target: any, user?: string): symbol {
+export function GetEffectiveAcl(target: Doc | ListImpl<FieldType>, user?: string): symbol {
if (!target) return AclPrivate;
if (target[UpdatingFromServer] || ClientUtils.CurrentUserEmail() === 'guest') return AclAdmin;
return getEffectiveAclCache(target, user); // all changes received from the server must be processed as Admin. return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
}
-export function GetPropAcl(target: any, prop: string | symbol | number) {
+export function GetPropAcl(target: Doc | ListImpl<FieldType>, prop: string | symbol | number) {
if (typeof prop === 'symbol' || target[UpdatingFromServer]) return AclAdmin; // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent
if (prop && DocServer.IsPlaygroundField(prop.toString())) return AclEdit; // playground props are always editable
return GetEffectiveAcl(target);
@@ -182,7 +191,8 @@ export function GetCachedGroupByName(name: string) {
export function SetCachedGroups(groups: string[]) {
runInAction(() => cachedGroups.push(...groups));
}
-function getEffectiveAcl(target: any, user?: string): symbol {
+function getEffectiveAcl(target: Doc | ListImpl<FieldType>, user?: string): symbol {
+ if (target instanceof ListImpl) return AclAdmin;
const targetAcls = target[DocAcl];
if (targetAcls?.acl_Me === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
@@ -287,14 +297,14 @@ export function inheritParentAcls(parent: Doc, child: Doc, layoutOnly: boolean)
* @param prop
* @param propSetter
*/
-export function SetPropSetterCb(prop: string, propSetter: ((target: any, value: any) => void) | undefined) {
+export function SetPropSetterCb(prop: string, propSetter: ((target: Doc, value: FieldType) => void) | undefined) {
_propSetterCB.set(prop, propSetter);
}
//
// target should be either a Doc or ListImpl. receiver should be a Proxy<Doc> Or List.
//
-export function setter(target: any, inProp: string | symbol | number, value: any, receiver: any): boolean {
+export function setter(target: ListImpl<FieldType> | Doc, inProp: string | symbol | number, value: unknown, receiver: Doc | ListImpl<FieldType>): boolean {
if (!inProp) {
console.log('WARNING: trying to set an empty property. This should be fixed. ');
return false;
@@ -303,12 +313,12 @@ export function setter(target: any, inProp: string | symbol | number, value: any
const effectiveAcl = inProp === 'constructor' || typeof inProp === 'symbol' ? AclAdmin : GetPropAcl(target, prop);
if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && effectiveAcl !== AclAdmin) return true;
// if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't
- if (typeof prop === 'string' && prop.startsWith('acl_') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value))) return true;
+ if (typeof prop === 'string' && prop.startsWith('acl_') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value as SharingPermissions))) return true;
if (typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('_')) {
if (!prop.startsWith('__')) prop = prop.substring(1);
- if (target.__LAYOUT__) {
- target.__LAYOUT__[prop] = value;
+ if (target.__LAYOUT__ instanceof Doc) {
+ target.__LAYOUT__[prop] = value as FieldResult;
return true;
}
}
@@ -317,10 +327,10 @@ export function setter(target: any, inProp: string | symbol | number, value: any
return !!ScriptCast(target.__fieldTuples[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success;
}
}
- return _setter(target, prop, value, receiver);
+ return _setter(target, prop, value as FieldType, receiver);
}
-function getFieldImpl(target: any, prop: string | number, proxy: any, ignoreProto: boolean = false): any {
+function getFieldImpl(target: ListImpl<FieldType> | Doc, prop: string | number, proxy: ListImpl<FieldType> | Doc, ignoreProto: boolean = false): FieldType {
const field = target.__fieldTuples[prop];
const value = field?.[ToValue]?.(proxy); // converts ComputedFields to values, or unpacks ProxyFields into Proxys
if (value) return value.value;
@@ -332,7 +342,7 @@ function getFieldImpl(target: any, prop: string | number, proxy: any, ignoreProt
}
return field;
}
-export function getter(target: any, prop: string | symbol, proxy: any): any {
+export function getter(target: Doc | ListImpl<FieldType>, prop: string | symbol, proxy: ListImpl<FieldType> | Doc): unknown {
// prettier-ignore
switch (prop) {
case 'then' : return undefined;
@@ -352,19 +362,23 @@ export function getter(target: any, prop: string | symbol, proxy: any): any {
}
const layoutProp = prop.startsWith('_') ? prop.substring(1) : undefined;
- if (layoutProp && target.__LAYOUT__) return target.__LAYOUT__[layoutProp];
+ if (layoutProp && target.__LAYOUT__) return (target.__LAYOUT__ as Doc)[layoutProp];
return getFieldImpl(target, layoutProp ?? prop, proxy);
}
-export function getField(target: any, prop: string | number, ignoreProto: boolean = false): any {
- return getFieldImpl(target, prop, target[SelfProxy], ignoreProto);
+export function getField(target: ListImpl<FieldType> | Doc, prop: string | number, ignoreProto: boolean = false): unknown {
+ return getFieldImpl(target, prop, target[SelfProxy] as Doc, ignoreProto);
}
-export function deleteProperty(target: any, prop: string | number | symbol) {
+export function deleteProperty(target: Doc | ListImpl<FieldType>, prop: string | number | symbol) {
if (typeof prop === 'symbol') {
delete target[prop];
} else {
- target[SelfProxy][prop] = undefined;
+ if (target instanceof Doc) {
+ target[SelfProxy][prop] = undefined;
+ } else if (+prop == prop) {
+ target[SelfProxy].splice(+prop, 1);
+ }
}
return true;
}
@@ -378,39 +392,42 @@ export function deleteProperty(target: any, prop: string | number | symbol) {
// were replaced. Based on this specification, an Undo event is setup that will save enough information about the ObjectField to be
// able to undo and redo the partial change.
//
-export function containedFieldChangedHandler(container: List<FieldType> | Doc, prop: string | number, liveContainedField: ObjectField) {
- let lastValue: FieldResult = liveContainedField instanceof ObjectField ? ObjectField.MakeCopy(liveContainedField) : liveContainedField;
- return (diff?: { op: '$addToSet' | '$remFromSet' | '$set'; items: FieldType[] | undefined; length: number | undefined; hint?: any } /* , dummyServerOp?: any */) => {
- const serializeItems = () => ({ __type: 'list', fields: diff?.items?.map((item: FieldType) => SerializationHelper.Serialize(item)) });
+export function containedFieldChangedHandler(container: ListImpl<FieldType> | Doc, prop: string | number, liveContainedField: ObjectField) {
+ let lastValue = ObjectField.MakeCopy(liveContainedField);
+ return (diff?: { op: '$addToSet' | '$remFromSet' | '$set'; items: (FieldType & { value?: FieldType })[] | undefined; length: number | undefined; hint?: { start: number; deleteCount: number } } /* , dummyServerOp?: any */) => {
+ const serializeItems = () => ({ __type: 'list', fields: diff?.items?.map((item: FieldType) => SerializationHelper.Serialize(item) as serializedFieldType) ?? [] });
// prettier-ignore
- const serverOp = diff?.op === '$addToSet'
- ? { $addToSet: { ['fields.' + prop]: serializeItems() }, length: diff.length }
+ const serverOp: serverOpType = diff?.op === '$addToSet'
+ ? { $addToSet: { ['fields.' + prop]: serializeItems(), length: diff.length ??0 }}
: diff?.op === '$remFromSet'
- ? { $remFromSet: { ['fields.' + prop]: serializeItems(), hint: diff.hint}, length: diff.length }
- : { $set: { ['fields.' + prop]: liveContainedField ? SerializationHelper.Serialize(liveContainedField) : undefined } };
+ ? { $remFromSet: { ['fields.' + prop]: serializeItems(), hint: diff.hint, length: diff.length ?? 0 } }
+ : { $set: { ['fields.' + prop]: SerializationHelper.Serialize(liveContainedField) as {fields: serializedFieldType[]}} };
if (!(container instanceof Doc) || !container[UpdatingFromServer]) {
- const prevValue = ObjectField.MakeCopy(lastValue as List<any>);
+ const cont = container as { [key: string | number]: FieldType };
+ const prevValue = ObjectField.MakeCopy(lastValue as List<FieldType>);
lastValue = ObjectField.MakeCopy(liveContainedField);
const newValue = ObjectField.MakeCopy(liveContainedField);
if (diff?.op === '$addToSet') {
UndoManager.AddEvent(
{
redo: () => {
+ const contList = cont[prop] as List<FieldType>;
// console.log('redo $add: ' + prop, diff.items); // bcz: uncomment to log undo
- (container as any)[prop as any]?.push(...((diff.items || [])?.map((item: any) => item.value ?? item) ?? []));
- lastValue = ObjectField.MakeCopy((container as any)[prop as any]);
+ contList?.push(...((diff.items || [])?.map(item => item.value ?? item) ?? []));
+ lastValue = ObjectField.MakeCopy(contList);
},
undo: action(() => {
+ const contList = cont[prop] as List<FieldType>;
// console.log('undo $add: ' + prop, diff.items); // bcz: uncomment to log undo
- diff.items?.forEach((item: any) => {
+ diff.items?.forEach(item => {
const ind =
item instanceof SchemaHeaderField //
- ? (container as any)[prop as any]?.findIndex((ele: any) => ele instanceof SchemaHeaderField && ele.heading === item.heading)
- : (container as any)[prop as any]?.indexOf(item.value ?? item);
- ind !== undefined && ind !== -1 && (container as any)[prop as any]?.splice(ind, 1);
+ ? contList?.findIndex(ele => ele instanceof SchemaHeaderField && ele.heading === item.heading)
+ : contList?.indexOf(item.value ?? item);
+ ind !== undefined && ind !== -1 && (cont[prop] as List<FieldType>)?.splice(ind, 1);
});
- lastValue = ObjectField.MakeCopy((container as any)[prop as any]);
+ lastValue = ObjectField.MakeCopy(contList);
}),
prop: 'add ' + (diff.items?.length ?? 0) + ' items to list',
},
@@ -420,48 +437,53 @@ export function containedFieldChangedHandler(container: List<FieldType> | Doc, p
UndoManager.AddEvent(
{
redo: action(() => {
+ const contList = cont[prop] as List<FieldType>;
// console.log('redo $rem: ' + prop, diff.items); // bcz: uncomment to log undo
- diff.items?.forEach((item: any) => {
+ diff.items?.forEach(item => {
const ind =
item instanceof SchemaHeaderField //
- ? (container as any)[prop as any]?.findIndex((ele: any) => ele instanceof SchemaHeaderField && ele.heading === item.heading)
- : (container as any)[prop as any]?.indexOf(item.value ?? item);
- ind !== undefined && ind !== -1 && (container as any)[prop as any]?.splice(ind, 1);
+ ? contList?.findIndex(ele => ele instanceof SchemaHeaderField && ele.heading === item.heading)
+ : contList?.indexOf(item.value ?? item);
+ ind !== undefined && ind !== -1 && contList?.splice(ind, 1);
});
- lastValue = ObjectField.MakeCopy((container as any)[prop as any]);
+ lastValue = ObjectField.MakeCopy(contList);
}),
undo: () => {
+ const contList = cont[prop] as List<FieldType>;
+ const prevList = prevValue as List<FieldType>;
// console.log('undo $rem: ' + prop, diff.items); // bcz: uncomment to log undo
- diff.items?.forEach((item: any) => {
+ diff.items?.forEach(item => {
if (item instanceof SchemaHeaderField) {
- const ind = (prevValue as List<any>).findIndex((ele: any) => ele instanceof SchemaHeaderField && ele.heading === item.heading);
- ind !== -1 && (container as any)[prop as any].findIndex((ele: any) => ele instanceof SchemaHeaderField && ele.heading === item.heading) === -1 && (container as any)[prop as any].splice(ind, 0, item);
+ const ind = prevList.findIndex(ele => ele instanceof SchemaHeaderField && ele.heading === item.heading);
+ ind !== -1 && contList.findIndex(ele => ele instanceof SchemaHeaderField && ele.heading === item.heading) === -1 && contList.splice(ind, 0, item);
} else {
- const ind = (prevValue as List<any>).indexOf(item.value ?? item);
- ind !== -1 && (container as any)[prop as any].indexOf(item.value ?? item) === -1 && (container as any)[prop as any].splice(ind, 0, item);
+ const ind = prevList.indexOf(item.value ?? item);
+ ind !== -1 && contList.indexOf(item.value ?? item) === -1 && (cont[prop] as List<FieldType>).splice(ind, 0, item);
}
});
- lastValue = ObjectField.MakeCopy((container as any)[prop as any]);
+ lastValue = ObjectField.MakeCopy(contList);
},
- prop: 'remove ' + (diff.items?.length ?? 0) + ' items from list(' + ((container as any)?.title ?? '') + ':' + prop + ')',
+ prop: 'remove ' + (diff.items?.length ?? 0) + ' items from list(' + (cont?.title ?? '') + ':' + prop + ')',
},
diff?.items
);
} else {
const setFieldVal = (val: FieldType | undefined) => {
- container instanceof Doc ? (container[prop as string] = val) : (container[prop as number] = val as FieldType);
+ container instanceof Doc ? (container[prop] = val) : (container[prop as number] = val as FieldType);
};
UndoManager.AddEvent(
{
redo: () => {
// console.log('redo list: ' + prop, fieldVal()); // bcz: uncomment to log undo
- setFieldVal(newValue instanceof ObjectField ? ObjectField.MakeCopy(newValue) : undefined);
- lastValue = ObjectField.MakeCopy((container as any)[prop as any]);
+ setFieldVal(ObjectField.MakeCopy(newValue));
+ const containerProp = cont[prop];
+ if (containerProp instanceof ObjectField) lastValue = ObjectField.MakeCopy(containerProp);
},
undo: () => {
// console.log('undo list: ' + prop, fieldVal()); // bcz: uncomment to log undo
- setFieldVal(prevValue instanceof ObjectField ? ObjectField.MakeCopy(prevValue) : undefined);
- lastValue = ObjectField.MakeCopy((container as any)[prop as any]);
+ setFieldVal(ObjectField.MakeCopy(prevValue));
+ const containerProp = cont[prop];
+ if (containerProp instanceof ObjectField) lastValue = ObjectField.MakeCopy(containerProp);
},
prop: 'set list field',
},