aboutsummaryrefslogtreecommitdiff
path: root/src/fields
diff options
context:
space:
mode:
Diffstat (limited to 'src/fields')
-rw-r--r--src/fields/DateField.ts5
-rw-r--r--src/fields/Doc.ts542
-rw-r--r--src/fields/HtmlField.ts4
-rw-r--r--src/fields/IconField.ts2
-rw-r--r--src/fields/InkField.ts11
-rw-r--r--src/fields/List.ts97
-rw-r--r--src/fields/ObjectField.ts26
-rw-r--r--src/fields/Proxy.ts17
-rw-r--r--src/fields/RefField.ts4
-rw-r--r--src/fields/RichTextUtils.ts210
-rw-r--r--src/fields/Schema.ts27
-rw-r--r--src/fields/SchemaHeaderField.ts25
-rw-r--r--src/fields/ScriptField.ts97
-rw-r--r--src/fields/Types.ts90
-rw-r--r--src/fields/URLField.ts40
-rw-r--r--src/fields/documentSchemas.ts4
-rw-r--r--src/fields/util.ts218
17 files changed, 775 insertions, 644 deletions
diff --git a/src/fields/DateField.ts b/src/fields/DateField.ts
index 1e5222fb6..f0a851ce6 100644
--- a/src/fields/DateField.ts
+++ b/src/fields/DateField.ts
@@ -1,5 +1,5 @@
+import { serializable, date as serializrDate } from 'serializr';
import { Deserializable } from '../client/util/SerializationHelper';
-import { serializable, date } from 'serializr';
import { ObjectField } from './ObjectField';
import { Copy, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals';
@@ -7,7 +7,7 @@ import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGloba
@scriptingGlobal
@Deserializable('date')
export class DateField extends ObjectField {
- @serializable(date())
+ @serializable(serializrDate())
readonly date: Date;
constructor(date: Date = new Date()) {
@@ -38,6 +38,7 @@ export class DateField extends ObjectField {
}
}
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function d(...dateArgs: any[]) {
return new DateField(new (Date as any)(...dateArgs));
});
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 48214cf25..725221a66 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1,37 +1,31 @@
-import { saveAs } from 'file-saver';
+/* 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';
import { alias, map, serializable } from 'serializr';
import { DocServer } from '../client/DocServer';
import { CollectionViewType, DocumentType } from '../client/documents/DocumentTypes';
-import { LinkManager } from '../client/util/LinkManager';
import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals';
import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from '../client/util/SerializationHelper';
-import { undoable } from '../client/util/UndoManager';
-import { DocumentView } from '../client/views/nodes/DocumentView';
-import { decycle } from '../decycler/decycler';
-import * as JSZipUtils from '../JSZipUtils';
-import { incrementTitleCopy, Utils } from '../Utils';
-import { DateField } from './DateField';
+import { undoable, UndoManager } from '../client/util/UndoManager';
+import { ClientUtils, incrementTitleCopy } from '../ClientUtils';
import {
AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, Animation, AudioPlay, Brushed, CachedUpdates, DirectLinks,
DocAcl, DocCss, DocData, DocLayout, DocViews, FieldKeys, FieldTuples, ForceServerWrite, Height, Highlight,
Initializing, Self, SelfProxy, TransitionTimer, UpdatingFromServer, Width
} from './DocSymbols'; // prettier-ignore
import { Copy, FieldChanged, HandleUpdate, Id, Parent, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
-import { InkField, InkTool } from './InkField';
-import { List, ListFieldName } from './List';
+import { InkTool } from './InkField';
+import { List } from './List';
import { ObjectField } from './ObjectField';
import { PrefetchProxy, ProxyField } from './Proxy';
import { FieldId, RefField } from './RefField';
import { RichTextField } from './RichTextField';
import { listSpec } from './Schema';
import { ComputedField, ScriptField } from './ScriptField';
-import { BoolCast, Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Types';
-import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField';
+import { BoolCast, Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor, toList } from './Types';
import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, setter, SharingPermissions } from './util';
-import * as JSZip from 'jszip';
-import { FieldViewProps } from '../client/views/nodes/FieldView';
+
export const LinkedTo = '-linkedTo';
export namespace Field {
/**
@@ -43,9 +37,9 @@ export namespace Field {
* @returns string representation of the field
*/
export function toKeyValueString(doc: Doc, key: string, showComputedValue?: boolean): string {
- const onDelegate = !Doc.IsDataProto(doc) && Object.keys(doc).includes(key.replace(/^_/, ''));
- const field = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
- const valFunc = (field: Field): string => {
+ const isOnDelegate = !Doc.IsDataProto(doc) && Object.keys(doc).includes(key.replace(/^_/, ''));
+ const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+ const valFunc = (field: FieldType): string => {
const res =
field instanceof ComputedField && showComputedValue
? field.value(doc)
@@ -61,9 +55,9 @@ export namespace Field {
.trim()
.replace(/^new List\((.*)\)$/, '$1');
};
- return !Field.IsField(field) ? (key.startsWith('_') ? '=' : '') : (onDelegate ? '=' : '') + valFunc(field);
+ return !Field.IsField(cfield) ? (key.startsWith('_') ? '=' : '') : (isOnDelegate ? '=' : '') + valFunc(cfield);
}
- export function toScriptString(field: Field) {
+ export function toScriptString(field: FieldType) {
switch (typeof field) {
case 'string': if (field.startsWith('{"')) return `'${field}'`; // bcz: hack ... want to quote the string the right way. if there are nested "'s, then use ' instead of ". In this case, test for the start of a JSON string of the format {"property": ... } and use outer 's instead of "s
return !field.includes('`') ? `\`${field}\`` : `"${field}"`;
@@ -72,8 +66,8 @@ export namespace Field {
default: return field?.[ToScriptString]?.() ?? 'null';
} // prettier-ignore
}
- export function toJavascriptString(field: Field) {
- var rawjava = '';
+ export function toJavascriptString(field: FieldType) {
+ let rawjava = '';
switch (typeof field) {
case 'string':
@@ -82,12 +76,12 @@ export namespace Field {
break;
default: rawjava = field?.[ToJavascriptString]?.() ?? '';
} // prettier-ignore
- var script = rawjava;
+ let script = rawjava;
// this is a bit hacky, but we treat '^@' references to a published document
// as a kind of macro to include the content of those documents
Doc.MyPublishedDocs.forEach(doc => {
const regexMultilineFlag = 'm';
- const regex = new RegExp(`^\\^${StrCast(doc.title).replace(/[\(\)]*/g, '')}\\s`, regexMultilineFlag); // need to remove characters that can cause the regular expression to be invalid
+ const regex = new RegExp(`^\\^${StrCast(doc.title).replace(/[()]*/g, '')}\\s`, regexMultilineFlag); // need to remove characters that can cause the regular expression to be invalid
const sections = (Cast(doc.text, RichTextField, null)?.Text ?? '').split('--DOCDATA--');
if (script.match(regex)) {
script = script.replace(regex, sections[0]) + (sections.length > 1 ? sections[1] : '');
@@ -95,23 +89,25 @@ export namespace Field {
});
return script;
}
- export function toString(field: Field) {
+ export function toString(field: FieldType) {
if (typeof field === 'string' || typeof field === 'number' || typeof field === 'boolean') return String(field);
return field?.[ToString]?.() || '';
}
- export function IsField(field: any): field is Field;
- export function IsField(field: any, includeUndefined: true): field is Field | undefined;
- export function IsField(field: any, includeUndefined: boolean = false): field is Field | undefined {
+ 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 {
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;
}
+ UndoManager.SetFieldPrinter(toJavascriptString);
}
-export type Field = number | string | boolean | ObjectField | RefField;
+export type FieldType = number | string | boolean | ObjectField | RefField;
export type Opt<T> = T | undefined;
export type FieldWaiting<T extends RefField = RefField> = T extends undefined ? never : Promise<T | undefined>;
-export type FieldResult<T extends Field = Field> = Opt<T> | FieldWaiting<Extract<T, RefField>>;
+export type FieldResult<T extends FieldType = FieldType> = Opt<T> | FieldWaiting<Extract<T, RefField>>;
/**
* Cast any field to either a List of Docs or undefined if the given field isn't a List of Docs.
@@ -120,7 +116,9 @@ export type FieldResult<T extends Field = Field> = Opt<T> | FieldWaiting<Extract
* 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);
@@ -156,19 +154,67 @@ export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol; im
// caches the document access permissions for the current user.
// this recursively updates all protos as well.
export function updateCachedAcls(doc: Doc) {
- if (!doc) return;
+ if (doc) {
+ const target = (doc as any)?.__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);
+ });
+ if (Object.keys(permissions).length || doc[DocAcl]?.length) {
+ runInAction(() => {
+ doc[DocAcl] = permissions;
+ });
+ }
- const target = (doc as any)?.__fieldTuples ?? doc;
- const permissions: { [key: string]: symbol } = !target.author || target.author === Doc.CurrentUserEmail ? { 'acl-Me': AclAdmin } : {};
- Object.keys(target).filter(key => key.startsWith('acl') && (permissions[key] = ReverseHierarchyMap.get(StrCast(target[key]))!.acl));
- if (Object.keys(permissions).length || doc[DocAcl]?.length) {
- runInAction(() => (doc[DocAcl] = permissions));
+ if (doc.proto instanceof Promise) {
+ doc.proto.then(proto => updateCachedAcls(DocCast(proto)));
+ return doc.proto;
+ }
}
+ return undefined;
+}
- if (doc.proto instanceof Promise) {
- doc.proto.then(proto => updateCachedAcls(DocCast(proto)));
- return doc.proto;
- }
+export function ActiveInkPen(): Doc { return Doc.UserDoc(); } // prettier-ignore
+export function ActiveInkColor(): string { return StrCast(ActiveInkPen()?.activeInkColor, 'black'); } // prettier-ignore
+export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, ''); } // prettier-ignore
+export function ActiveIsInkMask(): boolean { return BoolCast(ActiveInkPen()?.activeIsInkMask, false); } // prettier-ignore
+export function ActiveInkHideTextLabels(): boolean { return BoolCast(ActiveInkPen().activeInkHideTextLabels, false); } // prettier-ignore
+export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ''); } // prettier-ignore
+export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ''); } // prettier-ignore
+export function ActiveArrowScale(): number { return NumCast(ActiveInkPen()?.activeArrowScale, 1); } // prettier-ignore
+export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, '0'); } // prettier-ignore
+export function ActiveInkWidth(): number { return Number(ActiveInkPen()?.activeInkWidth); } // prettier-ignore
+export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); } // prettier-ignore
+
+export function SetActiveInkWidth(width: string): void {
+ !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width);
+}
+export function SetActiveBezierApprox(bezier: string): void {
+ ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? '' : bezier);
+}
+export function SetActiveInkColor(value: string) {
+ ActiveInkPen() && (ActiveInkPen().activeInkColor = value);
+}
+export function SetActiveIsInkMask(value: boolean) {
+ ActiveInkPen() && (ActiveInkPen().activeIsInkMask = value);
+}
+export function SetActiveInkHideTextLabels(value: boolean) {
+ ActiveInkPen() && (ActiveInkPen().activeInkHideTextLabels = value);
+}
+export function SetActiveFillColor(value: string) {
+ ActiveInkPen() && (ActiveInkPen().activeFillColor = value);
+}
+export function SetActiveArrowStart(value: string) {
+ ActiveInkPen() && (ActiveInkPen().activeArrowStart = value);
+}
+export function SetActiveArrowEnd(value: string) {
+ ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value);
+}
+export function SetActiveArrowScale(value: number) {
+ ActiveInkPen() && (ActiveInkPen().activeArrowScale = value);
+}
+export function SetActiveDash(dash: string): void {
+ !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash);
}
@scriptingGlobal
@@ -178,8 +224,38 @@ export class Doc extends RefField {
@observable public static GuestDashboard: Doc | undefined = undefined;
@observable public static GuestTarget: Doc | undefined = undefined;
@observable public static GuestMobile: Doc | undefined = undefined;
- public static CurrentUserEmail: string = '';
-
+ @observable.shallow public static CurrentlyLoading: Doc[] = observable([]);
+ // DocServer api
+ public static FindDocByTitle(title: string) {
+ const foundDocId =
+ title &&
+ Array.from(Object.keys(DocServer.Cache()))
+ .filter(key => DocServer.Cache()[key] instanceof Doc)
+ .find(key => (DocServer.Cache()[key] as Doc).title === title);
+
+ return foundDocId ? (DocServer.Cache()[foundDocId] as Doc) : undefined;
+ }
+ // removes from currently loading doc set
+ public static removeCurrentlyLoading(doc: Doc) {
+ if (Doc.CurrentlyLoading) {
+ const index = Doc.CurrentlyLoading.indexOf(doc);
+ runInAction(() => index !== -1 && Doc.CurrentlyLoading.splice(index, 1));
+ }
+ }
+ // adds doc to currently loading display
+ public static addCurrentlyLoading(doc: Doc) {
+ if (Doc.CurrentlyLoading.indexOf(doc) === -1) {
+ runInAction(() => Doc.CurrentlyLoading.push(doc));
+ }
+ }
+ // LinkManager api
+ public static AddLink: (link: Doc, checkExists?: boolean) => void;
+ public static DeleteLink: (link: Doc) => void;
+ public static Links: (link: Doc | undefined) => Doc[];
+ public static getOppositeAnchor: (linkDoc: Doc, anchor: Doc) => Doc | undefined;
+ // KeyValue SetField
+ public static SetField: (doc: Doc, key: string, value: string, forceOnDelegate?: boolean, setResult?: (value: FieldResult) => void) => boolean;
+ // UserDoc "API"
public static get MySharedDocs() { return DocCast(Doc.UserDoc().mySharedDocs); } // prettier-ignore
public static get MyUserDocView() { return DocCast(Doc.UserDoc().myUserDocView); } // prettier-ignore
public static get MyDockedBtns() { return DocCast(Doc.UserDoc().myDockedBtns); } // prettier-ignore
@@ -217,8 +293,8 @@ 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) { Doc.ActiveDashboard?.myOverlayDocs ? Doc.AddDocToList(Doc.ActiveDashboard, 'myOverlayDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
- public static RemFromMyOverlay(doc: Doc) { 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?.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 AddToMyPublished(doc: Doc) {
doc[DocData].title_custom = true;
doc[DocData].layout_showTitle = 'title';
@@ -274,9 +350,9 @@ export class Doc extends RefField {
return Reflect.getOwnPropertyDescriptor(target, prop);
}
return {
- configurable: true, //TODO Should configurable be true?
+ configurable: true, // TODO Should configurable be true?
enumerable: true,
- value: 0, //() => target.__fieldTuples[prop])
+ value: 0, // () => target.__fieldTuples[prop])
};
},
deleteProperty: deleteProperty,
@@ -288,7 +364,8 @@ export class Doc extends RefField {
if (!id || forceSave) {
DocServer.CreateField(docProxy);
}
- return 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;
@@ -300,14 +377,16 @@ export class Doc extends RefField {
private set __fieldTuples(value) {
// called by deserializer to set all fields in one shot
this[FieldTuples] = value;
- for (const key in value) {
- const field = value[key];
- field !== undefined && (this[FieldKeys][key] = true);
- if (field instanceof ObjectField) {
- field[Parent] = this[Self];
- field[FieldChanged] = containedFieldChangedHandler(this[SelfProxy], key, field);
+ Object.keys(value).forEach(key => {
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
+ const field = value[key];
+ field !== undefined && (this[FieldKeys][key] = true);
+ if (field instanceof ObjectField) {
+ field[Parent] = this[Self];
+ field[FieldChanged] = containedFieldChangedHandler(this[SelfProxy], key, field);
+ }
}
- }
+ });
}
@observable private [FieldTuples]: any = {};
@@ -320,7 +399,7 @@ export class Doc extends RefField {
@observable public [Animation]: Opt<Doc> = undefined;
@observable public [Highlight]: boolean = false;
@observable public [Brushed]: boolean = false;
- @observable public [DocViews] = new ObservableSet<DocumentView>();
+ @observable public [DocViews] = new ObservableSet<any /* DocumentView */>();
private [Self] = this;
private [SelfProxy]: any;
@@ -329,7 +408,7 @@ export class Doc extends RefField {
private [CachedUpdates]: { [key: string]: () => void | Promise<any> } = {};
public [Initializing]: boolean = false;
- public [FieldChanged] = (diff: undefined | { op: '$addToSet' | '$remFromSet' | '$set'; items: Field[] | undefined; length: number | undefined; hint?: any }, serverOp: any) => {
+ public [FieldChanged] = (diff: undefined | { op: '$addToSet' | '$remFromSet' | '$set'; items: FieldType[] | undefined; length: number | undefined; hint?: any }, serverOp: any) => {
if (!this[UpdatingFromServer] || this[ForceServerWrite]) {
DocServer.UpdateField(this[Id], serverOp);
}
@@ -352,7 +431,7 @@ export class Doc extends RefField {
let renderFieldKey: any;
const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layout_fieldKey, 'layout')];
if (typeof layoutField === 'string') {
- renderFieldKey = layoutField.split("fieldKey={'")[1].split("'")[0]; //layoutField.split("'")[1];
+ [renderFieldKey] = layoutField.split("fieldKey={'")[1].split("'"); // layoutField.split("'")[1];
} else {
return Cast(layoutField, Doc, null);
}
@@ -363,13 +442,11 @@ export class Doc extends RefField {
public async [HandleUpdate](diff: any) {
const set = diff.$set;
- const sameAuthor = this.author === Doc.CurrentUserEmail;
- if (set) {
- for (const key in set) {
- const fprefix = 'fields.';
- if (!key.startsWith(fprefix)) {
- continue;
- }
+ const sameAuthor = this.author === ClientUtils.CurrentUserEmail();
+ const fprefix = 'fields.';
+ 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]);
@@ -377,7 +454,7 @@ export class Doc extends RefField {
this[UpdatingFromServer] = true;
this[fKey] = value;
this[UpdatingFromServer] = false;
- if (fKey.startsWith('acl')) {
+ if (fKey.startsWith('acl_')) {
updateCachedAcls(this);
}
if (prev === AclPrivate && GetEffectiveAcl(this) !== AclPrivate) {
@@ -385,20 +462,18 @@ export class Doc extends RefField {
}
};
const writeMode = DocServer.getFieldWriteMode(fKey);
- if (fKey.startsWith('acl') || writeMode !== DocServer.WriteMode.Playground) {
+ 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;
- if (unset) {
- for (const key in unset) {
- if (!key.startsWith('fields.')) {
- continue;
- }
+ Object.keys(unset ?? {})
+ .filter(key => key.startsWith(fprefix))
+ .forEach(async key => {
const fKey = key.substring(7);
const fn = () => {
this[UpdatingFromServer] = true;
@@ -411,12 +486,22 @@ export class Doc extends RefField {
} else {
this[CachedUpdates][fKey] = fn;
}
- }
- }
+ });
}
}
+// 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;
+ }
export function SetContainer(doc: Doc, container: Doc) {
if (container !== Doc.MyRecentlyClosed) {
doc.embedContainer = container;
@@ -454,7 +539,7 @@ export namespace Doc {
return doc;
}
}
- export function GetT<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): FieldResult<T> {
+ export function GetT<T extends FieldType>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): FieldResult<T> {
return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult<T>;
}
export function isTemplateDoc(doc: Doc) {
@@ -482,8 +567,8 @@ export namespace Doc {
// 2) if the data doc has the field, then it's written there.
// 3) if neither already has the field, then 'defaultProto' determines whether to write it to the data doc (or the embedding)
//
- export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) {
- if (key.startsWith('_')) key = key.substring(1);
+ export async function SetInPlace(doc: Doc, keyIn: string, value: FieldType | undefined, defaultProto: boolean) {
+ const key = keyIn.startsWith('_') ? keyIn.substring(1) : keyIn;
const hasProto = doc[DocData] !== doc ? doc[DocData] : undefined;
const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
const onProto = hasProto && Object.getOwnPropertyNames(hasProto).indexOf(key) !== -1;
@@ -500,7 +585,6 @@ export namespace Doc {
}
return protos;
}
-
/**
* This function is intended to model Object.assign({}, {}) [https://mzl.la/1Mo3l21], which copies
* the values of the properties of a source object into the target.
@@ -512,17 +596,15 @@ 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>>>, skipUndefineds: boolean = false, isInitializing = false) {
+ export function assign<K extends string>(doc: Doc, fields: Partial<Record<K, Opt<FieldType>>>, skipUndefineds: boolean = false, isInitializing = false) {
isInitializing && (doc[Initializing] = true);
- for (const key in fields) {
- if (fields.hasOwnProperty(key)) {
- const value = fields[key];
- if (!skipUndefineds || value !== undefined) {
- // Do we want to filter out undefineds?
- doc[key] = value;
- }
+ Object.keys(fields).forEach(key => {
+ const value = (fields as any)[key];
+ if (!skipUndefineds || value !== undefined) {
+ // Do we want to filter out undefineds?
+ doc[key] = value;
}
- }
+ });
isInitializing && (doc[Initializing] = false);
return doc;
}
@@ -569,7 +651,7 @@ export namespace Doc {
* @returns true if successful, false otherwise.
*/
export function RemoveDocFromList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, ignoreProto = false) {
- const key = fieldKey ? fieldKey : Doc.LayoutFieldKey(listDoc);
+ const key = fieldKey || Doc.LayoutFieldKey(listDoc);
const list = Doc.Get(listDoc, key, ignoreProto) === undefined ? (listDoc[DocData][key] = new List<Doc>()) : Cast(listDoc[key], listSpec(Doc));
if (list) {
const ind = list.indexOf(doc);
@@ -586,7 +668,7 @@ export namespace Doc {
* @returns true if successful, false otherwise.
*/
export function AddDocToList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean, ignoreProto?: boolean) {
- const key = fieldKey ? fieldKey : Doc.LayoutFieldKey(listDoc);
+ const key = fieldKey || Doc.LayoutFieldKey(listDoc);
const list = Doc.Get(listDoc, key, ignoreProto) === undefined ? (listDoc[DocData][key] = new List<Doc>()) : Cast(listDoc[key], listSpec(Doc));
if (list) {
if (!allowDuplicates) {
@@ -603,6 +685,7 @@ export namespace Doc {
if (reversed) list.splice(0, 0, doc);
else list.push(doc);
} else {
+ // eslint-disable-next-line no-lonely-if
if (reversed) list.splice(before ? list.length - ind + 1 : list.length - ind, 0, doc);
else list.splice(before ? ind : ind + 1, 0, doc);
}
@@ -634,7 +717,7 @@ export namespace Doc {
embedding.createdFrom = doc;
embedding.proto_embeddingId = doc[DocData].proto_embeddingId = Doc.GetEmbeddings(doc).length - 1;
!Doc.GetT(embedding, 'title', 'string', true) && (embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`));
- embedding.author = Doc.CurrentUserEmail;
+ embedding.author = ClientUtils.CurrentUserEmail();
return embedding;
}
@@ -642,7 +725,7 @@ export namespace Doc {
export function BestEmbedding(doc: Doc) {
const dataDoc = doc[DocData];
const availableEmbeddings = Doc.GetEmbeddings(dataDoc);
- const bestEmbedding = [...(dataDoc !== doc ? [doc] : []), ...availableEmbeddings].find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail);
+ const bestEmbedding = [...(dataDoc !== doc ? [doc] : []), ...availableEmbeddings].find(d => !d.embedContainer && d.author === ClientUtils.CurrentUserEmail());
bestEmbedding && Doc.AddEmbedding(doc, doc);
return bestEmbedding ?? Doc.MakeEmbedding(doc);
}
@@ -670,37 +753,39 @@ export namespace Doc {
await Promise.all(
Object.keys(doc).map(async key => {
if (filter.includes(key)) return;
- const assignKey = (val: any) => (copy[key] = val);
+ const assignKey = (val: any) => {
+ copy[key] = val;
+ };
const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
const field = ProxyField.WithoutProxy(() => doc[key]);
- const copyObjectField = async (field: ObjectField) => {
+ const copyObjectField = async (objField: ObjectField) => {
const list = await Cast(doc[key], listSpec(Doc));
const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc);
if (docs !== undefined && docs.length) {
const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates)));
assignKey(new List<Doc>(clones));
} else {
- assignKey(ObjectField.MakeCopy(field));
- if (field instanceof RichTextField) {
- if (DocsInTextFieldIds.some(id => field.Data.includes(`"${id}":`))) {
+ assignKey(ObjectField.MakeCopy(objField));
+ if (objField instanceof RichTextField) {
+ if (DocsInTextFieldIds.some(id => objField.Data.includes(`"${id}":`))) {
const docidsearch = new RegExp('(' + DocsInTextFieldIds.map(exp => '(' + exp + ')').join('|') + ')":"([a-z-A-Z0-9_]*)"', 'g');
- const rawdocids = field.Data.match(docidsearch);
+ const rawdocids = objField.Data.match(docidsearch);
const docids = rawdocids?.map((str: string) =>
DocsInTextFieldIds.reduce((output, exp) => output.replace(new RegExp(`${exp}":`, 'g'), ''), str)
.replace(/"/g, '')
.trim()
);
const results = docids && (await DocServer.GetRefFields(docids));
- const docs = results && Array.from(Object.keys(results)).map(key => DocCast(results[key]));
- docs?.map(doc => doc && Doc.makeClone(doc, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
- rtfs.push({ copy, key, field });
+ const rdocs = results && Array.from(Object.keys(results)).map(rkey => DocCast(results[rkey]));
+ rdocs?.map(d => d && Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
+ rtfs.push({ copy, key, field: objField });
}
}
}
};
const docAtKey = doc[key];
if (key === 'author') {
- assignKey(Doc.CurrentUserEmail);
+ assignKey(ClientUtils.CurrentUserEmail());
} else if (docAtKey instanceof Doc) {
if (pruneDocs.includes(docAtKey)) {
// prune doc and do nothing
@@ -709,7 +794,7 @@ export namespace Doc {
(key.includes('layout[') ||
key.startsWith('layout') || //
['embedContainer', 'annotationOn', 'proto'].includes(key) ||
- (['link_anchor_1', 'link_anchor_2'].includes(key) && doc.author === Doc.CurrentUserEmail))
+ (['link_anchor_1', 'link_anchor_2'].includes(key) && doc.author === ClientUtils.CurrentUserEmail()))
) {
assignKey(await Doc.makeClone(docAtKey, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
} else {
@@ -722,7 +807,8 @@ export namespace Doc {
} else if (field instanceof ObjectField) {
await copyObjectField(field);
} else if (field instanceof Promise) {
- debugger; //This shouldn't happen...
+ // eslint-disable-next-line no-debugger
+ debugger; // This shouldn't happen...
} else {
assignKey(field);
}
@@ -749,7 +835,7 @@ export namespace Doc {
visited.add(clone);
Object.keys(clone)
.filter(key => key !== 'cloneOf')
- .map(key => {
+ .forEach(key => {
const docAtKey = DocCast(clone[key]);
if (docAtKey && !Doc.IsSystem(docAtKey)) {
if (!Array.from(cloneMap.values()).includes(docAtKey)) {
@@ -768,16 +854,16 @@ export namespace Doc {
export async function MakeClone(doc: Doc, cloneLinks = true, cloneTemplates = true, cloneMap: Map<string, Doc> = new Map()) {
const linkMap = new Map<string, Doc>();
const rtfMap: { copy: Doc; key: string; field: RichTextField }[] = [];
- const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.embedContainer ? [DocCast(doc.embedContainer)] : [], cloneLinks, cloneTemplates);
+ const clone = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.embedContainer ? [DocCast(doc.embedContainer)] : [], cloneLinks, cloneTemplates);
const repaired = new Set<Doc>();
const linkedDocs = Array.from(linkMap.values());
- linkedDocs.map((link: Doc) => LinkManager.Instance.addLink(link, true));
- rtfMap.map(({ copy, key, field }) => {
- const replacer = (match: any, attr: string, id: string, offset: any, string: any) => {
+ 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 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: any, href: string, id: string /* , offset: any, string: any */) => {
const mapped = cloneMap.get(id);
return href + (mapped ? mapped[Id] : id);
};
@@ -786,86 +872,8 @@ export namespace Doc {
copy[key] = new RichTextField(field.Data.replace(docidsearch, replacer).replace(re, replacer2), field.Text);
});
const clonedDocs = [...Array.from(cloneMap.values()), ...linkedDocs];
- clonedDocs.map(clone => Doc.repairClone(clone, cloneMap, cloneTemplates, repaired));
- return { clone: copy, map: cloneMap, linkMap };
- }
-
- export async function Zip(doc: Doc, zipFilename = 'dashExport.zip') {
- const { clone, map, linkMap } = await Doc.MakeClone(doc);
- const proms = new Set<string>();
- function replacer(key: any, value: any) {
- if (key && ['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined;
- if (value?.__type === 'image') {
- const extension = value.url.replace(/.*\./, '');
- proms.add(value.url.replace('.' + extension, '_o.' + extension));
- return SerializationHelper.Serialize(new ImageField(value.url));
- }
- if (value?.__type === 'pdf') {
- proms.add(value.url);
- return SerializationHelper.Serialize(new PdfField(value.url));
- }
- if (value?.__type === 'audio') {
- proms.add(value.url);
- return SerializationHelper.Serialize(new AudioField(value.url));
- }
- if (value?.__type === 'video') {
- proms.add(value.url);
- return SerializationHelper.Serialize(new VideoField(value.url));
- }
- if (
- value instanceof Doc ||
- value instanceof ScriptField ||
- value instanceof RichTextField ||
- value instanceof InkField ||
- value instanceof CsvField ||
- value instanceof WebField ||
- value instanceof DateField ||
- value instanceof ProxyField ||
- value instanceof ComputedField
- ) {
- return SerializationHelper.Serialize(value);
- }
- if (value instanceof Array && key !== ListFieldName && key !== InkField.InkDataFieldName) return { fields: value, __type: 'list' };
- return value;
- }
-
- const docs: { [id: string]: any } = {};
- const links: { [id: string]: any } = {};
- Array.from(map.entries()).forEach(f => (docs[f[0]] = f[1]));
- Array.from(linkMap.entries()).forEach(l => (links[l[0]] = l[1]));
- const jsonDocs = JSON.stringify({ id: clone[Id], docs, links }, decycle(replacer));
-
- const zip = new JSZip();
- var count = 0;
- const promArr = Array.from(proms)
- .filter(url => url?.startsWith('/files'))
- .map(url => url.replace('/', '')); // window.location.origin));
- console.log(promArr.length);
- if (!promArr.length) {
- zip.file('docs.json', jsonDocs);
- zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename));
- } else
- promArr.forEach((url, i) => {
- // loading a file and add it in a zip file
- JSZipUtils.getBinaryContent(window.location.origin + '/' + url, (err: any, data: any) => {
- if (err) throw err; // or handle the error
- // // Generate a directory within the Zip file structure
- // const assets = zip.folder("assets");
- // assets.file(filename, data, {binary: true});
- const assetPathOnServer = promArr[i].replace(window.location.origin + '/', '').replace(/\//g, '%%%');
- zip.file(assetPathOnServer, data, { binary: true });
- console.log(' => ' + url);
- if (++count === promArr.length) {
- zip.file('docs.json', jsonDocs);
- zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename));
- // const a = document.createElement("a");
- // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
- // a.href = url;
- // a.download = `DocExport-${this.props.Document[Id]}.zip`;
- // a.click();
- }
- });
- });
+ clonedDocs.forEach(cloneDoc => Doc.repairClone(cloneDoc, cloneMap, cloneTemplates, repaired));
+ return { clone, map: cloneMap, linkMap };
}
const _pendingMap = new Set<string>();
@@ -895,6 +903,7 @@ export namespace Doc {
if (templateLayoutDoc.resolvedDataDoc === targetDoc[DocData]) {
expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params
} else {
+ // eslint-disable-next-line no-param-reassign
templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = DocCast(templateLayoutDoc.proto, templateLayoutDoc)); // if the template has already been applied (ie, a nested template), then use the template's prototype
if (!targetDoc[expandedLayoutFieldKey]) {
_pendingMap.add(targetDoc[Id] + expandedLayoutFieldKey);
@@ -905,7 +914,7 @@ export namespace Doc {
newLayoutDoc.rootDocument = targetDoc;
newLayoutDoc.embedContainer = targetDoc;
newLayoutDoc.resolvedDataDoc = dataDoc;
- newLayoutDoc['acl-Guest'] = SharingPermissions.Edit;
+ newLayoutDoc.acl_Guest = SharingPermissions.Edit;
if (dataDoc[templateField] === undefined && (templateLayoutDoc[templateField] as any)?.length) {
dataDoc[templateField] = ObjectField.MakeCopy(templateLayoutDoc[templateField] as List<Doc>);
// ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"])`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc });
@@ -954,7 +963,7 @@ export namespace Doc {
}
} else {
const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
- const field = key === 'author' ? Doc.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]);
+ const field = key === 'author' ? ClientUtils.CurrentUserEmail() : ProxyField.WithoutProxy(() => doc[key]);
if (field instanceof RefField) {
if (field instanceof Doc) {
if (key === 'myLinkDatabase') {
@@ -965,6 +974,7 @@ export namespace Doc {
}
}
} else if (cfield instanceof ComputedField) {
+ /* empty */
} else if (field instanceof ObjectField) {
if (field instanceof Doc) {
Doc.FindReferences(field, references, system);
@@ -981,7 +991,8 @@ export namespace Doc {
Doc.FindReferences(field.value, references, system);
}
} else if (field instanceof Promise) {
- debugger; //This shouldn't happend...
+ // eslint-disable-next-line no-debugger
+ debugger; // This shouldn't happend...
}
}
});
@@ -1000,7 +1011,7 @@ export namespace Doc {
}
} else {
const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
- const field = key === 'author' ? Doc.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]);
+ const field = key === 'author' ? ClientUtils.CurrentUserEmail() : ProxyField.WithoutProxy(() => doc[key]);
if (field instanceof RefField) {
copy[key] = field;
} else if (cfield instanceof ComputedField) {
@@ -1011,7 +1022,8 @@ export namespace Doc {
? new ProxyField(Doc.MakeCopy(doc[key] as any)) // copy the expanded render template
: ObjectField.MakeCopy(field);
} else if (field instanceof Promise) {
- debugger; //This shouldn't happend...
+ // eslint-disable-next-line no-debugger
+ debugger; // This shouldn't happend...
} else {
copy[key] = field;
}
@@ -1038,10 +1050,12 @@ export namespace Doc {
delegate[Initializing] = true;
updateCachedAcls(delegate);
delegate.proto = doc;
- delegate.author = Doc.CurrentUserEmail;
+ delegate.author = ClientUtils.CurrentUserEmail();
Object.keys(doc)
- .filter(key => key.startsWith('acl'))
- .forEach(key => (delegate[key] = doc[key]));
+ .filter(key => key.startsWith('acl_'))
+ .forEach(key => {
+ delegate[key] = doc[key];
+ });
title && (delegate.title = title);
delegate[Initializing] = false;
if (!Doc.IsSystem(doc)) Doc.AddEmbedding(doc, delegate);
@@ -1054,7 +1068,7 @@ export namespace Doc {
// (ie, the 'data' doc), and then creates another delegate of that (ie, the 'layout' doc).
// This is appropriate if you're trying to create a document that behaves like all
// regularly created documents (e.g, text docs, pdfs, etc which all have data/layout docs)
- export function MakeDelegateWithProto(doc: Doc, id?: string, title?: string) {
+ export function MakeDelegateWithProto(doc: Doc /* , id?: string, title?: string */) {
const ndoc = Doc.ApplyTemplate(doc);
if (ndoc) {
Doc.GetProto(ndoc).isDataDoc = true;
@@ -1067,7 +1081,7 @@ export namespace Doc {
export function ApplyTemplate(templateDoc: Doc) {
if (templateDoc) {
const proto = new Doc();
- proto.author = Doc.CurrentUserEmail;
+ proto.author = ClientUtils.CurrentUserEmail();
const target = Doc.MakeDelegate(proto);
const targetKey = StrCast(templateDoc.layout_fieldKey, 'layout');
const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + '(...' + _applyCount++ + ')');
@@ -1103,7 +1117,6 @@ export namespace Doc {
!keepFieldKey && (templateField.title = metadataFieldKey);
const templateFieldValue = templateField[metadataFieldKey] || templateField[Doc.LayoutFieldKey(templateField)];
- const templateCaptionValue = templateField.caption;
// move any data that the template field had been rendering over to the template doc so that things will still be rendered
// when the template field is adjusted to point to the new metadatafield key.
// note 1: if the template field contained a list of documents, each of those documents will be converted to templates as well.
@@ -1123,7 +1136,7 @@ export namespace Doc {
// converts a document id to a url path on the server
export function globalServerPath(doc: Doc | string = ''): string {
- return Utils.prepend('/doc/' + (doc instanceof Doc ? doc[Id] : doc));
+ return ClientUtils.prepend('/doc/' + (doc instanceof Doc ? doc[Id] : doc));
}
// converts a document id to a url path on the server
export function localServerPath(doc?: Doc): string {
@@ -1189,7 +1202,9 @@ export namespace Doc {
return manager._searchQuery;
}
export function SetSearchQuery(query: string) {
- runInAction(() => (manager._searchQuery = query));
+ runInAction(() => {
+ manager._searchQuery = query;
+ });
}
export function UserDoc(): Doc {
return manager._user_doc;
@@ -1201,12 +1216,13 @@ export namespace Doc {
return Cast(Doc.UserDoc().myLinkDatabase, Doc, null);
}
export function SetUserDoc(doc: Doc) {
+ // eslint-disable-next-line no-return-assign
return (manager._user_doc = doc);
}
- const isSearchMatchCache = computedFn(function IsSearchMatch(doc: Doc) {
- return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) : brushManager.SearchMatchDoc.has(doc[DocData]) ? brushManager.SearchMatchDoc.get(doc[DocData]) : undefined;
- });
+ const isSearchMatchCache = computedFn((doc: Doc) =>
+ (brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) :
+ brushManager.SearchMatchDoc.has(doc[DocData]) ? brushManager.SearchMatchDoc.get(doc[DocData]) : undefined)); // prettier-ignore
export function IsSearchMatch(doc: Doc) {
return isSearchMatchCache(doc);
}
@@ -1252,11 +1268,16 @@ export namespace Doc {
return BrushDoc(doc, true);
}
export function UnBrushAllDocs() {
- Array.from(brushManager.BrushedDoc).forEach(action(doc => (doc[Brushed] = false)));
+ Array.from(brushManager.BrushedDoc).forEach(
+ action(doc => {
+ doc[Brushed] = false;
+ })
+ );
}
- let UnhighlightWatchers: (() => void)[] = [];
- export let UnhighlightTimer: any;
+ const UnhighlightWatchers: (() => void)[] = [];
+ let UnhighlightTimer: any;
+ export function IsUnhighlightTimerSet() { return UnhighlightTimer; } // prettier-ignore
export function AddUnHighlightWatcher(watcher: () => void) {
if (UnhighlightTimer) {
UnhighlightWatchers.push(watcher);
@@ -1270,9 +1291,9 @@ export namespace Doc {
highlightedDocs.forEach(doc => Doc.UnHighlightDoc(doc));
document.removeEventListener('pointerdown', linkFollowUnhighlight);
}
- export function linkFollowHighlight(destDoc: Doc | Doc[], dataAndDisplayDocs = true, presentation_effect?: Doc) {
+ export function linkFollowHighlight(destDoc: Doc | Doc[], dataAndDisplayDocs = true, presentationEffect?: Doc) {
linkFollowUnhighlight();
- (destDoc instanceof Doc ? [destDoc] : destDoc).forEach(doc => Doc.HighlightDoc(doc, dataAndDisplayDocs, presentation_effect));
+ toList(destDoc).forEach(doc => Doc.HighlightDoc(doc, dataAndDisplayDocs, presentationEffect));
document.removeEventListener('pointerdown', linkFollowUnhighlight);
document.addEventListener('pointerdown', linkFollowUnhighlight);
if (UnhighlightTimer) clearTimeout(UnhighlightTimer);
@@ -1282,31 +1303,34 @@ export namespace Doc {
}, 5000);
}
- export var highlightedDocs = new ObservableSet<Doc>();
+ export const highlightedDocs = new ObservableSet<Doc>();
export function IsHighlighted(doc: Doc) {
if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(doc[DocData]) === AclPrivate || doc.opacity === 0) return false;
return doc[Highlight] || doc[DocData][Highlight];
}
- export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true, presentation_effect?: Doc) {
+ export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true, presentationEffect?: Doc) {
runInAction(() => {
highlightedDocs.add(doc);
doc[Highlight] = true;
- doc[Animation] = presentation_effect;
+ doc[Animation] = presentationEffect;
if (dataAndDisplayDocs && !doc.resolvedDataDoc) {
// if doc is a layout template then we don't want to highlight the proto since that will be the entire template, not just the specific layout field
highlightedDocs.add(doc[DocData]);
doc[DocData][Highlight] = true;
+ // want to highlight the targets of presentation docs explicitly since following a pres target does not highlight PDf <Annotations> which are not DocumentViews
+ if (doc.presentation_targetDoc) DocCast(doc.presentation_targetDoc)[Highlight] = true;
}
});
}
/// if doc is defined, then it is unhighlighted, otherwise all highlighted docs are unhighlighted
- export function UnHighlightDoc(doc?: Doc) {
+ export function UnHighlightDoc(docs?: Doc) {
runInAction(() => {
- (doc ? [doc] : Array.from(highlightedDocs)).forEach(doc => {
+ (docs ? [docs] : Array.from(highlightedDocs)).forEach(doc => {
highlightedDocs.delete(doc);
highlightedDocs.delete(doc[DocData]);
doc[Highlight] = doc[DocData][Highlight] = false;
doc[Animation] = undefined;
+ if (doc.presentation_targetDoc) DocCast(doc.presentation_targetDoc)[Highlight] = false;
});
});
}
@@ -1380,6 +1404,7 @@ export namespace Doc {
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()) {
+ // eslint-disable-next-line no-param-reassign
if (toggle) modifiers = 'remove';
else return;
}
@@ -1404,9 +1429,12 @@ export namespace Doc {
return [Number(childFiltersByRanges[i + 1]), Number(childFiltersByRanges[i + 2])];
}
}
+ return undefined;
}
export function assignDocToField(doc: Doc, field: string, id: string) {
- DocServer.GetRefField(id).then(layout => layout instanceof Doc && (doc[field] = layout));
+ DocServer.GetRefField(id).then(layout => {
+ layout instanceof Doc && (doc[field] = layout);
+ });
return id;
}
@@ -1426,20 +1454,6 @@ export namespace Doc {
});
}
- export function styleFromLayoutString(doc: Doc, props: FieldViewProps, scale: number) {
- const style: { [key: string]: any } = {};
- const divKeys = ['width', 'height', 'fontSize', 'transform', 'left', 'backgroundColor', 'left', 'right', 'top', 'bottom', 'pointerEvents', 'position'];
- const replacer = (match: any, expr: string, offset: any, string: any) => {
- // bcz: this executes a script to convert a property expression string: { script } into a value
- return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ this: doc, self: doc, scale }).result?.toString() ?? '';
- };
- divKeys.map((prop: string) => {
- const p = (props as any)[prop];
- typeof p === 'string' && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer));
- });
- return style;
- }
-
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))
@@ -1495,6 +1509,7 @@ export namespace Doc {
case DocumentType.EQUATION: return 'calculator';
case DocumentType.SIMULATION: return 'rocket';
case DocumentType.CONFIG: return 'folder-closed';
+ default:
}
return 'question';
}
@@ -1505,7 +1520,7 @@ export namespace Doc {
// If they are not remapped, loading the file will overwrite any existing documents with those ids
//
export async function importDocument(file: File, remap = false) {
- const upload = Utils.prepend('/uploadDoc');
+ const upload = ClientUtils.prepend('/uploadDoc');
const formData = new FormData();
if (file) {
formData.append('file', file);
@@ -1513,12 +1528,13 @@ export namespace Doc {
const response = await fetch(upload, { method: 'POST', body: formData });
const json = await response.json();
if (json !== 'error') {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
const docs = await DocServer.GetRefFields(json.docids as string[]);
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])
- .forEach(link => link instanceof Doc && LinkManager.Instance.addLink(link));
+ .forEach(link => link instanceof Doc && Doc.AddLink?.(link));
return doc;
}
}
@@ -1578,6 +1594,7 @@ export namespace Doc {
*/
export function FromJson({ data, title, appendToExisting, excludeEmptyObjects }: JsonConversionOpts): Opt<Doc> {
if (excludeEmptyObjects === undefined) {
+ // eslint-disable-next-line no-param-reassign
excludeEmptyObjects = true;
}
if (data === undefined || data === null || ![...primitives, 'object'].includes(typeof data)) {
@@ -1617,12 +1634,12 @@ export namespace Doc {
if (hasEntries || !excludeEmptyObjects) {
const resolved = target ?? new Doc();
if (hasEntries) {
- let result: Opt<Field>;
- Object.keys(object).map(key => {
+ Object.keys(object).forEach(key => {
// if excludeEmptyObjects is true, any qualifying conversions from toField will
// be undefined, and thus the results that would have
// otherwise been empty (List or Doc)s will just not be written
- if ((result = toField(object[key], excludeEmptyObjects, key))) {
+ const result = toField(object[key], excludeEmptyObjects, key);
+ if (result) {
resolved[key] = result;
}
});
@@ -1630,6 +1647,7 @@ export namespace Doc {
title && (resolved.title = title);
return resolved;
}
+ return undefined;
};
/**
@@ -1639,19 +1657,22 @@ 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<Field>> => {
+ const convertList = (list: Array<any>, excludeEmptyObjects: boolean): Opt<List<FieldType>> => {
const target = new List();
- let result: Opt<Field>;
+ let result: Opt<FieldType>;
// if excludeEmptyObjects is true, any qualifying conversions from toField will
// be undefined, and thus the results that would have
// otherwise been empty (List or Doc)s will just not be written
- list.map(item => (result = toField(item, excludeEmptyObjects)) && target.push(result));
+ list.forEach(item => {
+ (result = toField(item, excludeEmptyObjects)) && target.push(result);
+ });
if (target.length || !excludeEmptyObjects) {
return target;
}
+ return undefined;
};
- const toField = (data: any, excludeEmptyObjects: boolean, title?: string): Opt<Field> => {
+ const toField = (data: any, excludeEmptyObjects: boolean, title?: string): Opt<FieldType> => {
if (data === null || data === undefined) {
return undefined;
}
@@ -1666,61 +1687,102 @@ export namespace 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;
+ }
+ return '';
+}
+export function GetDocFromUrl(url: string) {
+ return url.startsWith(document.location.origin) ? new URL(url).pathname.split('doc/').lastElement() : ''; // docId
+}
+
+let activeAudioLinker: (f: () => Doc | undefined, broadcast?: boolean) => (Doc | undefined)[];
+export function SetActiveAudioLinker(func: (f: () => Doc | undefined, broadcast?: boolean) => (Doc | undefined)[]) {
+ activeAudioLinker = func;
+}
+export function CreateLinkToActiveAudio(func: () => Doc | undefined, broadcast?: boolean) {
+ return activeAudioLinker(func, broadcast);
+}
+
export function IdToDoc(id: string) {
return DocCast(DocServer.GetCachedRefField(id));
}
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function idToDoc(id: string): any {
return IdToDoc(id);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function renameEmbedding(doc: any) {
return StrCast(doc[DocData].title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`;
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function getProto(doc: any) {
return Doc.GetProto(doc);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function getDocTemplate(doc?: any) {
return Doc.getDocTemplate(doc);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function getEmbedding(doc: any) {
return Doc.MakeEmbedding(doc);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) {
return doc.isTemplateDoc ? Doc.MakeDelegateWithProto(doc) : Doc.MakeCopy(doc, copyProto);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function copyField(field: any) {
return Field.Copy(field);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function docList(field: any) {
return DocListCast(field);
});
+// eslint-disable-next-line prefer-arrow-callback
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) {
return Doc.SetInPlace(doc, field, value, false);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) {
return Doc.AreProtosEqual(doc1, doc2);
});
+// eslint-disable-next-line prefer-arrow-callback
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 {
return Cast(doc, Doc);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function activePresentationItem() {
const curPres = Doc.ActivePresentation;
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') {
Doc.setDocFilter(container, key, value, modifiers);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function setDocRangeFilter(container: Doc, key: string, range: number[]) {
Doc.setDocRangeFilter(container, key, range);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function toJavascriptString(str: string) {
- return Field.toJavascriptString(str as Field);
+ return Field.toJavascriptString(str as FieldType);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function RtfField() {
return RichTextField.RTFfield();
});
diff --git a/src/fields/HtmlField.ts b/src/fields/HtmlField.ts
index b67f0f7e9..536f5ce4c 100644
--- a/src/fields/HtmlField.ts
+++ b/src/fields/HtmlField.ts
@@ -1,7 +1,7 @@
+import { primitive, serializable } from 'serializr';
import { Deserializable } from '../client/util/SerializationHelper';
-import { serializable, primitive } from 'serializr';
-import { ObjectField } from './ObjectField';
import { Copy, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
+import { ObjectField } from './ObjectField';
@Deserializable('html')
export class HtmlField extends ObjectField {
diff --git a/src/fields/IconField.ts b/src/fields/IconField.ts
index 4d2badb68..33e5be7af 100644
--- a/src/fields/IconField.ts
+++ b/src/fields/IconField.ts
@@ -1,5 +1,5 @@
-import { Deserializable } from '../client/util/SerializationHelper';
import { serializable, primitive } from 'serializr';
+import { Deserializable } from '../client/util/SerializationHelper';
import { ObjectField } from './ObjectField';
import { Copy, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index b3e01229a..c4f5f7a24 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -2,6 +2,7 @@ import { Bezier } from 'bezier-js';
import { alias, createSimpleSchema, list, object, serializable } from 'serializr';
import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
import { Deserializable } from '../client/util/SerializationHelper';
+import { PointData } from '../pen-gestures/GestureTypes';
import { Copy, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
import { ObjectField } from './ObjectField';
@@ -16,12 +17,6 @@ export enum InkTool {
PresentationPin = 'presentationpin',
}
-// Defines a point in an ink as a pair of x- and y-coordinates.
-export interface PointData {
- X: number;
- Y: number;
-}
-
export type Segment = Array<Bezier>;
// Defines an ink as an array of points.
@@ -62,10 +57,10 @@ const strokeDataSchema = createSimpleSchema({
'*': true,
});
+export const InkDataFieldName = '__inkData';
@Deserializable('ink')
export class InkField extends ObjectField {
- public static InkDataFieldName = '__inkData';
- @serializable(alias(InkField.InkDataFieldName, list(object(strokeDataSchema))))
+ @serializable(alias(InkDataFieldName, list(object(strokeDataSchema))))
readonly inkData: InkData;
constructor(data: InkData) {
diff --git a/src/fields/List.ts b/src/fields/List.ts
index ec31f7dae..38c47d546 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -1,31 +1,28 @@
import { action, computed, makeObservable, observable } from 'mobx';
-import { alias, list, serializable } from 'serializr';
-import { DocServer } from '../client/DocServer';
+import { alias, list as serializrList, serializable } from 'serializr';
import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
import { Deserializable, afterDocDeserialize, autoObject } from '../client/util/SerializationHelper';
-import { Field } from './Doc';
+import { Field, FieldType, StrListCast } from './Doc';
import { FieldTuples, Self, SelfProxy } from './DocSymbols';
import { Copy, FieldChanged, Parent, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
-import { ObjectField } from './ObjectField';
+import { ObjGetRefFields, ObjectField } from './ObjectField';
import { ProxyField } from './Proxy';
import { RefField } from './RefField';
-import { listSpec } from './Schema';
-import { Cast } from './Types';
import { containedFieldChangedHandler, deleteProperty, getter, setter } from './util';
-function toObjectField(field: Field) {
+function toObjectField(field: FieldType) {
return field instanceof RefField ? new ProxyField(field) : field;
}
-function toRealField(field: Field) {
+function toRealField(field: FieldType) {
return field instanceof ProxyField ? field.value : field;
}
-type StoredType<T extends Field> = T extends RefField ? ProxyField<T> : T;
+type StoredType<T extends FieldType> = T extends RefField ? ProxyField<T> : T;
export const ListFieldName = 'fields';
@Deserializable('list')
-class ListImpl<T extends Field> extends ObjectField {
+class ListImpl<T extends FieldType> extends ObjectField {
static listHandlers: any = {
/// Mutator methods
copyWithin() {
@@ -44,14 +41,14 @@ class ListImpl<T extends Field> extends ObjectField {
this[SelfProxy][FieldChanged]?.();
return field;
},
- push: action(function (this: ListImpl<any>, ...items: any[]) {
- items = items.map(toObjectField);
+ push: action(function (this: ListImpl<any>, ...itemsIn: any[]) {
+ const items = itemsIn.map(toObjectField);
const list = this[Self];
- const length = list.__fieldTuples.length;
+ const { length } = list.__fieldTuples;
for (let i = 0; i < items.length; i++) {
const item = items[i];
- //TODO Error checking to make sure parent doesn't already exist
+ // TODO Error checking to make sure parent doesn't already exist
if (item instanceof ObjectField) {
item[Parent] = list;
item[FieldChanged] = containedFieldChangedHandler(this[SelfProxy], i + length, item);
@@ -77,21 +74,21 @@ class ListImpl<T extends Field> extends ObjectField {
this[SelfProxy][FieldChanged]?.();
return res;
},
- splice: action(function (this: any, start: number, deleteCount: number, ...items: any[]) {
+ splice: action(function (this: any, start: number, deleteCount: number, ...itemsIn: any[]) {
this[Self].__realFields; // coerce retrieving entire array
- items = items.map(toObjectField);
+ const items = itemsIn.map(toObjectField);
const list = this[Self];
const removed = list.__fieldTuples.filter((item: any, 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
- //TODO Need to change indices of other fields in array
+ // TODO Error checking to make sure parent doesn't already exist
+ // TODO Need to change indices of other fields in array
if (item instanceof ObjectField) {
item[Parent] = list;
item[FieldChanged] = containedFieldChangedHandler(this, i + start, item);
}
}
- let hintArray: { val: any; index: number }[] = [];
+ const hintArray: { val: any; index: number }[] = [];
for (let i = start; i < start + deleteCount; i++) {
hintArray.push({ val: list.__fieldTuples[i], index: i });
}
@@ -107,13 +104,13 @@ class ListImpl<T extends Field> extends ObjectField {
);
return res.map(toRealField);
}),
- unshift(...items: any[]) {
- items = items.map(toObjectField);
+ unshift(...itemsIn: any[]) {
+ const items = itemsIn.map(toObjectField);
const list = this[Self];
for (let i = 0; i < items.length; i++) {
const item = items[i];
- //TODO Error checking to make sure parent doesn't already exist
- //TODO Need to change indices of other fields in array
+ // TODO Error checking to make sure parent doesn't already exist
+ // TODO Need to change indices of other fields in array
if (item instanceof ObjectField) {
item[Parent] = list;
item[FieldChanged] = containedFieldChangedHandler(this, i, item);
@@ -131,9 +128,8 @@ class ListImpl<T extends Field> extends ObjectField {
includes(valueToFind: any, fromIndex: number) {
if (valueToFind instanceof RefField) {
return this[Self].__realFields.includes(valueToFind, fromIndex);
- } else {
- return this[Self].__fieldTuples.includes(valueToFind, fromIndex);
}
+ return this[Self].__fieldTuples.includes(valueToFind, fromIndex);
},
indexOf(valueToFind: any, fromIndex: number) {
if (valueToFind instanceof RefField) {
@@ -151,9 +147,8 @@ class ListImpl<T extends Field> extends ObjectField {
lastIndexOf(valueToFind: any, fromIndex: number) {
if (valueToFind instanceof RefField) {
return this[Self].__realFields.lastIndexOf(valueToFind, fromIndex);
- } else {
- return this[Self].__fieldTuples.lastIndexOf(valueToFind, fromIndex);
}
+ return this[Self].__fieldTuples.lastIndexOf(valueToFind, fromIndex);
},
slice(begin: number, end: number) {
this[Self].__realFields;
@@ -226,7 +221,7 @@ class ListImpl<T extends Field> extends ObjectField {
},
};
static listGetter(target: any, prop: string | symbol, receiver: any): any {
- if (ListImpl.listHandlers.hasOwnProperty(prop)) {
+ if (Object.prototype.hasOwnProperty.call(ListImpl.listHandlers, prop)) {
return ListImpl.listHandlers[prop];
}
return getter(target, prop, receiver);
@@ -244,7 +239,7 @@ class ListImpl<T extends Field> extends ObjectField {
getOwnPropertyDescriptor: (target, prop) => {
if (prop in target[FieldTuples]) {
return {
- configurable: true, //TODO Should configurable be true?
+ configurable: true, // TODO Should configurable be true?
enumerable: true,
};
}
@@ -255,11 +250,13 @@ class ListImpl<T extends Field> extends ObjectField {
throw new Error("Currently properties can't be defined on documents using Object.defineProperty");
},
});
- this[SelfProxy] = list as any as List<Field>; // bcz: ugh .. don't know how to convince typesecript that list is a List
+ // 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
if (fields) {
this[SelfProxy].push(...fields);
}
- return list;
+ // eslint-disable-next-line no-constructor-return
+ return list; // need to return the proxy here, otherwise we don't get any of our list handler functions
}
[key: number]: T | (T extends RefField ? Promise<T> : never);
@@ -271,7 +268,7 @@ class ListImpl<T extends Field> extends ObjectField {
// 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 = DocServer.GetRefFields(unrequested.map(p => p.fieldId));
+ 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]))));
@@ -282,20 +279,20 @@ class ListImpl<T extends Field> extends ObjectField {
return this[FieldTuples].map(toRealField);
}
- @serializable(alias(ListFieldName, list(autoObject(), { afterDeserialize: afterDocDeserialize })))
+ @serializable(alias(ListFieldName, serializrList(autoObject(), { afterDeserialize: afterDocDeserialize })))
private get __fieldTuples() {
return this[FieldTuples];
}
private set __fieldTuples(value) {
this[FieldTuples] = value;
- for (const key in value) {
- const item = value[key];
+ Object.keys(value).forEach(key => {
+ const item = value[Number(key)];
if (item instanceof ObjectField) {
item[Parent] = this[Self];
item[FieldChanged] = containedFieldChangedHandler(this[SelfProxy], Number(key), item);
}
- }
+ });
}
[Copy]() {
@@ -308,24 +305,24 @@ class ListImpl<T extends Field> extends ObjectField {
@observable
private [FieldTuples]: StoredType<T>[] = [];
private [Self] = this;
- private [SelfProxy]: List<Field>; // also used in utils.ts even though it won't be found using find all references
+ // eslint-disable-next-line no-use-before-define
+ private [SelfProxy]: List<FieldType>; // also used in utils.ts even though it won't be found using find all references
- [ToJavascriptString]() {
- return `[${(this as any).map((field: any) => Field.toScriptString(field))}]`;
- }
- [ToScriptString]() {
- return `new List([${(this as any).map((field: any) => Field.toScriptString(field))}])`;
- }
- [ToString]() {
- return `[${(this as any).map((field: any) => Field.toString(field))}]`;
- }
+ [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
}
-export type List<T extends Field> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[];
-export const List: { new <T extends Field>(fields?: T[]): List<T> } = ListImpl as any;
+
+// 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>()
+// eslint-disable-next-line no-redeclare
+export const List: { new <T extends FieldType>(fields?: T[]): List<T> } = ListImpl as any;
ScriptingGlobals.add('List', List);
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function compareLists(l1: any, l2: any) {
- const L1 = Cast(l1, listSpec('string'), []);
- const L2 = Cast(l2, listSpec('string'), []);
+ 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);
}, 'compare two lists');
diff --git a/src/fields/ObjectField.ts b/src/fields/ObjectField.ts
index e1b5b036c..231086262 100644
--- a/src/fields/ObjectField.ts
+++ b/src/fields/ObjectField.ts
@@ -1,26 +1,36 @@
-import { RefField } from './RefField';
-import { FieldChanged, Parent, Copy, ToScriptString, ToString, ToJavascriptString } from './FieldSymbols';
import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
-import { Field } from './Doc';
+import { Copy, FieldChanged, Parent, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
+import { RefField } from './RefField';
export abstract class ObjectField {
// prettier-ignore
public [FieldChanged]?: (diff?: { op: '$addToSet' | '$remFromSet' | '$set';
- items: Field[] | undefined;
+ // eslint-disable-next-line no-use-before-define
+ items: FieldType[] | undefined;
length: number | undefined;
hint?: any }, serverOp?: any) => void;
+ // eslint-disable-next-line no-use-before-define
public [Parent]?: RefField | ObjectField;
abstract [Copy](): ObjectField;
abstract [ToJavascriptString](): string;
abstract [ToScriptString](): string;
abstract [ToString](): string;
-}
-
-export namespace ObjectField {
- export function MakeCopy<T extends ObjectField>(field: T) {
+ static MakeCopy<T extends ObjectField>(field: T) {
return field?.[Copy]();
}
}
+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 820d9b6ff..4f8058ce4 100644
--- a/src/fields/Proxy.ts
+++ b/src/fields/Proxy.ts
@@ -21,7 +21,7 @@ export class ProxyField<T extends RefField> extends ObjectField {
constructor(value?: T | string) {
super();
if (typeof value === 'string') {
- //this.cache = DocServer.GetCachedRefField(value) as any;
+ // this.cache = DocServer.GetCachedRefField(value) as any;
this.fieldId = value;
} else if (value) {
this.cache = { field: value, p: undefined };
@@ -29,7 +29,7 @@ export class ProxyField<T extends RefField> extends ObjectField {
}
}
- [ToValue](doc: any) {
+ [ToValue](/* doc: any */) {
return ProxyField.toValue(this);
}
@@ -39,10 +39,10 @@ export class ProxyField<T extends RefField> extends ObjectField {
}
[ToJavascriptString]() {
- return Field.toScriptString(this[ToValue](undefined)?.value);
+ return Field.toScriptString(this[ToValue]()?.value);
}
[ToScriptString]() {
- return Field.toScriptString(this[ToValue](undefined)?.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); // not sure this is quite right since it doesn't recreate a proxy field, but better than 'invalid' ?
}
[ToString]() {
return 'ProxyField';
@@ -59,7 +59,9 @@ export class ProxyField<T extends RefField> extends ObjectField {
return this._cache;
}
private set cache(val: { field: T | undefined; p: FieldWaiting<T> | undefined }) {
- runInAction(() => (this._cache = { ...val }));
+ runInAction(() => {
+ this._cache = { ...val };
+ });
}
private failed = false;
@@ -78,7 +80,7 @@ export class ProxyField<T extends RefField> extends ObjectField {
return this.cache.field ?? this.cache.p;
}
@computed get needsRequesting(): boolean {
- return !this.cache.field && !this.failed && !this._cache.p && !DocServer.GetCachedRefField(this.fieldId) ? true : false;
+ return !!(!this.cache.field && !this.failed && !this._cache.p && !DocServer.GetCachedRefField(this.fieldId));
}
setExternalValuePromise(externalValuePromise: Promise<any>) {
@@ -92,6 +94,7 @@ export class ProxyField<T extends RefField> extends ObjectField {
}
}
+// eslint-disable-next-line no-redeclare
export namespace ProxyField {
let useProxy = true;
export function DisableProxyFields() {
@@ -115,9 +118,11 @@ export namespace ProxyField {
if (useProxy) {
return { value: value.value };
}
+ return undefined;
}
}
+// eslint-disable-next-line no-use-before-define
function prefetchValue(proxy: PrefetchProxy<RefField>) {
return proxy.value as any;
}
diff --git a/src/fields/RefField.ts b/src/fields/RefField.ts
index 01828dd14..1ce81368a 100644
--- a/src/fields/RefField.ts
+++ b/src/fields/RefField.ts
@@ -1,7 +1,7 @@
-import { serializable, primitive, alias } from 'serializr';
+import { alias, primitive, serializable } from 'serializr';
import { Utils } from '../Utils';
-import { Id, HandleUpdate, ToScriptString, ToString, ToJavascriptString } from './FieldSymbols';
import { afterDocDeserialize } from '../client/util/SerializationHelper';
+import { HandleUpdate, Id, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
export type FieldId = string;
export abstract class RefField {
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index b84a91709..5eb60a2f8 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -1,21 +1,25 @@
+/* eslint-disable no-await-in-loop */
+/* eslint-disable no-use-before-define */
import { AssertionError } from 'assert';
-import { docs_v1 } from 'googleapis';
+import * as Color from 'color';
+import { docs_v1 as docsV1 } from 'googleapis';
import { Fragment, Mark, Node } from 'prosemirror-model';
import { sinkListItem } from 'prosemirror-schema-list';
import { EditorState, TextSelection, Transaction } from 'prosemirror-state';
-import { GoogleApiClientUtils } from '../client/apis/google_docs/GoogleApiClientUtils';
-import { GooglePhotos } from '../client/apis/google_docs/GooglePhotosClientUtils';
+import { ClientUtils, DashColor } from '../ClientUtils';
+import { Utils } from '../Utils';
import { DocServer } from '../client/DocServer';
-import { Docs, DocUtils } from '../client/documents/Documents';
import { Networking } from '../client/Network';
+import { GoogleApiClientUtils } from '../client/apis/google_docs/GoogleApiClientUtils';
+import { GooglePhotos } from '../client/apis/google_docs/GooglePhotosClientUtils';
+import { Docs } from '../client/documents/Documents';
+import { DocUtils } from '../client/documents/DocUtils';
import { FormattedTextBox } from '../client/views/nodes/formattedText/FormattedTextBox';
import { schema } from '../client/views/nodes/formattedText/schema_rts';
-import { DashColor, Utils } from '../Utils';
import { Doc, Opt } from './Doc';
import { Id } from './FieldSymbols';
import { RichTextField } from './RichTextField';
import { Cast, StrCast } from './Types';
-import * as Color from 'color';
export namespace RichTextUtils {
const delimiter = '\n';
@@ -47,13 +51,11 @@ export namespace RichTextUtils {
return JSON.stringify(state);
};
- export const Synthesize = (plainText: string, oldState?: RichTextField) => {
- return new RichTextField(ToProsemirrorState(plainText, oldState), plainText);
- };
+ export const Synthesize = (plainText: string, oldState?: RichTextField) => new RichTextField(ToProsemirrorState(plainText, oldState), plainText);
export const ToPlainText = (state: EditorState) => {
// Because we're working with plain text, just concatenate all paragraphs
- const content = state.doc.content;
+ const { content } = state.doc;
const paragraphs: Node[] = [];
content.forEach(node => node.type.name === 'paragraph' && paragraphs.push(node));
@@ -112,9 +114,9 @@ export namespace RichTextUtils {
agnostic: string;
}
- const parseInlineObjects = async (document: docs_v1.Schema$Document): Promise<Map<string, ImageTemplate>> => {
+ const parseInlineObjects = async (document: docsV1.Schema$Document): Promise<Map<string, ImageTemplate>> => {
const inlineObjectMap = new Map<string, ImageTemplate>();
- const inlineObjects = document.inlineObjects;
+ const { inlineObjects } = document;
if (inlineObjects) {
const objects = Object.keys(inlineObjects).map(objectId => inlineObjects[objectId]);
@@ -140,8 +142,8 @@ export namespace RichTextUtils {
inlineObjectMap.set(object.objectId!, {
title: embeddedObject.title || `Imported Image from ${document.title}`,
width,
- url: Utils.prepend(_m.client),
- agnostic: Utils.prepend(agnostic.client),
+ url: ClientUtils.prepend(_m.client),
+ agnostic: ClientUtils.prepend(agnostic.client),
});
}
}
@@ -170,10 +172,12 @@ export namespace RichTextUtils {
const indentMap = new Map<ListGroup, BulletPosition[]>();
let globalOffset = 0;
const nodes: Node[] = [];
+ // eslint-disable-next-line no-restricted-syntax
for (const element of structured) {
if (Array.isArray(element)) {
lists.push(element);
const positions: BulletPosition[] = [];
+ // eslint-disable-next-line no-loop-func
const items = element.map(paragraph => {
const item = listItem(state.schema, paragraph.contents);
const sinks = paragraph.bullet!;
@@ -187,41 +191,42 @@ export namespace RichTextUtils {
});
indentMap.set(element, positions);
nodes.push(list(state.schema, items));
+ } else if (element.contents.some(child => 'inlineObjectId' in child)) {
+ const group = element.contents;
+ // eslint-disable-next-line no-loop-func
+ group.forEach((child, i) => {
+ let node: Opt<Node>;
+ if ('inlineObjectId' in child) {
+ node = imageNode(state.schema, inlineObjectMap.get(child.inlineObjectId!)!, textNote);
+ } else if ('content' in child && (i !== group.length - 1 || child.content!.removeTrailingNewlines().length)) {
+ node = paragraphNode(state.schema, [child]);
+ }
+ if (node) {
+ position += node.nodeSize;
+ nodes.push(node);
+ }
+ });
} else {
- if (element.contents.some(child => 'inlineObjectId' in child)) {
- const group = element.contents;
- group.forEach((child, i) => {
- let node: Opt<Node>;
- if ('inlineObjectId' in child) {
- node = imageNode(state.schema, inlineObjectMap.get(child.inlineObjectId!)!, textNote);
- } else if ('content' in child && (i !== group.length - 1 || child.content!.removeTrailingNewlines().length)) {
- node = paragraphNode(state.schema, [child]);
- }
- if (node) {
- position += node.nodeSize;
- nodes.push(node);
- }
- });
- } else {
- const paragraph = paragraphNode(state.schema, element.contents);
- nodes.push(paragraph);
- position += paragraph.nodeSize;
- }
+ const paragraph = paragraphNode(state.schema, element.contents);
+ nodes.push(paragraph);
+ position += paragraph.nodeSize;
}
}
state = state.apply(state.tr.replaceWith(0, 2, nodes));
const sink = sinkListItem(state.schema.nodes.list_item);
- const dispatcher = (tr: Transaction) => (state = state.apply(tr));
- for (const list of lists) {
- for (const pos of indentMap.get(list)!) {
+ const dispatcher = (tr: Transaction) => {
+ state = state.apply(tr);
+ };
+ lists.forEach(list => {
+ indentMap.get(list)?.forEach(pos => {
const resolved = state.doc.resolve(pos.value);
state = state.apply(state.tr.setSelection(new TextSelection(resolved)));
for (let i = 0; i < pos.sinks; i++) {
sink(state, dispatcher);
}
- }
- }
+ });
+ });
return { title, text, state };
};
@@ -233,7 +238,7 @@ export namespace RichTextUtils {
const parseLists = (paragraphs: ListGroup) => {
const groups: PreparedParagraphs = [];
let group: ListGroup = [];
- for (const paragraph of paragraphs) {
+ paragraphs.forEach(paragraph => {
if (paragraph.bullet !== undefined) {
group.push(paragraph);
} else {
@@ -243,26 +248,22 @@ export namespace RichTextUtils {
}
groups.push(paragraph);
}
- }
+ });
group.length && groups.push(group);
return groups;
};
- const listItem = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => {
- return schema.node('list_item', null, paragraphNode(schema, runs));
- };
+ const listItem = (lschema: any, runs: docsV1.Schema$TextRun[]): Node => lschema.node('list_item', null, paragraphNode(lschema, runs));
- const list = (schema: any, items: Node[]): Node => {
- return schema.node('ordered_list', { mapStyle: 'bullet' }, items);
- };
+ const list = (lschema: any, items: Node[]): Node => lschema.node('ordered_list', { mapStyle: 'bullet' }, items);
- const paragraphNode = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => {
- const children = runs.map(run => textNode(schema, run)).filter(child => child !== undefined);
+ const paragraphNode = (lschema: any, runs: docsV1.Schema$TextRun[]): Node => {
+ const children = runs.map(run => textNode(lschema, run)).filter(child => child !== undefined);
const fragment = children.length ? Fragment.from(children) : undefined;
- return schema.node('paragraph', null, fragment);
+ return lschema.node('paragraph', null, fragment);
};
- const imageNode = (schema: any, image: ImageTemplate, textNote: Doc) => {
+ const imageNode = (lschema: any, image: ImageTemplate, textNote: Doc) => {
const { url: src, width, agnostic } = image;
let docId: string;
const guid = Utils.GenerateDeterministicGuid(agnostic);
@@ -275,30 +276,30 @@ export namespace RichTextUtils {
} else {
docId = backingDocId;
}
- return schema.node('image', { src, agnostic, width, docId, float: null });
+ return lschema.node('image', { src, agnostic, width, docId, float: null });
};
- const textNode = (schema: any, run: docs_v1.Schema$TextRun) => {
+ const textNode = (lschema: any, run: docsV1.Schema$TextRun) => {
const text = run.content!.removeTrailingNewlines();
- return text.length ? schema.text(text, styleToMarks(schema, run.textStyle)) : undefined;
+ return text.length ? lschema.text(text, styleToMarks(lschema, run.textStyle)) : undefined;
};
- const StyleToMark = new Map<keyof docs_v1.Schema$TextStyle, keyof typeof schema.marks>([
+ const StyleToMark = new Map<keyof docsV1.Schema$TextStyle, keyof typeof schema.marks>([
['bold', 'strong'],
['italic', 'em'],
['foregroundColor', 'pFontColor'],
['fontSize', 'pFontSize'],
]);
- const styleToMarks = (schema: any, textStyle?: docs_v1.Schema$TextStyle) => {
+ const styleToMarks = (lschema: any, textStyle?: docsV1.Schema$TextStyle) => {
if (!textStyle) {
return undefined;
}
const marks: Mark[] = [];
Object.keys(textStyle).forEach(key => {
- let value: any;
- const targeted = key as keyof docs_v1.Schema$TextStyle;
- if ((value = textStyle[targeted])) {
+ const targeted = key as keyof docsV1.Schema$TextStyle;
+ const value = textStyle[targeted] as any;
+ if (value) {
const attributes: any = {};
let converted = StyleToMark.get(targeted) || targeted;
@@ -315,20 +316,20 @@ export namespace RichTextUtils {
converted = ImportFontFamilyMapping.get(value.fontFamily) || 'timesNewRoman';
}
- const mapped = schema.marks[converted];
+ const mapped = lschema.marks[converted];
if (!mapped) {
alert(`No mapping found for ${converted}!`);
return;
}
- const mark = schema.mark(mapped, attributes);
+ const mark = lschema.mark(mapped, attributes);
mark && marks.push(mark);
}
});
return marks;
};
- const MarkToStyle = new Map<keyof typeof schema.marks, keyof docs_v1.Schema$TextStyle>([
+ const MarkToStyle = new Map<keyof typeof schema.marks, keyof docsV1.Schema$TextStyle>([
['strong', 'bold'],
['em', 'italic'],
['pFontColor', 'foregroundColor'],
@@ -360,16 +361,18 @@ export namespace RichTextUtils {
const ignored = ['user_mark'];
- const marksToStyle = async (nodes: (Node | null)[]): Promise<docs_v1.Schema$Request[]> => {
- const requests: docs_v1.Schema$Request[] = [];
+ const marksToStyle = async (nodes: (Node | null)[]): Promise<docsV1.Schema$Request[]> => {
+ const requests: docsV1.Schema$Request[] = [];
let position = 1;
+ // eslint-disable-next-line no-restricted-syntax
for (const node of nodes) {
if (node === null) {
position += 2;
+ // eslint-disable-next-line no-continue
continue;
}
const { marks, attrs, nodeSize } = node;
- const textStyle: docs_v1.Schema$TextStyle = {};
+ const textStyle: docsV1.Schema$TextStyle = {};
const information: LinkInformation = {
startIndex: position,
endIndex: position + nodeSize,
@@ -377,36 +380,45 @@ export namespace RichTextUtils {
};
let mark: Mark;
const markMap = BuildMarkMap(marks);
+ // eslint-disable-next-line no-restricted-syntax
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 docs_v1.Schema$TextStyle);
+ let converted = MarkToStyle.get(markName) || (markName as keyof docsV1.Schema$TextStyle);
let value: any = true;
if (!converted) {
+ // eslint-disable-next-line no-continue
continue;
}
+ // eslint-disable-next-line @typescript-eslint/no-shadow
const { attrs } = mark;
switch (converted) {
case 'link':
- let url = attrs.allLinks.length ? attrs.allLinks[0].href : '';
- const delimiter = '/doc/';
- const alreadyShared = '?sharing=true';
- if (new RegExp(window.location.origin + delimiter).test(url) && !url.endsWith(alreadyShared)) {
- const linkDoc = await DocServer.GetRefField(url.split(delimiter)[1]);
- if (linkDoc instanceof Doc) {
- let exported = (await Cast(linkDoc.link_anchor_2, Doc))!;
- if (!exported.customLayout) {
- exported = Doc.MakeEmbedding(exported);
- DocUtils.makeCustomViewClicked(exported, Docs.Create.FreeformDocument);
- linkDoc.link_anchor_2 = exported;
+ {
+ let url = attrs.allLinks.length ? attrs.allLinks[0].href : '';
+ 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);
+ DocUtils.makeCustomViewClicked(exported, Docs.Create.FreeformDocument);
+ linkDoc.link_anchor_2 = exported;
+ }
+ url = ClientUtils.shareUrl(exported[Id]);
}
- url = Utils.shareUrl(exported[Id]);
}
+ value = { url };
+ textStyle.foregroundColor = fromRgb.blue;
+ textStyle.bold = true;
}
- value = { url };
- textStyle.foregroundColor = fromRgb.blue;
- textStyle.bold = true;
break;
case 'fontSize':
value = { magnitude: attrs.fontSize, unit: 'PT' };
@@ -416,9 +428,11 @@ export namespace RichTextUtils {
break;
case 'weightedFontFamily':
value = { fontFamily: ExportFontFamilyMapping.get(markName) };
+ break;
+ default:
}
- let matches: RegExpExecArray | null;
- if ((matches = /p(\d+)/g.exec(markName)) !== null) {
+ const matches = /p(\d+)/g.exec(markName);
+ if (matches !== null) {
converted = 'fontSize';
value = { magnitude: parseInt(matches[1].replace('px', '')), unit: 'PT' };
}
@@ -428,7 +442,7 @@ export namespace RichTextUtils {
requests.push(EncodeStyleUpdate(information));
}
if (node.type.name === 'image') {
- const width = attrs.width;
+ const { width } = attrs;
requests.push(
await EncodeImage({
startIndex: position + nodeSize - 1,
@@ -444,14 +458,16 @@ export namespace RichTextUtils {
const BuildMarkMap = (marks: readonly Mark[]) => {
const markMap: { [type: string]: Mark } = {};
- marks.forEach(mark => (markMap[mark.type.name] = mark));
+ marks.forEach(mark => {
+ markMap[mark.type.name] = mark;
+ });
return markMap;
};
interface LinkInformation {
startIndex: number;
endIndex: number;
- textStyle: docs_v1.Schema$TextStyle;
+ textStyle: docsV1.Schema$TextStyle;
}
interface ImageInformation {
@@ -461,36 +477,34 @@ export namespace RichTextUtils {
}
namespace fromRgb {
- export const convert = (red: number, green: number, blue: number): docs_v1.Schema$OptionalColor => {
- return {
- color: {
- rgbColor: {
- red: red / 255,
- green: green / 255,
- blue: blue / 255,
- },
+ export const convert = (red: number, green: number, blue: number): docsV1.Schema$OptionalColor => ({
+ color: {
+ rgbColor: {
+ red: red / 255,
+ green: green / 255,
+ blue: blue / 255,
},
- };
- };
+ },
+ });
export const red = convert(255, 0, 0);
export const green = convert(0, 255, 0);
export const blue = convert(0, 0, 255);
}
- const fromHex = (color: string): docs_v1.Schema$OptionalColor => {
+ const fromHex = (color: string): docsV1.Schema$OptionalColor => {
const c = DashColor(color);
return fromRgb.convert(c.red(), c.green(), c.blue());
};
- const EncodeStyleUpdate = (information: LinkInformation): docs_v1.Schema$Request => {
+ const EncodeStyleUpdate = (information: LinkInformation): docsV1.Schema$Request => {
const { startIndex, endIndex, textStyle } = information;
return {
updateTextStyle: {
fields: '*',
range: { startIndex, endIndex },
textStyle,
- } as docs_v1.Schema$UpdateTextStyleRequest,
+ } as docsV1.Schema$UpdateTextStyleRequest,
};
};
diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts
index f5e64ae1f..89e5cda8d 100644
--- a/src/fields/Schema.ts
+++ b/src/fields/Schema.ts
@@ -1,5 +1,9 @@
+/* eslint-disable guard-for-in */
+/* eslint-disable no-restricted-syntax */
+/* eslint-disable no-redeclare */
+/* eslint-disable no-use-before-define */
import { Interface, ToInterface, Cast, ToConstructor, HasTail, Head, Tail, ListSpec, ToType, DefaultFieldConstructor } from './Types';
-import { Doc, Field } from './Doc';
+import { Doc, FieldType } from './Doc';
import { ObjectField } from './ObjectField';
import { RefField } from './RefField';
import { SelfProxy } from './DocSymbols';
@@ -12,6 +16,7 @@ type AllToInterface<T extends Interface[]> = {
export const emptySchema = createSchema({});
export const Document = makeInterface(emptySchema);
+// eslint-disable-next-line no-redeclare
export type Document = makeInterface<[typeof emptySchema]>;
export interface InterfaceFunc<T extends Interface[]> {
@@ -36,9 +41,10 @@ export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFu
if (prop in schema) {
const desc = prop === 'proto' ? Doc : (schema as any)[prop]; // bcz: proto doesn't appear in schemas ... maybe it should?
if (typeof desc === 'object' && 'defaultVal' in desc && 'type' in desc) {
- //defaultSpec
+ // defaultSpec
return Cast(field, desc.type, desc.defaultVal);
}
+ // eslint-disable-next-line no-prototype-builtins
if (typeof desc === 'function' && !ObjectField.isPrototypeOf(desc) && !RefField.isPrototypeOf(desc)) {
const doc = Cast(field, Doc);
if (doc === undefined) {
@@ -47,7 +53,7 @@ export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFu
if (doc instanceof Doc) {
return desc(doc);
}
- return doc.then(doc => doc && desc(doc));
+ return doc.then(d => d && desc(d));
}
return Cast(field, desc);
}
@@ -73,8 +79,8 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc)
get() {
return Cast(this.__doc[key], type as any);
},
- set(value) {
- value = Cast(value, type as any);
+ set(setValue) {
+ const value = Cast(setValue, type as any);
if (value !== undefined) {
this.__doc[key] = value;
return;
@@ -93,17 +99,18 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc)
};
}
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function createSchema<T extends Interface>(schema: T): T & { proto: ToConstructor<Doc> } {
return undefined as any;
- (schema as any).proto = Doc;
- return schema as any;
+ // (schema as any).proto = Doc;
+ // return schema as any;
}
-export function listSpec<U extends ToConstructor<Field>>(type: U): ListSpec<ToType<U>> {
- return { List: type as any }; //TODO Types
+export function listSpec<U extends ToConstructor<FieldType>>(type: U): ListSpec<ToType<U>> {
+ return { List: type as any }; // TODO Types
}
-export function defaultSpec<T extends ToConstructor<Field>>(type: T, defaultVal: ToType<T>): DefaultFieldConstructor<ToType<T>> {
+export function defaultSpec<T extends ToConstructor<FieldType>>(type: T, defaultVal: ToType<T>): DefaultFieldConstructor<ToType<T>> {
return {
type: type as any,
defaultVal,
diff --git a/src/fields/SchemaHeaderField.ts b/src/fields/SchemaHeaderField.ts
index fb4dc4e5b..0a8dd1d9e 100644
--- a/src/fields/SchemaHeaderField.ts
+++ b/src/fields/SchemaHeaderField.ts
@@ -1,9 +1,19 @@
+import { primitive, serializable } from 'serializr';
+import { ScriptingGlobals, scriptingGlobal } from '../client/util/ScriptingGlobals';
import { Deserializable } from '../client/util/SerializationHelper';
-import { serializable, primitive } from 'serializr';
+import { Copy, FieldChanged, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
import { ObjectField } from './ObjectField';
-import { Copy, ToScriptString, ToString, FieldChanged, ToJavascriptString } from './FieldSymbols';
-import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals';
-import { ColumnType } from '../client/views/collections/collectionSchema/CollectionSchemaView';
+
+export enum ColumnType {
+ Number,
+ String,
+ Boolean,
+ Date,
+ Image,
+ RTF,
+ Enumeration,
+ Any,
+}
export const PastelSchemaPalette = new Map<string, string>([
// ["pink1", "#FFB4E8"],
@@ -69,13 +79,14 @@ export class SchemaHeaderField extends ObjectField {
@serializable(primitive())
desc: boolean | undefined; // boolean determines sort order, undefined when no sort
+ // eslint-disable-next-line default-param-last
constructor(heading: string = '', color: string = RandomPastel(), type?: ColumnType, width?: number, desc?: boolean, collapsed?: boolean) {
super();
this.heading = heading;
this.color = color;
- this.type = type ? type : 0;
- this.width = width ? width : -1;
+ this.type = type ?? 0;
+ this.width = width ?? -1;
this.desc = desc;
this.collapsed = collapsed;
}
@@ -124,6 +135,8 @@ export class SchemaHeaderField extends ObjectField {
return `SchemaHeaderField`;
}
}
+
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function schemaHeaderField(heading: string, color: string, type: number, width: number, desc?: boolean, collapsed?: boolean) {
return new SchemaHeaderField(heading, color, type, width, desc, collapsed);
});
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 8b51088b2..8fe365ac2 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -1,17 +1,16 @@
import { action, makeObservable, observable } from 'mobx';
import { computedFn } from 'mobx-utils';
-import { createSimpleSchema, custom, map, object, primitive, PropSchema, serializable, SKIP } from 'serializr';
-import { DocServer } from '../client/DocServer';
-import { CompiledScript, CompileScript, ScriptOptions, Transformer } from '../client/util/Scripting';
-import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals';
-import { autoObject, Deserializable } from '../client/util/SerializationHelper';
+import { PropSchema, SKIP, createSimpleSchema, custom, map, object, primitive, serializable } from 'serializr';
import { numberRange } from '../Utils';
-import { Doc, Field, FieldResult, Opt } from './Doc';
+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 { Copy, FieldChanged, Id, ToJavascriptString, ToScriptString, ToString, ToValue } from './FieldSymbols';
import { List } from './List';
-import { ObjectField } from './ObjectField';
+import { ObjGetRefField, ObjectField } from './ObjectField';
import { Cast, StrCast } from './Types';
-import { GPTCallType, gptAPICall } from '../client/apis/gpt/GPT';
function optional(propSchema: PropSchema) {
return custom(
@@ -44,7 +43,9 @@ const scriptSchema = createSimpleSchema({
originalScript: true,
});
-function finalizeScript(script: ScriptField) {
+// eslint-disable-next-line no-use-before-define
+function finalizeScript(scriptIn: ScriptField) {
+ const script = scriptIn;
const comp = CompileScript(script.script.originalScript, script.script.options);
if (!comp.compiled) {
throw new Error("Couldn't compile loaded script");
@@ -54,11 +55,13 @@ function finalizeScript(script: ScriptField) {
if (!compset.compiled) {
throw new Error("Couldn't compile setter script");
}
- (script as any).setterscript = compset;
+ script.setterscript = compset;
}
return comp;
}
-async function deserializeScript(script: ScriptField) {
+// eslint-disable-next-line no-use-before-define
+async function deserializeScript(scriptIn: ScriptField) {
+ const script = scriptIn;
if (script.captures) {
const captured: any = {};
(script.script.options as ScriptOptions).capturedVariables = captured;
@@ -68,13 +71,16 @@ async function deserializeScript(script: ScriptField) {
const val = capture.split(':')[1];
if (val === 'true') captured[key] = true;
else if (val === 'false') captured[key] = false;
- else if (val.startsWith('ID->')) captured[key] = await DocServer.GetRefField(val.replace('ID->', ''));
+ else if (val.startsWith('ID->')) captured[key] = await ObjGetRefField(val.replace('ID->', ''));
else if (!isNaN(Number(val))) captured[key] = Number(val);
else captured[key] = val;
})
- ).then(() => ((script as any).script = finalizeScript(script)));
+ ).then(() => {
+ script.script = finalizeScript(script);
+ });
} else {
- (script as any).script = ScriptField.GetScriptFieldCache(script.script.originalScript) ?? finalizeScript(script);
+ // eslint-disable-next-line no-use-before-define
+ script.script = ScriptField.GetScriptFieldCache(script.script.originalScript) ?? finalizeScript(script);
}
}
@@ -84,9 +90,9 @@ export class ScriptField extends ObjectField {
@serializable
readonly rawscript: string | undefined;
@serializable(object(scriptSchema))
- readonly script: CompiledScript;
+ script: CompiledScript;
@serializable(object(scriptSchema))
- readonly setterscript: CompiledScript | undefined;
+ setterscript: CompiledScript | undefined;
@serializable
@observable
_cachedResult: FieldResult = undefined;
@@ -131,11 +137,11 @@ export class ScriptField extends ObjectField {
[ToString]() {
return this.script.originalScript;
}
+ // eslint-disable-next-line default-param-last
public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Doc | string | number | boolean }, transformer?: Transformer) {
return CompileScript(script, {
params: {
this: Doc?.name || 'Doc', // this is the doc that executes the script
- self: Doc?.name || 'Doc', // self is the root doc of the doc that executes the script
documentView: 'any',
_last_: 'any', // _last_ is the previous value of a computed field when it is being triggered to re-run.
_setCacheResult_: 'any', // set the cached value of the function
@@ -150,20 +156,23 @@ export class ScriptField extends ObjectField {
});
}
+ // eslint-disable-next-line default-param-last
public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) {
const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
return compiled.compiled ? new ScriptField(compiled) : undefined;
}
+ // eslint-disable-next-line default-param-last
public static MakeScript(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) {
const compiled = ScriptField.CompileScript(script, params, false, capturedVariables);
return compiled.compiled ? new ScriptField(compiled) : undefined;
}
- public static CallGpt(queryText: string, setVal: (val: FieldResult) => void, target: Doc) {
+ public static CallGpt(queryTextIn: string, setVal: (val: FieldResult) => void, target: Doc) {
+ let queryText = queryTextIn;
if (typeof queryText === 'string' && setVal) {
while (queryText.match(/\(this\.[a-zA-Z_]*\)/)?.length) {
const fieldRef = queryText.split('(this.')[1].replace(/\).*/, '');
- queryText = queryText.replace(/\(this\.[a-zA-Z_]*\)/, Field.toString(target[fieldRef] as Field));
+ queryText = queryText.replace(/\(this\.[a-zA-Z_]*\)/, Field.toString(target[fieldRef] as FieldType));
}
setVal(`Chat Pending: ${queryText}`);
gptAPICall(queryText, GPTCallType.COMPLETION).then(result => {
@@ -198,24 +207,29 @@ export class ComputedField extends ScriptField {
}
_lastComputedResult: FieldResult;
- value = (doc:Doc) => (this._lastComputedResult = this._cachedResult ??
- computedFn((doc: Doc) =>
- this.script.compiled &&
- this.script.run( {
- this: doc,
- //value: '',
- _setCacheResult_: this.setCacheResult,
- _last_: this._lastComputedResult,
- _readOnly_: true,
- },
- console.log
- ).result
- )(doc)
- ); // prettier-ignore
+ value = (doc: Doc) => {
+ this._lastComputedResult =
+ this._cachedResult ??
+ computedFn(() =>
+ this.script.compiled &&
+ this.script.run(
+ {
+ this: doc,
+ // value: '',
+ _setCacheResult_: this.setCacheResult,
+ _last_: this._lastComputedResult,
+ _readOnly_: true,
+ },
+ console.log
+ ).result
+ )(); // prettier-ignore
+ return this._lastComputedResult;
+ };
- [ToValue](doc: Doc) { if (ComputedField.useComputed) return { value: this.value(doc) }; } // prettier-ignore
+ [ToValue](doc: Doc) { return ComputedField.useComputed ? { value: this.value(doc) } : undefined; } // prettier-ignore
[Copy](): ObjectField { return new ComputedField(this.script, this.setterscript, this.rawscript); } // prettier-ignore
+ // eslint-disable-next-line default-param-last
public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }, setterscript?: string) {
const compiled = ScriptField.CompileScript(script, params, true, { value: '', ...capturedVariables });
const compiledsetter = setterscript ? ScriptField.CompileScript(setterscript, { ...params, value: 'any' }, false, capturedVariables) : undefined;
@@ -225,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(i => undefined) as any as number[]);
+ const flist = new List<number>(numberRange(curTimecode + 1).map(() => undefined) as any as number[]);
flist[curTimecode] = Cast(doc[fieldKey], 'number', null);
doc[`${fieldKey}_indexed`] = flist;
}
@@ -235,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(i => undefined) as any as string[]);
+ const flist = new List<string>(numberRange(curTimecode + 1).map(() => undefined) as any as string[]);
flist[curTimecode] = StrCast(doc[fieldKey]);
doc[`${fieldKey}_indexed`] = flist;
}
@@ -244,9 +258,9 @@ export class ComputedField extends ScriptField {
return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
}
public static MakeInterpolatedDataField(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) {
- if (doc[`${fieldKey}`] instanceof List) return;
+ if (doc[`${fieldKey}`] instanceof List) return undefined;
if (!doc[`${fieldKey}_indexed`]) {
- const flist = new List<Field>(numberRange(curTimecode + 1).map(i => undefined) as any as Field[]);
+ const flist = new List<FieldType>(numberRange(curTimecode + 1).map(() => undefined) as any as FieldType[]);
flist[curTimecode] = Field.Copy(doc[fieldKey]);
doc[`${fieldKey}_indexed`] = flist;
}
@@ -257,11 +271,13 @@ export class ComputedField extends ScriptField {
false,
{}
);
- return (doc[`${fieldKey}`] = getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined);
+ doc[fieldKey] = getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
+ return doc[fieldKey];
}
}
ScriptingGlobals.add(
+ // eslint-disable-next-line prefer-arrow-callback
function setIndexVal(list: any[], index: number, value: any) {
while (list.length <= index) list.push(undefined);
list[index] = value;
@@ -271,6 +287,7 @@ ScriptingGlobals.add(
);
ScriptingGlobals.add(
+ // eslint-disable-next-line prefer-arrow-callback
function getIndexVal(list: any[], index: number, defaultVal: Opt<number> = undefined) {
return list?.reduce((p, x, i) => ((i <= index && x !== undefined) || p === undefined ? x : p), defaultVal);
},
@@ -279,12 +296,14 @@ ScriptingGlobals.add(
);
ScriptingGlobals.add(
+ // eslint-disable-next-line prefer-arrow-callback
function makeScript(script: string) {
return ScriptField.MakeScript(script);
},
'returns the value at a given index of a list',
'(list: any[], index: number)'
);
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function dashCallChat(setVal: (val: FieldResult) => void, target: Doc, queryText: string) {
ScriptField.CallGpt(queryText, setVal, target);
}, 'calls chat gpt for the query string and then calls setVal with the result');
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index 337e8ca21..26196d15d 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -1,5 +1,5 @@
import { DateField } from './DateField';
-import { Doc, Field, FieldResult, Opt } from './Doc';
+import { Doc, FieldType, FieldResult, Opt } from './Doc';
import { List } from './List';
import { ProxyField } from './Proxy';
import { RefField } from './RefField';
@@ -7,63 +7,69 @@ import { RichTextField } from './RichTextField';
import { ScriptField } from './ScriptField';
import { CsvField, ImageField, WebField } from './URLField';
+// eslint-disable-next-line no-use-before-define
+export type ToConstructor<T extends FieldType> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends List<infer U> ? ListSpec<U> : new (...args: any[]) => T;
+
+export type DefaultFieldConstructor<T extends FieldType> = {
+ type: ToConstructor<T>;
+ defaultVal: T;
+};
+// type ListSpec<T extends Field[]> = { List: ToContructor<Head<T>> | ListSpec<Tail<T>> };
+export type ListSpec<T extends FieldType> = { List: ToConstructor<T> };
+
+export type InterfaceValue = ToConstructor<FieldType> | ListSpec<FieldType> | DefaultFieldConstructor<FieldType> | ((doc?: Doc) => any);
+
export type ToType<T extends InterfaceValue> = T extends 'string'
? string
: T extends 'number'
- ? number
- : T extends 'boolean'
- ? boolean
- : T extends ListSpec<infer U>
- ? List<U>
- : // T extends { new(...args: any[]): infer R } ? (R | Promise<R>) : never;
- T extends DefaultFieldConstructor<infer _U>
- ? never
- : T extends { new (...args: any[]): List<Field> }
- ? never
- : T extends { new (...args: any[]): infer R }
- ? R
- : T extends (doc?: Doc) => infer R
- ? R
- : never;
-
-export type ToConstructor<T extends Field> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends List<infer U> ? ListSpec<U> : new (...args: any[]) => T;
+ ? number
+ : T extends 'boolean'
+ ? boolean
+ : T extends ListSpec<infer U>
+ ? List<U>
+ : // T extends { new(...args: any[]): infer R } ? (R | Promise<R>) : never;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ T extends DefaultFieldConstructor<infer _U>
+ ? never
+ : T extends { new (...args: any[]): List<FieldType> }
+ ? never
+ : T extends { new (...args: any[]): infer R }
+ ? R
+ : T extends (doc?: Doc) => infer R
+ ? R
+ : never;
+export interface Interface {
+ [key: string]: InterfaceValue;
+ // [key: string]: ToConstructor<Field> | ListSpec<Field[]>;
+}
export type ToInterface<T extends Interface> = {
[P in Exclude<keyof T, 'proto'>]: T[P] extends DefaultFieldConstructor<infer F> ? Exclude<FieldResult<F>, undefined> : FieldResult<ToType<T[P]>>;
};
-// type ListSpec<T extends Field[]> = { List: ToContructor<Head<T>> | ListSpec<Tail<T>> };
-export type ListSpec<T extends Field> = { List: ToConstructor<T> };
-
-export type DefaultFieldConstructor<T extends Field> = {
- type: ToConstructor<T>;
- defaultVal: T;
-};
-
// type ListType<U extends Field[]> = { 0: List<ListType<Tail<U>>>, 1: ToType<Head<U>> }[HasTail<U> extends true ? 0 : 1];
export type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never;
export type Tail<T extends any[]> = ((...t: T) => any) extends (_: any, ...tail: infer TT) => any ? TT : [];
export type HasTail<T extends any[]> = T extends [] | [any] ? false : true;
+// TODO Allow you to optionally specify default values for schemas, which should then make that field not be partial
+export type WithoutRefField<T extends FieldType> = T extends RefField ? never : T;
-export type InterfaceValue = ToConstructor<Field> | ListSpec<Field> | DefaultFieldConstructor<Field> | ((doc?: Doc) => any);
-//TODO Allow you to optionally specify default values for schemas, which should then make that field not be partial
-export interface Interface {
- [key: string]: InterfaceValue;
- // [key: string]: ToConstructor<Field> | ListSpec<Field[]>;
-}
-export type WithoutRefField<T extends Field> = T extends RefField ? never : T;
+export type CastCtor = ToConstructor<FieldType> | ListSpec<FieldType>;
-export type CastCtor = ToConstructor<Field> | ListSpec<Field>;
+type WithoutList<T extends FieldType> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
export function Cast<T extends CastCtor>(field: FieldResult, ctor: T): FieldResult<ToType<T>>;
+// eslint-disable-next-line no-redeclare
export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal: WithoutList<WithoutRefField<ToType<T>>> | null): WithoutList<ToType<T>>;
+// eslint-disable-next-line no-redeclare
export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal?: ToType<T> | null): FieldResult<ToType<T>> | undefined {
if (field instanceof Promise) {
return defaultVal === undefined ? (field.then(f => Cast(f, ctor) as any) as any) : defaultVal === null ? undefined : defaultVal;
}
if (field !== undefined && !(field instanceof Promise)) {
if (typeof ctor === 'string') {
+ // eslint-disable-next-line valid-typeof
if (typeof field === ctor) {
return field as ToType<T>;
}
@@ -80,6 +86,10 @@ export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal
return defaultVal === null ? undefined : defaultVal;
}
+export function toList(doc: Doc | Doc[]) {
+ return doc instanceof Doc ? [doc] : doc;
+}
+
export function DocCast(field: FieldResult, defaultVal?: Doc) {
const doc = Cast(field, Doc, null);
return doc && !(doc instanceof Promise) ? doc : (defaultVal as Doc);
@@ -116,18 +126,18 @@ export function ImageCast(field: FieldResult, defaultVal: ImageField | null = nu
return Cast(field, ImageField, defaultVal);
}
-type WithoutList<T extends Field> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
-
-export function FieldValue<T extends Field, U extends WithoutList<T>>(field: FieldResult<T>, defaultValue: U): WithoutList<T>;
-export function FieldValue<T extends Field>(field: FieldResult<T>): Opt<T>;
-export function FieldValue<T extends Field>(field: FieldResult<T>, defaultValue?: T): Opt<T> {
+export function FieldValue<T extends FieldType, U extends WithoutList<T>>(field: FieldResult<T>, defaultValue: U): WithoutList<T>;
+// eslint-disable-next-line no-redeclare
+export function FieldValue<T extends FieldType>(field: FieldResult<T>): Opt<T>;
+// eslint-disable-next-line no-redeclare
+export function FieldValue<T extends FieldType>(field: FieldResult<T>, defaultValue?: T): Opt<T> {
return field instanceof Promise || field === undefined ? defaultValue : field;
}
export interface PromiseLike<T> {
then(callback: (field: Opt<T>) => void): void;
}
-export function PromiseValue<T extends Field>(field: FieldResult<T>): PromiseLike<Opt<T>> {
+export function PromiseValue<T extends FieldType>(field: FieldResult<T>): PromiseLike<Opt<T>> {
if (field instanceof Promise) return field as Promise<Opt<T>>;
return {
then(cb: (field: Opt<T>) => void) {
diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts
index 87334ad16..3a83e7ca0 100644
--- a/src/fields/URLField.ts
+++ b/src/fields/URLField.ts
@@ -1,18 +1,14 @@
+import { custom, serializable } from 'serializr';
+import { ClientUtils } from '../ClientUtils';
+import { scriptingGlobal } from '../client/util/ScriptingGlobals';
import { Deserializable } from '../client/util/SerializationHelper';
-import { serializable, custom } from 'serializr';
+import { Copy, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
import { ObjectField } from './ObjectField';
-import { ToScriptString, ToString, Copy, ToJavascriptString } from './FieldSymbols';
-import { scriptingGlobal } from '../client/util/ScriptingGlobals';
-import { Utils } from '../Utils';
function url() {
return custom(
- function (value: URL) {
- return value?.origin === window.location.origin ? value.pathname : value?.href;
- },
- function (jsonValue: string) {
- return new URL(jsonValue, window.location.origin);
- }
+ (value: URL) => (value?.origin === window.location.origin ? value.pathname : value?.href),
+ (jsonValue: string) => new URL(jsonValue, window.location.origin)
);
}
@@ -20,30 +16,34 @@ export abstract class URLField extends ObjectField {
@serializable(url())
readonly url: URL;
- constructor(url: string);
- constructor(url: URL);
- constructor(url: URL | string) {
+ constructor(urlVal: string);
+ // eslint-disable-next-line @typescript-eslint/no-shadow
+ constructor(urlVal: URL);
+ // eslint-disable-next-line @typescript-eslint/no-shadow
+ constructor(urlVal: URL | string) {
super();
- if (typeof url === 'string') {
- url = url.startsWith('http') ? new URL(url) : new URL(url, window.location.origin);
- }
- this.url = url;
+ this.url =
+ typeof urlVal !== 'string'
+ ? urlVal // it's an URL
+ : urlVal.startsWith('http')
+ ? new URL(urlVal)
+ : new URL(urlVal, window.location.origin);
}
[ToScriptString]() {
- if (Utils.prepend(this.url?.pathname) === this.url?.href) {
+ if (ClientUtils.prepend(this.url?.pathname) === this.url?.href) {
return `new ${this.constructor.name}("${this.url.pathname}")`;
}
return `new ${this.constructor.name}("${this.url?.href}")`;
}
[ToJavascriptString]() {
- if (Utils.prepend(this.url?.pathname) === this.url?.href) {
+ if (ClientUtils.prepend(this.url?.pathname) === this.url?.href) {
return `new ${this.constructor.name}("${this.url.pathname}")`;
}
return `new ${this.constructor.name}("${this.url?.href}")`;
}
[ToString]() {
- if (Utils.prepend(this.url?.pathname) === this.url?.href) {
+ if (ClientUtils.prepend(this.url?.pathname) === this.url?.href) {
return this.url.pathname;
}
return this.url?.href;
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 1cacfe30c..335683270 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -86,7 +86,6 @@ export const documentSchema = createSchema({
followLinkLocation: 'string', // flag for where to place content when following a click interaction (e.g., add:right, lightbox, default, )
hideLinkButton: 'boolean', // whether the blue link counter button should be hidden
layout_hideAllLinks: 'boolean', // whether all individual blue anchor dots should be hidden
- link_displayLine: 'boolean', // whether a link connection should be shown between link anchor endpoints.
isLightbox: 'boolean', // whether the marked object will display addDocTab() calls that target "lightbox" destinations
layers: listSpec('string'), // which layers the document is part of
_lockedPosition: 'boolean', // whether the document can be moved (dragged)
@@ -104,7 +103,7 @@ export const documentSchema = createSchema({
export const collectionSchema = createSchema({
childLayoutTemplate: Doc, // layout template to use to render children of a collecion
- childLayoutString: 'string', //layout string to use to render children of a collection
+ childLayoutString: 'string', // layout string to use to render children of a collection
childClickedOpenTemplateView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to read this value and apply template)
childDontRegisterViews: 'boolean', // whether views made of this document are registered so that they can be found when drawing links
onChildClick: ScriptField, // script to run for each child when its clicked
@@ -113,4 +112,5 @@ export const collectionSchema = createSchema({
});
export type Document = makeInterface<[typeof documentSchema]>;
+// eslint-disable-next-line no-redeclare
export const Document = makeInterface(documentSchema);
diff --git a/src/fields/util.ts b/src/fields/util.ts
index ad592391e..9361430cb 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -1,12 +1,11 @@
-import { $mobx, action, observable, runInAction, trace, values } from 'mobx';
+import { $mobx, action, observable, runInAction, trace } from 'mobx';
import { computedFn } from 'mobx-utils';
-import { returnZero } from '../Utils';
+import { ClientUtils, returnZero } from '../ClientUtils';
import { DocServer } from '../client/DocServer';
-import { LinkManager } from '../client/util/LinkManager';
import { SerializationHelper } from '../client/util/SerializationHelper';
import { UndoManager } from '../client/util/UndoManager';
-import { Doc, DocListCast, Field, FieldResult, HierarchyMapping, ReverseHierarchyMap, StrListCast, aclLevel, updateCachedAcls } from './Doc';
-import { AclAdmin, AclAugment, AclEdit, AclPrivate, DocAcl, DocData, DocLayout, FieldKeys, ForceServerWrite, Height, Initializing, SelfProxy, UpdatingFromServer, Width } from './DocSymbols';
+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';
@@ -16,24 +15,44 @@ import { RichTextField } from './RichTextField';
import { SchemaHeaderField } from './SchemaHeaderField';
import { ComputedField } from './ScriptField';
import { DocCast, ScriptCast, StrCast } from './Types';
-import { BaseException } from 'pdfjs-dist/types/src/shared/util';
+
+/**
+ * These are the various levels of access a user can have to a document.
+ *
+ * Admin: a user with admin access to a document can remove/edit that document, add/remove/edit annotations (depending on permissions), as well as change others' access rights to that document.
+ * Edit: a user with edit access to a document can remove/edit that document, add/remove/edit annotations (depending on permissions), but not change any access rights to that document.
+ * Add: a user with add access to a document can augment documents/annotations to that document but cannot edit or delete anything.
+ * View: a user with view access to a document can only view it - they cannot add/remove/edit anything.
+ * None: the document is not shared with that user.
+ * Unset: Remove a sharing permission (eg., used )
+ */
+export enum SharingPermissions {
+ Admin = 'Admin',
+ Edit = 'Edit',
+ Augment = 'Augment',
+ View = 'View',
+ None = 'Not-Shared',
+}
function _readOnlySetter(): never {
throw new Error("Documents can't be modified in read-only mode");
}
-var tracing = false;
+// eslint-disable-next-line prefer-const
+let tracing = false;
export function TraceMobx() {
tracing && trace();
}
-const _setterImpl = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean {
+export const _propSetterCB = new Map<string, ((target: any, value: any) => void) | undefined>();
+
+const _setterImpl = action((target: any, prop: string | symbol | number, valueIn: any, receiver: any): boolean => {
if (SerializationHelper.IsSerializing() || typeof prop === 'symbol') {
- target[prop] = value;
+ target[prop] = valueIn;
return true;
}
- value = value?.[SelfProxy] ?? value; // convert any Doc type values to Proxy's
+ let value = valueIn?.[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])) {
@@ -50,6 +69,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
throw new Error("Can't put the same object in multiple documents at the same time");
}
value[Parent] = receiver;
+ // eslint-disable-next-line no-use-before-define
value[FieldChanged] = containedFieldChangedHandler(receiver, prop, value);
}
if (curValue instanceof ObjectField) {
@@ -59,11 +79,12 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
if (typeof prop === 'string' && _propSetterCB.has(prop)) _propSetterCB.get(prop)!(target[SelfProxy], value);
+ // eslint-disable-next-line no-use-before-define
const effectiveAcl = GetEffectiveAcl(target);
const writeMode = DocServer.getFieldWriteMode(prop as string);
const fromServer = target[UpdatingFromServer];
- const sameAuthor = fromServer || receiver.author === Doc.CurrentUserEmail;
+ const sameAuthor = fromServer || receiver.author === ClientUtils.CurrentUserEmail();
const writeToDoc =
sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode === DocServer.WriteMode.Playground || writeMode === DocServer.WriteMode.LivePlayground || (effectiveAcl === AclAugment && value instanceof RichTextField);
const writeToServer =
@@ -86,7 +107,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
if (value === undefined)
(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}});
- if (prop === 'author' || prop.toString().startsWith('acl')) updateCachedAcls(target);
+ if (prop === 'author' || prop.toString().startsWith('acl_')) updateCachedAcls(target);
} else {
DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue);
}
@@ -95,7 +116,9 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
(!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) &&
UndoManager.AddEvent(
{
- redo: () => (receiver[prop] = value),
+ redo: () => {
+ receiver[prop] = value;
+ },
undo: () => {
const wasUpdate = receiver[UpdatingFromServer];
const wasForce = receiver[ForceServerWrite];
@@ -131,57 +154,16 @@ export function denormalizeEmail(email: string) {
return email.replace(/__/g, '.');
}
-/**
- * Copies parent's acl fields to the child
- */
-export function inheritParentAcls(parent: Doc, child: Doc, layoutOnly: boolean) {
- [...Object.keys(parent), ...(Doc.CurrentUserEmail !== parent.author ? ['acl-Owner'] : [])]
- .filter(key => key.startsWith('acl'))
- .forEach(key => {
- // if the default acl mode is private, then don't inherit the acl-guest permission, but set it to private.
- // const permission: string = key === 'acl-guest' && Doc.defaultAclPrivate ? AclPrivate : parent[key];
- const parAcl = ReverseHierarchyMap.get(StrCast(key === 'acl-Owner' ? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Edit) : parent[key]))?.acl;
- if (parAcl) {
- const sharePermission = HierarchyMapping.get(parAcl)?.name;
- sharePermission && distributeAcls(key === 'acl-Owner' ? `acl-${normalizeEmail(StrCast(parent.author))}` : key, sharePermission, child, undefined, false, layoutOnly);
- }
- });
-}
-
-/**
- * These are the various levels of access a user can have to a document.
- *
- * Admin: a user with admin access to a document can remove/edit that document, add/remove/edit annotations (depending on permissions), as well as change others' access rights to that document.
- *
- * Edit: a user with edit access to a document can remove/edit that document, add/remove/edit annotations (depending on permissions), but not change any access rights to that document.
- *
- * Add: a user with add access to a document can augment documents/annotations to that document but cannot edit or delete anything.
- *
- * View: a user with view access to a document can only view it - they cannot add/remove/edit anything.
- *
- * None: the document is not shared with that user.
- *
- * Unset: Remove a sharing permission (eg., used )
- */
-export enum SharingPermissions {
- Admin = 'Admin',
- Edit = 'Edit',
- Augment = 'Augment',
- View = 'View',
- None = 'Not-Shared',
-}
-
// return acl from cache or cache the acl and return.
-const getEffectiveAclCache = computedFn(function (target: any, user?: string) {
- return getEffectiveAcl(target, user);
-}, true);
+// eslint-disable-next-line no-use-before-define
+const getEffectiveAclCache = computedFn((target: any, 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 {
if (!target) return AclPrivate;
- if (target[UpdatingFromServer] || Doc.CurrentUserEmail === 'guest') return AclAdmin;
+ 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)
}
@@ -191,10 +173,9 @@ export function GetPropAcl(target: any, prop: string | symbol | number) {
return GetEffectiveAcl(target);
}
-let cachedGroups = observable([] as string[]);
-const getCachedGroupByNameCache = computedFn(function (name: string) {
- return cachedGroups.includes(name);
-}, true);
+const cachedGroups = observable([] as string[]);
+const getCachedGroupByNameCache = computedFn((name: string) => cachedGroups.includes(name), true);
+
export function GetCachedGroupByName(name: string) {
return getCachedGroupByNameCache(name);
}
@@ -203,12 +184,12 @@ export function SetCachedGroups(groups: string[]) {
}
function getEffectiveAcl(target: any, user?: string): symbol {
const targetAcls = target[DocAcl];
- if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
+ if (targetAcls?.acl_Me === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
- const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
+ const userChecked = user || ClientUtils.CurrentUserEmail(); // if the current user is the author of the document / the current user is a member of the admin group
if (targetAcls && Object.keys(targetAcls).length) {
let effectiveAcl = AclPrivate;
- for (const [key, value] of Object.entries(targetAcls)) {
+ Object.entries(targetAcls).forEach(([key, value]) => {
// there are issues with storing fields with . in the name, so they are replaced with _ during creation
// as a result we need to restore them again during this comparison.
const entity = denormalizeEmail(key.substring(4)); // an individual or a group
@@ -217,7 +198,7 @@ function getEffectiveAcl(target: any, user?: string): symbol {
effectiveAcl = value as symbol;
}
}
- }
+ });
return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl;
}
@@ -229,16 +210,16 @@ function getEffectiveAcl(target: any, user?: string): symbol {
/**
* Recursively distributes the access right for a user across the children of a document and its annotations.
- * @param key the key storing the access right (e.g. acl-groupname)
+ * @param key the key storing the access right (e.g. acl_groupname)
* @param acl the access right being stored (e.g. "Can Edit")
* @param target the document on which this access right is being set
* @param visited list of Doc's already distributed to.
* @param allowUpgrade whether permissions can be made less restrictive
* @param layoutOnly just sets the layout doc's ACL (unless the data doc has no entry for the ACL, in which case it will be set as well)
*/
-export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, visited?: Doc[], allowUpgrade?: boolean, layoutOnly = false) {
- const selfKey = `acl-${normalizeEmail(Doc.CurrentUserEmail)}`;
- if (!visited) visited = [] as Doc[];
+// eslint-disable-next-line default-param-last
+export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, visited: Doc[] = [], allowUpgrade?: boolean, layoutOnly = false) {
+ const selfKey = `acl_${normalizeEmail(ClientUtils.CurrentUserEmail())}`;
if (!target || visited.includes(target) || key === selfKey) return;
visited.push(target);
@@ -249,23 +230,21 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
if (!layoutOnly && dataDoc && (allowUpgrade !== false || !dataDoc[key] || curVal > aclVal)) {
// propagate ACLs to links, children, and annotations
- LinkManager.Links(dataDoc).forEach(link => distributeAcls(key, acl, link, visited, allowUpgrade ? true : false));
+ dataDoc[DirectLinks].forEach(link => distributeAcls(key, acl, link, visited, !!allowUpgrade));
DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => {
- distributeAcls(key, acl, d, visited, allowUpgrade ? true : false);
- d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade ? true : false);
+ distributeAcls(key, acl, d, visited, !!allowUpgrade);
+ d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, !!allowUpgrade);
});
DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '_annotations']).forEach(d => {
- distributeAcls(key, acl, d, visited, allowUpgrade ? true : false);
- d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade ? true : false);
+ distributeAcls(key, acl, d, visited, !!allowUpgrade);
+ d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, !!allowUpgrade);
});
Object.keys(target) // share expanded layout templates (eg, for presElementBox'es )
.filter(lkey => lkey.includes('layout[') && DocCast(target[lkey]))
- .map(lkey => {
- distributeAcls(key, acl, DocCast(target[lkey]), visited, allowUpgrade ? true : false);
- });
+ .forEach(lkey => distributeAcls(key, acl, DocCast(target[lkey]), visited, !!allowUpgrade));
if (GetEffectiveAcl(dataDoc) === AclAdmin) {
dataDoc[key] = acl;
@@ -285,30 +264,46 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
dataDocChanged && updateCachedAcls(dataDoc);
}
-export var _propSetterCB = new Map<string, ((target: any, value: any) => void) | undefined>();
+/**
+ * Copies parent's acl fields to the child
+ */
+export function inheritParentAcls(parent: Doc, child: Doc, layoutOnly: boolean) {
+ [...Object.keys(parent), ...(ClientUtils.CurrentUserEmail() !== parent.author ? ['acl_Owner'] : [])]
+ .filter(key => key.startsWith('acl_'))
+ .forEach(key => {
+ // if the default acl mode is private, then don't inherit the acl_guest permission, but set it to private.
+ // const permission: string = key === 'acl_Guest' && Doc.defaultAclPrivate ? AclPrivate : parent[key];
+ const parAcl = ReverseHierarchyMap.get(StrCast(key === 'acl_Owner' ? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Edit) : parent[key]))?.acl;
+ if (parAcl) {
+ const sharePermission = HierarchyMapping.get(parAcl)?.name;
+ sharePermission && distributeAcls(key === 'acl_Owner' ? `acl_${normalizeEmail(StrCast(parent.author))}` : key, sharePermission, child, undefined, false, layoutOnly);
+ }
+ });
+}
+
/**
* sets a callback function to be called whenever a value is assigned to the specified field key.
* For example, this is used to "publish" documents with titles that start with '@'
* @param prop
- * @param setter
+ * @param propSetter
*/
-export function SetPropSetterCb(prop: string, setter: ((target: any, value: any) => void) | undefined) {
- _propSetterCB.set(prop, setter);
+export function SetPropSetterCb(prop: string, propSetter: ((target: any, value: any) => 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, in_prop: string | symbol | number, value: any, receiver: any): boolean {
- if (!in_prop) {
+export function setter(target: any, inProp: string | symbol | number, value: any, receiver: any): boolean {
+ if (!inProp) {
console.log('WARNING: trying to set an empty property. This should be fixed. ');
return false;
}
- let prop = in_prop;
- const effectiveAcl = in_prop === 'constructor' || typeof in_prop === 'symbol' ? AclAdmin : GetPropAcl(target, prop);
+ let prop = inProp;
+ 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))) return true;
if (typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('_')) {
if (!prop.startsWith('__')) prop = prop.substring(1);
@@ -319,12 +314,24 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
}
if (target.__fieldTuples[prop] instanceof ComputedField) {
if (target.__fieldTuples[prop].setterscript && value !== undefined && !(value instanceof ComputedField)) {
- return ScriptCast(target.__fieldTuples[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false;
+ return !!ScriptCast(target.__fieldTuples[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success;
}
}
return _setter(target, prop, value, receiver);
}
+function getFieldImpl(target: any, prop: string | number, proxy: any, ignoreProto: boolean = false): any {
+ const field = target.__fieldTuples[prop];
+ const value = field?.[ToValue]?.(proxy); // converts ComputedFields to values, or unpacks ProxyFields into Proxys
+ if (value) return value.value;
+ if (field === undefined && !ignoreProto && prop !== 'proto') {
+ const proto = getFieldImpl(target, 'proto', proxy, true); // TODO tfs: instead of proxy we could use target[SelfProxy]... I don't which semantics we want or if it really matters
+ if (proto instanceof Doc && GetEffectiveAcl(proto) !== AclPrivate) {
+ return getFieldImpl(proto, prop, proxy, ignoreProto);
+ }
+ }
+ return field;
+}
export function getter(target: any, prop: string | symbol, proxy: any): any {
// prettier-ignore
switch (prop) {
@@ -336,6 +343,7 @@ export function getter(target: any, prop: string | symbol, proxy: any): any {
case $mobx: return target.__fieldTuples[prop];
case DocLayout: return target.__LAYOUT__;
case Height: case Width: if (GetEffectiveAcl(target) === AclPrivate) return returnZero;
+ // eslint-disable-next-line no-fallthrough
default :
if (typeof prop === 'symbol') return target[prop];
if (prop.startsWith('isMobX')) return target[prop];
@@ -343,23 +351,11 @@ export function getter(target: any, prop: string | symbol, proxy: any): any {
if (GetEffectiveAcl(target) === AclPrivate && prop !== 'author') return undefined;
}
- const layout_prop = prop.startsWith('_') ? prop.substring(1) : undefined;
- if (layout_prop && target.__LAYOUT__) return target.__LAYOUT__[layout_prop];
- return getFieldImpl(target, layout_prop ?? prop, proxy);
+ const layoutProp = prop.startsWith('_') ? prop.substring(1) : undefined;
+ if (layoutProp && target.__LAYOUT__) return target.__LAYOUT__[layoutProp];
+ return getFieldImpl(target, layoutProp ?? prop, proxy);
}
-function getFieldImpl(target: any, prop: string | number, proxy: any, ignoreProto: boolean = false): any {
- const field = target.__fieldTuples[prop];
- const value = field?.[ToValue]?.(proxy); // converts ComputedFields to values, or unpacks ProxyFields into Proxys
- if (value) return value.value;
- if (field === undefined && !ignoreProto && prop !== 'proto') {
- const proto = getFieldImpl(target, 'proto', proxy, true); //TODO tfs: instead of proxy we could use target[SelfProxy]... I don't which semantics we want or if it really matters
- if (proto instanceof Doc && GetEffectiveAcl(proto) !== AclPrivate) {
- return getFieldImpl(proto, prop, proxy, ignoreProto);
- }
- }
- return field;
-}
export function getField(target: any, prop: string | number, ignoreProto: boolean = false): any {
return getFieldImpl(target, prop, target[SelfProxy], ignoreProto);
}
@@ -382,10 +378,10 @@ 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<Field> | Doc, prop: string | number, liveContainedField: ObjectField) {
+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: Field[] | undefined; length: number | undefined; hint?: any }, dummyServerOp?: any) => {
- const serializeItems = () => ({ __type: 'list', fields: diff?.items?.map((item: Field) => SerializationHelper.Serialize(item)) });
+ 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)) });
// prettier-ignore
const serverOp = diff?.op === '$addToSet'
? { $addToSet: { ['fields.' + prop]: serializeItems() }, length: diff.length }
@@ -401,8 +397,8 @@ export function containedFieldChangedHandler(container: List<Field> | Doc, prop:
UndoManager.AddEvent(
{
redo: () => {
- //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));
+ // 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]);
},
undo: action(() => {
@@ -416,7 +412,7 @@ export function containedFieldChangedHandler(container: List<Field> | Doc, prop:
});
lastValue = ObjectField.MakeCopy((container as any)[prop as any]);
}),
- prop: 'add ' + diff.items?.length + ' items to list',
+ prop: 'add ' + (diff.items?.length ?? 0) + ' items to list',
},
diff?.items
);
@@ -447,12 +443,14 @@ export function containedFieldChangedHandler(container: List<Field> | Doc, prop:
});
lastValue = ObjectField.MakeCopy((container as any)[prop as any]);
},
- prop: 'remove ' + diff.items?.length + ' items from list',
+ prop: 'remove ' + (diff.items?.length ?? 0) + ' items from list',
},
diff?.items
);
} else {
- const setFieldVal = (val: Field | undefined) => (container instanceof Doc ? (container[prop as string] = val) : (container[prop as number] = val as Field));
+ const setFieldVal = (val: FieldType | undefined) => {
+ container instanceof Doc ? (container[prop as string] = val) : (container[prop as number] = val as FieldType);
+ };
UndoManager.AddEvent(
{
redo: () => {