aboutsummaryrefslogtreecommitdiff
path: root/src/fields
diff options
context:
space:
mode:
Diffstat (limited to 'src/fields')
-rw-r--r--src/fields/Doc.ts282
-rw-r--r--src/fields/DocSymbols.ts26
-rw-r--r--src/fields/FieldSymbols.ts21
-rw-r--r--src/fields/IconField.ts26
-rw-r--r--src/fields/List.ts92
-rw-r--r--src/fields/ListSpec.ts0
-rw-r--r--src/fields/PresField.ts6
-rw-r--r--src/fields/Proxy.ts4
-rw-r--r--src/fields/Schema.ts78
-rw-r--r--src/fields/documentSchemas.ts24
-rw-r--r--src/fields/util.ts94
11 files changed, 313 insertions, 340 deletions
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index d0e5d03b3..f13dab68c 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -11,9 +11,38 @@ import { SelectionManager } from '../client/util/SelectionManager';
import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from '../client/util/SerializationHelper';
import { undoable, UndoManager } from '../client/util/UndoManager';
import { decycle } from '../decycler/decycler';
+import * as JSZipUtils from '../JSZipUtils';
import { DashColor, incrementTitleCopy, intersectRect, Utils } from '../Utils';
import { DateField } from './DateField';
-import { Copy, HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from './FieldSymbols';
+import {
+ AclAdmin,
+ AclAugment,
+ AclEdit,
+ AclPrivate,
+ AclReadonly,
+ AclSelfEdit,
+ AclUnset,
+ Animation,
+ CachedUpdates,
+ DirectLinks,
+ DocAcl,
+ DocCss,
+ DocData,
+ DocFields,
+ DocLayout,
+ FieldKeys,
+ FieldTuples,
+ ForceServerWrite,
+ Height,
+ Highlight,
+ Initializing,
+ Self,
+ SelfProxy,
+ Update,
+ UpdatingFromServer,
+ Width,
+} from './DocSymbols';
+import { Copy, HandleUpdate, Id, OnUpdate, Parent, ToScriptString, ToString } from './FieldSymbols';
import { InkField, InkTool } from './InkField';
import { List, ListFieldName } from './List';
import { ObjectField } from './ObjectField';
@@ -26,7 +55,6 @@ import { Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Ty
import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField';
import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from './util';
import JSZip = require('jszip');
-import * as JSZipUtils from '../JSZipUtils';
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
const onDelegate = Object.keys(doc).includes(key.replace(/^_/, ''));
@@ -93,28 +121,6 @@ export function DocListCast(field: FieldResult, defaultVal: Doc[] = []) {
return Cast(field, listSpec(Doc), defaultVal).filter(d => d instanceof Doc) as Doc[];
}
-export const WidthSym = Symbol('Width');
-export const HeightSym = Symbol('Height');
-export const AnimationSym = Symbol('Animation');
-export const HighlightSym = Symbol('Highlight');
-export const DataSym = Symbol('Data');
-export const LayoutSym = Symbol('Layout');
-export const FieldsSym = Symbol('Fields');
-export const CssSym = Symbol('Css');
-export const AclSym = Symbol('Acl');
-export const DirectLinksSym = Symbol('DirectLinks');
-export const AclUnset = Symbol('AclUnset');
-export const AclPrivate = Symbol('AclOwnerOnly');
-export const AclReadonly = Symbol('AclReadOnly');
-export const AclAugment = Symbol('AclAugment');
-export const AclSelfEdit = Symbol('AclSelfEdit');
-export const AclEdit = Symbol('AclEdit');
-export const AclAdmin = Symbol('AclAdmin');
-export const UpdatingFromServer = Symbol('UpdatingFromServer');
-export const Initializing = Symbol('Initializing');
-export const ForceServerWrite = Symbol('ForceServerWrite');
-export const CachedUpdates = Symbol('Cached updates');
-
export enum aclLevel {
unset = -1,
unshared = 0,
@@ -141,15 +147,15 @@ export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol }>
export function updateCachedAcls(doc: Doc) {
if (!doc) return;
- const target = (doc as any)?.__fields ?? doc;
+ 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[AclSym]?.length) {
- runInAction(() => (doc[AclSym] = permissions));
+ if (Object.keys(permissions).length || doc[DocAcl]?.length) {
+ runInAction(() => (doc[DocAcl] = permissions));
}
if (doc.proto instanceof Promise) {
- doc.proto.then(updateCachedAcls);
+ doc.proto.then(proto => updateCachedAcls(DocCast(proto)));
return doc.proto;
}
}
@@ -295,25 +301,20 @@ export class Doc extends RefField {
set: setter,
get: getter,
// getPrototypeOf: (target) => Cast(target[SelfProxy].proto, Doc) || null, // TODO this might be able to replace the proto logic in getter
- has: (target, key) => GetEffectiveAcl(target) !== AclPrivate && key in target.__fields,
+ has: (target, key) => GetEffectiveAcl(target) !== AclPrivate && key in target.__fieldTuples,
ownKeys: target => {
- const obj = {} as any;
- if (GetEffectiveAcl(target) !== AclPrivate) Object.assign(obj, target.___fieldKeys);
- runInAction(() => (obj.__LAYOUT__ = target.__LAYOUT__));
- return Object.keys(obj);
+ const keys = GetEffectiveAcl(target) !== AclPrivate ? Object.keys(target[FieldKeys]) : [];
+ return [...keys, '__LAYOUT__'];
},
getOwnPropertyDescriptor: (target, prop) => {
- if (prop.toString() === '__LAYOUT__') {
+ if (prop.toString() === '__LAYOUT__' || !(prop in target[FieldKeys])) {
return Reflect.getOwnPropertyDescriptor(target, prop);
}
- if (prop in target.__fieldKeys) {
- return {
- configurable: true, //TODO Should configurable be true?
- enumerable: true,
- value: 0, //() => target.__fields[prop])
- };
- }
- return Reflect.getOwnPropertyDescriptor(target, prop);
+ return {
+ configurable: true, //TODO Should configurable be true?
+ enumerable: true,
+ value: 0, //() => target.__fieldTuples[prop])
+ };
},
deleteProperty: deleteProperty,
defineProperty: () => {
@@ -327,41 +328,35 @@ export class Doc extends RefField {
return docProxy;
}
- proto: Opt<Doc>;
[key: string]: FieldResult;
@serializable(alias('fields', map(autoObject(), { afterDeserialize: afterDocDeserialize })))
- private get __fields() {
- return this.___fields;
+ private get __fieldTuples() {
+ return this[FieldTuples];
}
- private set __fields(value) {
- this.___fields = value;
+ 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);
+ field !== undefined && (this[FieldKeys][key] = true);
if (!(field instanceof ObjectField)) continue;
field[Parent] = this[Self];
field[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]);
}
}
- private get __fieldKeys() {
- return this.___fieldKeys;
- }
- private set __fieldKeys(value) {
- this.___fieldKeys = value;
- }
- @observable private ___fields: any = {};
- @observable private ___fieldKeys: any = {};
+ @observable private [FieldTuples]: any = {};
+ @observable private [FieldKeys]: any = {};
/// all of the raw acl's that have been set on this document. Use GetEffectiveAcl to determine the actual ACL of the doc for editing
- @observable public [AclSym]: { [key: string]: symbol } = {};
- @observable public [CssSym]: number = 0; // incrementer denoting a change to CSS layout
- @observable public [DirectLinksSym]: Set<Doc> = new Set();
- @observable public [AnimationSym]: Opt<Doc>;
- @observable public [HighlightSym]: boolean = false;
+ @observable public [DocAcl]: { [key: string]: symbol } = {};
+ @observable public [DocCss]: number = 0; // incrementer denoting a change to CSS layout
+ @observable public [DirectLinks]: Set<Doc> = new Set();
+ @observable public [Animation]: Opt<Doc>;
+ @observable public [Highlight]: boolean = false;
static __Anim(Doc: Doc) {
// for debugging to print AnimationSym field easily.
- return Doc[AnimationSym];
+ return Doc[Animation];
}
private [UpdatingFromServer]: boolean = false;
@@ -374,15 +369,15 @@ export class Doc extends RefField {
private [Self] = this;
private [SelfProxy]: any;
- public [FieldsSym] = () => this[Self].___fields; // Object.keys(this).reduce((fields, key) => { fields[key] = this[key]; return fields; }, {} as any);
- public [WidthSym] = () => NumCast(this[SelfProxy]._width);
- public [HeightSym] = () => NumCast(this[SelfProxy]._height);
+ public [DocFields] = () => this[Self][FieldTuples]; // Object.keys(this).reduce((fields, key) => { fields[key] = this[key]; return fields; }, {} as any);
+ public [Width] = () => NumCast(this[SelfProxy]._width);
+ public [Height] = () => NumCast(this[SelfProxy]._height);
public [ToScriptString] = () => `idToDoc("${this[Self][Id]}")`;
public [ToString] = () => `Doc(${GetEffectiveAcl(this[SelfProxy]) === AclPrivate ? '-inaccessible-' : this[SelfProxy].title})`;
- public get [LayoutSym]() {
+ public get [DocLayout]() {
return this[SelfProxy].__LAYOUT__;
}
- public get [DataSym](): Doc {
+ public get [DocData](): Doc {
const self = this[SelfProxy];
return self.resolvedDataDoc && !self.isTemplateForField ? self : Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self);
}
@@ -535,17 +530,17 @@ export namespace Doc {
if (key.startsWith('_')) key = key.substring(1);
const hasProto = Doc.GetProto(doc) !== doc ? Doc.GetProto(doc) : undefined;
const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
- const onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1;
+ const onProto = hasProto && Object.getOwnPropertyNames(hasProto).indexOf(key) !== -1;
if (onDeleg || !hasProto || (!onProto && !defaultProto)) {
doc[key] = value;
- } else doc.proto![key] = value;
+ } else hasProto[key] = value;
}
export function GetAllPrototypes(doc: Doc): Doc[] {
const protos: Doc[] = [];
let d: Opt<Doc> = doc;
while (d) {
protos.push(d);
- d = FieldValue(d.proto);
+ d = DocCast(FieldValue(d.proto));
}
return protos;
}
@@ -585,7 +580,7 @@ export namespace Doc {
// return the doc's proto, but rather recursively searches through the proto inheritance chain
// and returns the document who's proto is undefined or whose proto is marked as a data doc ('isDataDoc').
export function GetProto(doc: Doc): Doc {
- const proto = doc && (Doc.GetT(doc, 'isDataDoc', 'boolean', true) ? doc : doc.proto || doc);
+ const proto = doc && (Doc.GetT(doc, 'isDataDoc', 'boolean', true) ? doc : DocCast(doc.proto, doc));
return proto === doc ? proto : Doc.GetProto(proto);
}
export function GetDataDoc(doc: Doc): Doc {
@@ -599,7 +594,7 @@ export namespace Doc {
let proto: Doc | undefined = doc;
while (proto) {
Object.keys(proto).forEach(key => results.add(key));
- proto = proto.proto;
+ proto = DocCast(FieldValue(proto.proto));
}
return Array.from(results);
@@ -676,7 +671,7 @@ export namespace Doc {
const bounds = docList.reduce(
(bounds, doc) => {
const [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
- const [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
+ const [bptX, bptY] = [sptX + doc[Width](), sptY + doc[Height]()];
return {
x: Math.min(sptX, bounds.x),
y: Math.min(sptY, bounds.y),
@@ -700,7 +695,7 @@ export namespace Doc {
embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`);
embedding.author = Doc.CurrentUserEmail;
- Doc.AddDocToList(Doc.GetProto(doc)[DataSym], 'proto_embeddings', embedding);
+ Doc.AddDocToList(Doc.GetProto(doc)[DocData], 'proto_embeddings', embedding);
return embedding;
}
@@ -772,7 +767,7 @@ export namespace Doc {
}
})
);
- Array.from(doc[DirectLinksSym]).forEach(async link => {
+ Array.from(doc[DirectLinks]).forEach(async link => {
if (
cloneLinks ||
((cloneMap.has(DocCast(link.link_anchor_1)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_1)?.annotationOn)?.[Id])) &&
@@ -781,7 +776,7 @@ export namespace Doc {
linkMap.set(link[Id], await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks));
}
});
- Doc.SetInPlace(copy, 'title', 'CLONE: ' + doc.title, true);
+ Doc.SetInPlace(copy, 'title', '>:' + doc.title, true);
copy.cloneOf = doc;
cloneMap.set(doc[Id], copy);
@@ -930,7 +925,7 @@ export namespace Doc {
if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc))) {
expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params
} else {
- templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = Cast(templateLayoutDoc.proto, Doc, null) || templateLayoutDoc); // if the template has already been applied (ie, a nested template), then use the template's prototype
+ 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.set(targetDoc[Id] + expandedLayoutFieldKey, true);
setTimeout(
@@ -969,7 +964,7 @@ export namespace Doc {
const field = ProxyField.WithoutProxy(() => doc[key]);
if (key === 'proto' && copyProto) {
if (doc.proto instanceof Doc && overwrite.proto instanceof Doc) {
- overwrite[key] = Doc.Overwrite(doc[key]!, overwrite.proto);
+ overwrite[key] = Doc.Overwrite(doc.proto, overwrite.proto);
}
} else {
if (field instanceof RefField) {
@@ -991,38 +986,37 @@ export namespace Doc {
const copy = new Doc(copyProtoId, true);
updateCachedAcls(copy);
const exclude = [...StrListCast(doc.cloneFieldFilter), 'dragFactory_count', 'cloneFieldFilter'];
- Object.keys(doc).forEach(key => {
- if (exclude.includes(key)) return;
- const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
- const field = key === 'author' ? Doc.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]);
- if (key === 'proto' && copyProto) {
- if (doc[key] instanceof Doc) {
- copy[key] = Doc.MakeCopy(doc[key]!, false);
- }
- } else {
- if (field instanceof RefField) {
- copy[key] = field;
- } else if (cfield instanceof ComputedField) {
- copy[key] = cfield[Copy](); // ComputedField.MakeFunction(cfield.script.originalScript);
- } else if (field instanceof ObjectField) {
- copy[key] =
- doc[key] instanceof Doc
- ? key.includes('layout[')
- ? undefined
- : doc[key] // reference documents except remove documents that are expanded teplate fields
- : ObjectField.MakeCopy(field);
- } else if (field instanceof Promise) {
- debugger; //This shouldn't happend...
+ Object.keys(doc)
+ .filter(key => !exclude.includes(key))
+ .forEach(key => {
+ if (key === 'proto' && copyProto) {
+ if (doc.proto instanceof Doc) {
+ copy[key] = Doc.MakeCopy(doc.proto, false);
+ }
} else {
- copy[key] = field;
+ const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+ const field = key === 'author' ? Doc.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]);
+ if (field instanceof RefField) {
+ copy[key] = field;
+ } else if (cfield instanceof ComputedField) {
+ copy[key] = cfield[Copy](); // ComputedField.MakeFunction(cfield.script.originalScript);
+ } else if (field instanceof ObjectField) {
+ copy[key] =
+ doc[key] instanceof Doc && key.includes('layout[')
+ ? undefined // remove expanded template field documents
+ : ObjectField.MakeCopy(field);
+ } else if (field instanceof Promise) {
+ debugger; //This shouldn't happend...
+ } else {
+ copy[key] = field;
+ }
}
- }
- });
+ });
if (copyProto) {
Doc.GetProto(copy).embedContainer = undefined;
Doc.GetProto(copy).proto_embeddings = new List<Doc>([copy]);
} else {
- Doc.AddDocToList(Doc.GetProto(copy)[DataSym], 'proto_embeddings', copy);
+ Doc.AddDocToList(Doc.GetProto(copy)[DocData], 'proto_embeddings', copy);
}
copy.embedContainer = undefined;
Doc.defaultAclPrivate && (copy['acl-Public'] = 'Not Shared');
@@ -1045,7 +1039,7 @@ export namespace Doc {
Object.keys(doc)
.filter(key => key.startsWith('acl'))
.forEach(key => (delegate[key] = doc[key]));
- if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DataSym], 'proto_embeddings', delegate);
+ if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DocData], 'proto_embeddings', delegate);
title && (delegate.title = title);
delegate[Initializing] = false;
Doc.AddFileOrphan(delegate);
@@ -1069,7 +1063,7 @@ export namespace Doc {
delegate[Initializing] = true;
delegate.proto = delegateProto;
delegate.author = Doc.CurrentUserEmail;
- Doc.AddDocToList(delegateProto[DataSym], 'proto_embeddings', delegate);
+ Doc.AddDocToList(delegateProto[DocData], 'proto_embeddings', delegate);
delegate[Initializing] = false;
delegateProto[Initializing] = false;
return delegate;
@@ -1173,7 +1167,7 @@ export namespace Doc {
}
export const brushManager = new DocBrush();
- export class DocData {
+ export class UserDocData {
@observable _user_doc: Doc = undefined!;
@observable _sharing_doc: Doc = undefined!;
@observable _searchQuery: string = '';
@@ -1183,7 +1177,7 @@ export namespace Doc {
// a layout field or 'layout' is given.
export function Layout(doc: Doc, layout?: Doc): Doc {
const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, 'data')}-layout[` + layout[Id] + ']'], Doc, null);
- return overrideLayout || doc[LayoutSym] || doc;
+ return overrideLayout || doc[DocLayout] || doc;
}
export function SetLayout(doc: Doc, layout: Doc | string) {
doc[StrCast(doc.layout_fieldKey, 'layout')] = layout;
@@ -1198,12 +1192,12 @@ export namespace Doc {
return Doc.NativeWidth(doc, dataDoc, useDim) / (Doc.NativeHeight(doc, dataDoc, useDim) || 1);
}
export function NativeWidth(doc?: Doc, dataDoc?: Doc, useWidth?: boolean) {
- return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeWidth'], useWidth ? doc[WidthSym]() : 0));
+ return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeWidth'], useWidth ? doc[Width]() : 0));
}
export function NativeHeight(doc?: Doc, dataDoc?: Doc, useHeight?: boolean) {
if (!doc) return 0;
- const nheight = (Doc.NativeWidth(doc, dataDoc, useHeight) * doc[HeightSym]()) / doc[WidthSym]();
- const dheight = NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeHeight'], useHeight ? doc[HeightSym]() : 0);
+ const nheight = (Doc.NativeWidth(doc, dataDoc, useHeight) * doc[Height]()) / doc[Width]();
+ const dheight = NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeHeight'], useHeight ? doc[Height]() : 0);
return NumCast(doc._nativeHeight, nheight || dheight);
}
export function SetNativeWidth(doc: Doc, width: number | undefined, fieldKey?: string) {
@@ -1213,7 +1207,7 @@ export namespace Doc {
doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '_nativeHeight'] = height;
}
- const manager = new DocData();
+ const manager = new UserDocData();
export function SearchQuery(): string {
return manager._searchQuery;
}
@@ -1348,16 +1342,16 @@ export namespace Doc {
export var highlightedDocs = new ObservableSet<Doc>();
export function IsHighlighted(doc: Doc) {
if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return false;
- return doc[HighlightSym] || Doc.GetProto(doc)[HighlightSym];
+ return doc[Highlight] || Doc.GetProto(doc)[Highlight];
}
export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true, presEffect?: Doc) {
runInAction(() => {
highlightedDocs.add(doc);
- doc[HighlightSym] = true;
- doc[AnimationSym] = presEffect;
+ doc[Highlight] = true;
+ doc[Animation] = presEffect;
if (dataAndDisplayDocs) {
highlightedDocs.add(Doc.GetProto(doc));
- Doc.GetProto(doc)[HighlightSym] = true;
+ Doc.GetProto(doc)[Highlight] = true;
}
});
}
@@ -1367,8 +1361,8 @@ export namespace Doc {
(doc ? [doc] : Array.from(highlightedDocs)).forEach(doc => {
highlightedDocs.delete(doc);
highlightedDocs.delete(Doc.GetProto(doc));
- doc[HighlightSym] = Doc.GetProto(doc)[HighlightSym] = false;
- doc[AnimationSym] = undefined;
+ doc[Highlight] = Doc.GetProto(doc)[Highlight] = false;
+ doc[Animation] = undefined;
});
});
}
@@ -1421,55 +1415,57 @@ export namespace Doc {
}
export function setDocRangeFilter(container: Opt<Doc>, key: string, range?: number[]) {
if (!container) return;
- const docRangeFilters = Cast(container._docRangeFilters, listSpec('string'), []);
- for (let i = 0; i < docRangeFilters.length; i += 3) {
- if (docRangeFilters[i] === key) {
- docRangeFilters.splice(i, 3);
+ const childFiltersByRanges = Cast(container._childFiltersByRanges, listSpec('string'), []);
+ for (let i = 0; i < childFiltersByRanges.length; i += 3) {
+ if (childFiltersByRanges[i] === key) {
+ childFiltersByRanges.splice(i, 3);
break;
}
}
if (range !== undefined) {
- docRangeFilters.push(key);
- docRangeFilters.push(range[0].toString());
- docRangeFilters.push(range[1].toString());
- container._docRangeFilters = new List<string>(docRangeFilters);
+ childFiltersByRanges.push(key);
+ childFiltersByRanges.push(range[0].toString());
+ childFiltersByRanges.push(range[1].toString());
+ container._childFiltersByRanges = new List<string>(childFiltersByRanges);
}
}
+ export const FilterSep = '::';
+
// filters document in a container collection:
// all documents with the specified value for the specified key are included/excluded
// based on the modifiers :"check", "x", undefined
export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) {
if (!container) return;
- const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'docFilters';
- const docFilters = StrListCast(container[filterField]);
+ const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'childFilters';
+ const childFilters = StrListCast(container[filterField]);
runInAction(() => {
- for (let i = 0; i < docFilters.length; i++) {
- const fields = docFilters[i].split(':'); // split key:value:modifier
+ for (let i = 0; i < childFilters.length; i++) {
+ const fields = childFilters[i].split(FilterSep); // split key:value:modifier
if (fields[0] === key && (fields[1] === value || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) {
if (fields[2] === modifiers && modifiers && fields[1] === value) {
if (toggle) modifiers = 'remove';
else return;
}
- docFilters.splice(i, 1);
- container[filterField] = new List<string>(docFilters);
+ childFilters.splice(i, 1);
+ container[filterField] = new List<string>(childFilters);
break;
}
}
- if (!docFilters.length && modifiers === 'match' && value === undefined) {
+ if (!childFilters.length && modifiers === 'match' && value === undefined) {
container[filterField] = undefined;
} else if (modifiers !== 'remove') {
- !append && (docFilters.length = 0);
- docFilters.push(key + ':' + value + ':' + modifiers);
- container[filterField] = new List<string>(docFilters);
+ !append && (childFilters.length = 0);
+ childFilters.push(key + FilterSep + value + FilterSep + modifiers);
+ container[filterField] = new List<string>(childFilters);
}
});
}
export function readDocRangeFilter(doc: Doc, key: string) {
- const docRangeFilters = Cast(doc._docRangeFilters, listSpec('string'), []);
- for (let i = 0; i < docRangeFilters.length; i += 3) {
- if (docRangeFilters[i] === key) {
- return [Number(docRangeFilters[i + 1]), Number(docRangeFilters[i + 2])];
+ const childFiltersByRanges = Cast(doc._childFiltersByRanges, listSpec('string'), []);
+ for (let i = 0; i < childFiltersByRanges.length; i += 3) {
+ if (childFiltersByRanges[i] === key) {
+ return [Number(childFiltersByRanges[i + 1]), Number(childFiltersByRanges[i + 2])];
}
}
}
@@ -1540,13 +1536,13 @@ export namespace Doc {
// prettier-ignore
export function toIcon(doc?: Doc, isOpen?: boolean) {
- switch (StrCast(doc?.type)) {
+ switch (isOpen !== undefined ? DocumentType.COL: StrCast(doc?.type)) {
case DocumentType.IMG: return 'image';
case DocumentType.COMPARISON: return 'columns';
case DocumentType.RTF: return 'sticky-note';
case DocumentType.COL:
- const folder: IconProp = isOpen ? 'folder-open' : 'folder';
- const chevron: IconProp = isOpen ? 'chevron-down' : 'chevron-right';
+ const folder: IconProp = isOpen === true ? 'folder-open' : isOpen === false ? 'folder' : 'question';
+ const chevron: IconProp = isOpen === true ? 'chevron-down' : isOpen === false ? 'chevron-right' : 'question';
return !doc?.isFolder ? folder : chevron;
case DocumentType.WEB: return 'globe-asia';
case DocumentType.SCREENSHOT: return 'photo-video';
diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts
new file mode 100644
index 000000000..65decc147
--- /dev/null
+++ b/src/fields/DocSymbols.ts
@@ -0,0 +1,26 @@
+export const Update = Symbol('DocUpdate');
+export const Self = Symbol('DocSelf');
+export const SelfProxy = Symbol('DocSelfProxy');
+export const FieldKeys = Symbol('DocFieldKeys');
+export const FieldTuples = Symbol('DocFieldTuples');
+export const Width = Symbol('DocWidth');
+export const Height = Symbol('DocHeight');
+export const Animation = Symbol('DocAnimation');
+export const Highlight = Symbol('DocHighlight');
+export const DocData = Symbol('DocData');
+export const DocLayout = Symbol('DocLayout');
+export const DocFields = Symbol('DocFields');
+export const DocCss = Symbol('DocCss');
+export const DocAcl = Symbol('DocAcl');
+export const DirectLinks = Symbol('DocDirectLinks');
+export const AclUnset = Symbol('DocAclUnset');
+export const AclPrivate = Symbol('DocAclOwnerOnly');
+export const AclReadonly = Symbol('DocAclReadOnly');
+export const AclAugment = Symbol('DocAclAugment');
+export const AclSelfEdit = Symbol('DocAclSelfEdit');
+export const AclEdit = Symbol('DocAclEdit');
+export const AclAdmin = Symbol('DocAclAdmin');
+export const UpdatingFromServer = Symbol('DocUpdatingFromServer');
+export const Initializing = Symbol('DocInitializing');
+export const ForceServerWrite = Symbol('DocForceServerWrite');
+export const CachedUpdates = Symbol('DocCachedUpdates');
diff --git a/src/fields/FieldSymbols.ts b/src/fields/FieldSymbols.ts
index e50c2856f..c381f14f5 100644
--- a/src/fields/FieldSymbols.ts
+++ b/src/fields/FieldSymbols.ts
@@ -1,12 +1,9 @@
-export const Update = Symbol('Update');
-export const Self = Symbol('Self');
-export const SelfProxy = Symbol('SelfProxy');
-export const HandleUpdate = Symbol('HandleUpdate');
-export const Id = Symbol('Id');
-export const OnUpdate = Symbol('OnUpdate');
-export const Parent = Symbol('Parent');
-export const Copy = Symbol('Copy');
-export const ToValue = Symbol('ToValue');
-export const ToScriptString = Symbol('ToScriptString');
-export const ToPlainText = Symbol('ToPlainText');
-export const ToString = Symbol('ToString');
+export const HandleUpdate = Symbol('FieldHandleUpdate');
+export const Id = Symbol('FieldId');
+export const OnUpdate = Symbol('FieldOnUpdate');
+export const Parent = Symbol('FieldParent');
+export const Copy = Symbol('FieldCopy');
+export const ToValue = Symbol('FieldToValue');
+export const ToScriptString = Symbol('FieldToScriptString');
+export const ToPlainText = Symbol('FieldToPlainText');
+export const ToString = Symbol('FieldToString');
diff --git a/src/fields/IconField.ts b/src/fields/IconField.ts
deleted file mode 100644
index 76c4ddf1b..000000000
--- a/src/fields/IconField.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Deserializable } from "../client/util/SerializationHelper";
-import { serializable, primitive } from "serializr";
-import { ObjectField } from "./ObjectField";
-import { Copy, ToScriptString, ToString } from "./FieldSymbols";
-
-@Deserializable("icon")
-export class IconField extends ObjectField {
- @serializable(primitive())
- readonly icon: string;
-
- constructor(icon: string) {
- super();
- this.icon = icon;
- }
-
- [Copy]() {
- return new IconField(this.icon);
- }
-
- [ToScriptString]() {
- return "invalid";
- }
- [ToString]() {
- return "ICONfield";
- }
-}
diff --git a/src/fields/List.ts b/src/fields/List.ts
index dfd24cf7a..033fa569b 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -3,8 +3,9 @@ import { alias, list, serializable } from 'serializr';
import { DocServer } from '../client/DocServer';
import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
import { afterDocDeserialize, autoObject, Deserializable } from '../client/util/SerializationHelper';
+import { FieldTuples, Self, SelfProxy, Update } from './DocSymbols';
import { Field } from './Doc';
-import { Copy, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from './FieldSymbols';
+import { Copy, OnUpdate, Parent, ToScriptString, ToString } from './FieldSymbols';
import { ObjectField } from './ObjectField';
import { ProxyField } from './Proxy';
import { RefField } from './RefField';
@@ -21,12 +22,12 @@ const listHandlers: any = {
if (value instanceof RefField) {
throw new Error('fill with RefFields not supported yet');
}
- const res = this[Self].__fields.fill(value, start, end);
+ const res = this[Self].__fieldTuples.fill(value, start, end);
this[Update]();
return res;
},
pop(): any {
- const field = toRealField(this[Self].__fields.pop());
+ const field = toRealField(this[Self].__fieldTuples.pop());
this[Update]();
return field;
},
@@ -34,7 +35,7 @@ const listHandlers: any = {
items = items.map(toObjectField);
const list = this[Self];
- const length = list.__fields.length;
+ const length = list.__fieldTuples.length;
for (let i = 0; i < items.length; i++) {
const item = items[i];
//TODO Error checking to make sure parent doesn't already exist
@@ -43,23 +44,23 @@ const listHandlers: any = {
item[OnUpdate] = updateFunction(list, i + length, item, this);
}
}
- const res = list.__fields.push(...items);
+ const res = list.__fieldTuples.push(...items);
this[Update]({ op: '$addToSet', items, length: length + items.length });
return res;
}),
reverse() {
- const res = this[Self].__fields.reverse();
+ const res = this[Self].__fieldTuples.reverse();
this[Update]();
return res;
},
shift() {
- const res = toRealField(this[Self].__fields.shift());
+ const res = toRealField(this[Self].__fieldTuples.shift());
this[Update]();
return res;
},
sort(cmpFunc: any) {
this[Self].__realFields(); // coerce retrieving entire array
- const res = this[Self].__fields.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined);
+ const res = this[Self].__fieldTuples.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined);
this[Update]();
return res;
},
@@ -67,7 +68,7 @@ const listHandlers: any = {
this[Self].__realFields(); // coerce retrieving entire array
items = items.map(toObjectField);
const list = this[Self];
- const removed = list.__fields.filter((item: any, i: number) => i >= start && i < start + deleteCount);
+ 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
@@ -77,18 +78,18 @@ const listHandlers: any = {
item[OnUpdate] = updateFunction(list, i + start, item, this);
}
}
- let hintArray: {val : any, index : number}[] = [];
- for(let i = start; i < start + deleteCount; i++) {
- hintArray.push({val : list.__fields[i], index : i});
+ let hintArray: { val: any; index: number }[] = [];
+ for (let i = start; i < start + deleteCount; i++) {
+ hintArray.push({ val: list.__fieldTuples[i], index: i });
}
- const res = list.__fields.splice(start, deleteCount, ...items);
+ const res = list.__fieldTuples.splice(start, deleteCount, ...items);
// the hint object sends the starting index of the slice and the number
- // of elements to delete.
+ // of elements to delete.
this[Update](
items.length === 0 && deleteCount
- ? { op: '$remFromSet', items: removed, hint : { start : start, deleteCount : deleteCount }, length: list.__fields.length }
- : items.length && !deleteCount && start === list.__fields.length
- ? { op: '$addToSet', items, length: list.__fields.length }
+ ? { op: '$remFromSet', items: removed, hint: { start: start, deleteCount: deleteCount }, length: list.__fieldTuples.length }
+ : items.length && !deleteCount && start === list.__fieldTuples.length
+ ? { op: '$addToSet', items, length: list.__fieldTuples.length }
: undefined
);
return res.map(toRealField);
@@ -96,7 +97,7 @@ const listHandlers: any = {
unshift(...items: any[]) {
items = items.map(toObjectField);
const list = this[Self];
- const length = list.__fields.length;
+ const length = list.__fieldTuples.length;
for (let i = 0; i < items.length; i++) {
const item = items[i];
//TODO Error checking to make sure parent doesn't already exist
@@ -106,32 +107,32 @@ const listHandlers: any = {
item[OnUpdate] = updateFunction(list, i, item, this);
}
}
- const res = this[Self].__fields.unshift(...items);
+ const res = this[Self].__fieldTuples.unshift(...items);
this[Update]();
return res;
},
/// Accessor methods
concat: action(function (this: any, ...items: any[]) {
this[Self].__realFields();
- return this[Self].__fields.map(toRealField).concat(...items);
+ return this[Self].__fieldTuples.map(toRealField).concat(...items);
}),
includes(valueToFind: any, fromIndex: number) {
if (valueToFind instanceof RefField) {
return this[Self].__realFields().includes(valueToFind, fromIndex);
} else {
- return this[Self].__fields.includes(valueToFind, fromIndex);
+ return this[Self].__fieldTuples.includes(valueToFind, fromIndex);
}
},
indexOf(valueToFind: any, fromIndex: number) {
if (valueToFind instanceof RefField) {
return this[Self].__realFields().indexOf(valueToFind, fromIndex);
} else {
- return this[Self].__fields.indexOf(valueToFind, fromIndex);
+ return this[Self].__fieldTuples.indexOf(valueToFind, fromIndex);
}
},
join(separator: any) {
this[Self].__realFields();
- return this[Self].__fields.map(toRealField).join(separator);
+ return this[Self].__fieldTuples.map(toRealField).join(separator);
},
lastElement() {
return this[Self].__realFields().lastElement();
@@ -140,12 +141,12 @@ const listHandlers: any = {
if (valueToFind instanceof RefField) {
return this[Self].__realFields().lastIndexOf(valueToFind, fromIndex);
} else {
- return this[Self].__fields.lastIndexOf(valueToFind, fromIndex);
+ return this[Self].__fieldTuples.lastIndexOf(valueToFind, fromIndex);
}
},
slice(begin: number, end: number) {
this[Self].__realFields();
- return this[Self].__fields.slice(begin, end).map(toRealField);
+ return this[Self].__fieldTuples.slice(begin, end).map(toRealField);
},
/// Iteration methods
@@ -156,55 +157,55 @@ const listHandlers: any = {
return this[Self].__realFields().every(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
- // return this[Self].__fields.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+ // return this[Self].__fieldTuples.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
filter(callback: any, thisArg: any) {
return this[Self].__realFields().filter(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
- // return this[Self].__fields.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+ // return this[Self].__fieldTuples.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
find(callback: any, thisArg: any) {
return this[Self].__realFields().find(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
- // return this[Self].__fields.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+ // return this[Self].__fieldTuples.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
findIndex(callback: any, thisArg: any) {
return this[Self].__realFields().findIndex(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
- // return this[Self].__fields.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+ // return this[Self].__fieldTuples.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
forEach(callback: any, thisArg: any) {
return this[Self].__realFields().forEach(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
- // return this[Self].__fields.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+ // return this[Self].__fieldTuples.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
map(callback: any, thisArg: any) {
return this[Self].__realFields().map(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
- // return this[Self].__fields.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+ // return this[Self].__fieldTuples.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
reduce(callback: any, initialValue: any) {
return this[Self].__realFields().reduce(callback, initialValue);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
- // return this[Self].__fields.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
+ // return this[Self].__fieldTuples.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
},
reduceRight(callback: any, initialValue: any) {
return this[Self].__realFields().reduceRight(callback, initialValue);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
- // return this[Self].__fields.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
+ // return this[Self].__fieldTuples.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
},
some(callback: any, thisArg: any) {
return this[Self].__realFields().some(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
- // return this[Self].__fields.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+ // return this[Self].__fieldTuples.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
values() {
return this[Self].__realFields().values();
@@ -246,7 +247,7 @@ type ListUpdate<T> = ListSpliceUpdate<T> | ListIndexUpdate<T>;
type StoredType<T extends Field> = T extends RefField ? ProxyField<T> : T;
-export const ListFieldName="fields";
+export const ListFieldName = 'fields';
@Deserializable('list')
class ListImpl<T extends Field> extends ObjectField {
constructor(fields?: T[]) {
@@ -254,9 +255,9 @@ class ListImpl<T extends Field> extends ObjectField {
const list = new Proxy<this>(this, {
set: setter,
get: listGetter,
- ownKeys: target => Object.keys(target.__fields),
+ ownKeys: target => Object.keys(target.__fieldTuples),
getOwnPropertyDescriptor: (target, prop) => {
- if (prop in target.__fields) {
+ if (prop in target[FieldTuples]) {
return {
configurable: true, //TODO Should configurable be true?
enumerable: true,
@@ -281,7 +282,7 @@ class ListImpl<T extends Field> extends ObjectField {
// this requests all ProxyFields at the same time to avoid the overhead
// of separate network requests and separate updates to the React dom.
private __realFields() {
- const unrequested = this.__fields.filter(f => f instanceof ProxyField && f.needsRequesting).map(f => f as ProxyField<RefField>);
+ const unrequested = this[FieldTuples].filter(f => f instanceof ProxyField && f.needsRequesting).map(f => f as ProxyField<RefField>);
// if we find any ProxyFields that don't have a current value, then
// start the server request for all of them
if (unrequested.length) {
@@ -293,17 +294,16 @@ class ListImpl<T extends Field> extends ObjectField {
// will await the batch request and return the requested field value.
unrequested.forEach(p => p.setExternalValuePromise(allSetPromise));
}
- return this.__fields.map(toRealField);
+ return this[FieldTuples].map(toRealField);
}
- public static FieldDataName = 'fields';
@serializable(alias(ListFieldName, list(autoObject(), { afterDeserialize: afterDocDeserialize })))
- private get __fields() {
- return this.___fields;
+ private get __fieldTuples() {
+ return this[FieldTuples];
}
- private set __fields(value) {
- this.___fields = value;
+ private set __fieldTuples(value) {
+ this[FieldTuples] = value;
for (const key in value) {
const field = value[key];
if (field instanceof ObjectField) {
@@ -314,14 +314,14 @@ class ListImpl<T extends Field> extends ObjectField {
}
[Copy]() {
- const copiedData = this[Self].__fields.map(f => (f instanceof ObjectField ? f[Copy]() : f));
+ const copiedData = this[Self].__fieldTuples.map(f => (f instanceof ObjectField ? f[Copy]() : f));
const deepCopy = new ListImpl<T>(copiedData as any);
return deepCopy;
}
// @serializable(alias("fields", list(autoObject())))
@observable
- private ___fields: StoredType<T>[] = [];
+ private [FieldTuples]: StoredType<T>[] = [];
private [Update] = (diff: any) => {
// console.log(diff);
diff --git a/src/fields/ListSpec.ts b/src/fields/ListSpec.ts
deleted file mode 100644
index e69de29bb..000000000
--- a/src/fields/ListSpec.ts
+++ /dev/null
diff --git a/src/fields/PresField.ts b/src/fields/PresField.ts
deleted file mode 100644
index f236a04fd..000000000
--- a/src/fields/PresField.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-//insert code here
-import { ObjectField } from "./ObjectField";
-
-export abstract class PresField extends ObjectField {
-
-} \ No newline at end of file
diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts
index 55d1d9ea4..c076f5fe1 100644
--- a/src/fields/Proxy.ts
+++ b/src/fields/Proxy.ts
@@ -1,5 +1,5 @@
import { Deserializable } from '../client/util/SerializationHelper';
-import { FieldWaiting, Opt } from './Doc';
+import { Field, FieldWaiting, Opt } from './Doc';
import { primitive, serializable } from 'serializr';
import { observable, action, runInAction, computed } from 'mobx';
import { DocServer } from '../client/DocServer';
@@ -39,7 +39,7 @@ export class ProxyField<T extends RefField> extends ObjectField {
}
[ToScriptString]() {
- return 'invalid';
+ 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' ?
}
[ToString]() {
return 'ProxyField';
diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts
index 7ad376a28..f035eeb0d 100644
--- a/src/fields/Schema.ts
+++ b/src/fields/Schema.ts
@@ -1,13 +1,13 @@
-import { Interface, ToInterface, Cast, ToConstructor, HasTail, Head, Tail, ListSpec, ToType, DefaultFieldConstructor } from "./Types";
-import { Doc, Field } from "./Doc";
-import { ObjectField } from "./ObjectField";
-import { RefField } from "./RefField";
-import { SelfProxy } from "./FieldSymbols";
-import { List } from "./List";
+import { Interface, ToInterface, Cast, ToConstructor, HasTail, Head, Tail, ListSpec, ToType, DefaultFieldConstructor } from './Types';
+import { Doc, Field } from './Doc';
+import { ObjectField } from './ObjectField';
+import { RefField } from './RefField';
+import { SelfProxy } from './DocSymbols';
+import { List } from './List';
type AllToInterface<T extends Interface[]> = {
- 1: ToInterface<Head<T>> & AllToInterface<Tail<T>>,
- 0: ToInterface<Head<T>>
+ 1: ToInterface<Head<T>> & AllToInterface<Tail<T>>;
+ 0: ToInterface<Head<T>>;
}[HasTail<T> extends true ? 1 : 0];
export const emptySchema = createSchema({});
@@ -28,36 +28,40 @@ export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFu
schema[key] = s[key];
}
}
- const proto = new Proxy({}, {
- get(target: any, prop, receiver) {
- const field = receiver.doc?.[prop];
- 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
- return Cast(field, desc.type, desc.defaultVal);
- }
- if (typeof desc === "function" && !ObjectField.isPrototypeOf(desc) && !RefField.isPrototypeOf(desc)) {
- const doc = Cast(field, Doc);
- if (doc === undefined) {
- return undefined;
+ const proto = new Proxy(
+ {},
+ {
+ get(target: any, prop, receiver) {
+ const field = receiver.doc?.[prop];
+ 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
+ return Cast(field, desc.type, desc.defaultVal);
}
- if (doc instanceof Doc) {
- return desc(doc);
+ if (typeof desc === 'function' && !ObjectField.isPrototypeOf(desc) && !RefField.isPrototypeOf(desc)) {
+ const doc = Cast(field, Doc);
+ if (doc === undefined) {
+ return undefined;
+ }
+ if (doc instanceof Doc) {
+ return desc(doc);
+ }
+ return doc.then(doc => doc && desc(doc));
}
- return doc.then(doc => doc && desc(doc));
+ return Cast(field, desc);
}
- return Cast(field, desc);
- }
- return field;
- },
- set(target: any, prop, value, receiver) {
- receiver.doc && (receiver.doc[prop] = value); // receiver.doc may be undefined as the result of a change in acls
- return true;
+ return field;
+ },
+ set(target: any, prop, value, receiver) {
+ receiver.doc && (receiver.doc[prop] = value); // receiver.doc may be undefined as the result of a change in acls
+ return true;
+ },
}
- });
+ );
// !(doc instanceof Doc) && (throw new Error("Currently wrapping a schema in another schema isn't supported"));
const fn = (doc: Doc) => Object.create(proto, { doc: { value: doc[SelfProxy], writable: false } });
- return ((doc?: Doc | Doc[]) => (doc instanceof List ? doc : undefined)?.map?.(fn) ?? fn((doc as Doc) ?? new Doc)) as InterfaceFunc<T>;
+ return ((doc?: Doc | Doc[]) => (doc instanceof List ? doc : undefined)?.map?.(fn) ?? fn((doc as Doc) ?? new Doc())) as InterfaceFunc<T>;
}
export type makeStrictInterface<T extends Interface> = Partial<ToInterface<T>>;
@@ -75,8 +79,8 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc)
this.__doc[key] = value;
return;
}
- throw new TypeError("Expected type " + type);
- }
+ throw new TypeError('Expected type ' + type);
+ },
});
}
return function (doc: any) {
@@ -95,12 +99,12 @@ export function createSchema<T extends Interface>(schema: T): T & { proto: ToCon
}
export function listSpec<U extends ToConstructor<Field>>(type: U): ListSpec<ToType<U>> {
- return { List: type as any };//TODO Types
+ return { List: type as any }; //TODO Types
}
export function defaultSpec<T extends ToConstructor<Field>>(type: T, defaultVal: ToType<T>): DefaultFieldConstructor<ToType<T>> {
return {
type: type as any,
- defaultVal
+ defaultVal,
};
-} \ No newline at end of file
+}
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index a5361b98a..76b287be7 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -49,14 +49,14 @@ export const documentSchema = createSchema({
_columnsHideIfEmpty: 'boolean', // whether empty stacking view column headings should be hidden
// _columnHeaders: listSpec(SchemaHeaderField), // header descriptions for stacking/masonry
// _schemaHeaders: listSpec(SchemaHeaderField), // header descriptions for schema views
- _fontSize: 'string',
- _fontFamily: 'string',
+ _text_fontSize: 'string',
+ _text_fontFamily: 'string',
_layout_sidebarWidthPercent: 'string', // percent of text window width taken up by sidebar
// appearance properties on the data document
backgroundColor: 'string', // background color of document
- borderRounding: 'string', // border radius rounding of document
- boxShadow: 'string', // the amount of shadow around the perimeter of a document
+ layout_borderRounding: 'string', // border radius rounding of document
+ layout_boxShadow: 'string', // the amount of shadow around the perimeter of a document
color: 'string', // foreground color of document
freeform_fitContentsToBox: 'boolean', // whether freeform view contents should be zoomed/panned to fill the area of the document view box
fontSize: 'string',
@@ -86,21 +86,21 @@ export const documentSchema = createSchema({
onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped.
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
- hideAllLinks: 'boolean', // whether all individual blue anchor dots should be hidden
- layout_linkDisplay: 'boolean', // whether a link connection should be shown between link anchor endpoints.
+ 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)
_lockedTransform: 'boolean', // whether a freeformview can pan/zoom
- layout_linkDisplayArrow: 'boolean', // toggles directed arrows
+ link_displayArrow: 'boolean', // toggles directed arrows
// drag drop properties
- _stayInCollection: 'boolean', // whether document can be dropped into a different collection
+ _dragOnlyWithinContainer: 'boolean', // whether document can be dropped into a different collection
dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document.
- dropAction: 'string', // override specifying what should happen when this document is dropped (can be "embed", "copy", "move")
- targetDropAction: 'string', // allows the target of a drop event to specify the dropAction ("embed", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move'
- childDropAction: 'string', // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "embed" or "copy")
- removeDropProperties: listSpec('string'), // properties that should be removed from the embed/copy/etc of this document when it is dropped
+ dropAction: 'string', // override specifying what should happen when something is dropped on this document (can be "embed", "copy", "move")
+ dragAction: 'string', // override specifying what should happen when this document s dragged (can be "embed", "copy", "move")
+ childDragAction: 'string', // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "embed" or "copy")
+ dropPropertiesToRemove: listSpec('string'), // properties that should be removed from the embed/copy/etc of this document when it is dropped
});
export const collectionSchema = createSchema({
diff --git a/src/fields/util.ts b/src/fields/util.ts
index a2b445d6c..0f164a709 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -7,35 +7,16 @@ import { SerializationHelper } from '../client/util/SerializationHelper';
import { UndoManager } from '../client/util/UndoManager';
import { returnZero } from '../Utils';
import CursorField from './CursorField';
-import {
- AclAdmin,
- AclEdit,
- aclLevel,
- AclPrivate,
- AclSelfEdit,
- AclSym,
- DataSym,
- Doc,
- DocListCast,
- DocListCastAsync,
- ForceServerWrite,
- HeightSym,
- HierarchyMapping,
- Initializing,
- LayoutSym,
- ReverseHierarchyMap,
- updateCachedAcls,
- UpdatingFromServer,
- WidthSym,
-} from './Doc';
-import { Id, OnUpdate, Parent, SelfProxy, ToValue, Update } from './FieldSymbols';
+import { aclLevel, Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap, StrListCast, updateCachedAcls } from './Doc';
+import { AclAdmin, AclEdit, AclPrivate, AclSelfEdit, DocAcl, DocData, DocLayout, FieldKeys, ForceServerWrite, Height, Initializing, SelfProxy, Update, UpdatingFromServer, Width } from './DocSymbols';
+import { Id, OnUpdate, Parent, ToValue } from './FieldSymbols';
import { List } from './List';
import { ObjectField } from './ObjectField';
import { PrefetchProxy, ProxyField } from './Proxy';
import { RefField } from './RefField';
import { RichTextField } from './RichTextField';
import { SchemaHeaderField } from './SchemaHeaderField';
-import { ComputedField } from './ScriptField';
+import { ComputedField, ScriptField } from './ScriptField';
import { ScriptCast, StrCast } from './Types';
function _readOnlySetter(): never {
@@ -53,10 +34,9 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
return true;
}
- if (value !== undefined) {
- value = value[SelfProxy] || value;
- }
- const curValue = target.__fields[prop];
+ value = value?.[SelfProxy] ?? value; // 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])) {
// TODO This kind of checks correctly in the case that curValue is a ProxyField and value is a RefField, but technically
// curValue should get filled in with value if it isn't already filled in, in case we fetched the referenced field some other way
@@ -88,11 +68,13 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
if (writeToDoc) {
if (value === undefined) {
- target.__fieldKeys && delete target.__fieldKeys[prop];
- delete target.__fields[prop];
+ target[FieldKeys] && delete target[FieldKeys][prop]; // Lists don't have a FieldKeys field
+ delete target.__fieldTuples[prop];
} else {
- target.__fieldKeys && (target.__fieldKeys[prop] = true);
- target.__fields[prop] = value;
+ // bcz: uncomment to see if server is being updated
+ // console.log(prop + ' = ' + value + '(' + curValue + ')');
+ target[FieldKeys] && (target[FieldKeys][prop] = true); // Lists don't have a FieldKeys field
+ target.__fieldTuples[prop] = value;
}
if (writeToServer) {
@@ -103,7 +85,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue);
}
!receiver[Initializing] &&
- !receiver.dontUndo &&
+ !StrListCast(receiver.undoIgnoreFields).includes(prop.toString()) &&
(!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) &&
UndoManager.AddEvent(
{
@@ -148,12 +130,12 @@ export function denormalizeEmail(email: string) {
*/
export function inheritParentAcls(parent: Doc, child: Doc) {
return;
- const dataDoc = parent[DataSym];
- for (const key of Object.keys(dataDoc)) {
- // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
- const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
- key.startsWith('acl') && distributeAcls(key, permission, child);
- }
+ // const dataDoc = parent[DataSym];
+ // for (const key of Object.keys(dataDoc)) {
+ // // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
+ // const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
+ // key.startsWith('acl') && distributeAcls(key, permission, child);
+ // }
}
/**
@@ -212,7 +194,7 @@ export function SetCachedGroups(groups: string[]) {
runInAction(() => cachedGroups.push(...groups));
}
function getEffectiveAcl(target: any, user?: string): symbol {
- const targetAcls = target[AclSym];
+ const targetAcls = target[DocAcl];
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
@@ -237,7 +219,7 @@ function getEffectiveAcl(target: any, user?: string): symbol {
return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl;
}
// authored documents are private until an ACL is set.
- const targetAuthor = target.__fields?.author || target.author; // target may be a Doc of Proxy, so check __fields.author and .author
+ const targetAuthor = target.__fieldTuples?.author || target.author; // target may be a Doc of Proxy, so check __fieldTuples.author and .author
if (targetAuthor && targetAuthor !== userChecked) return AclPrivate;
return AclAdmin;
}
@@ -277,7 +259,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
}
let dataDocChanged = false;
- const dataDoc = target[DataSym];
+ const dataDoc = target[DocData];
if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || ReverseHierarchyMap.get(StrCast(dataDoc[key]))! > ReverseHierarchyMap.get(acl)!)) {
if (GetEffectiveAcl(dataDoc) === AclAdmin) {
dataDoc[key] = acl;
@@ -290,13 +272,13 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
// maps over the children of the document
DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => {
distributeAcls(key, acl, d, inheritingFromCollection, visited);
- distributeAcls(key, acl, d[DataSym], inheritingFromCollection, visited);
+ distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited);
});
// maps over the annotations of the document
DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '_annotations']).forEach(d => {
distributeAcls(key, acl, d, inheritingFromCollection, visited);
- distributeAcls(key, acl, d[DataSym], inheritingFromCollection, visited);
+ distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited);
});
}
@@ -311,16 +293,16 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
// 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 !== '__id' && prop !== '__fields' && prop.startsWith('_')) {
+ if (typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('_')) {
if (!prop.startsWith('__')) prop = prop.substring(1);
if (target.__LAYOUT__) {
target.__LAYOUT__[prop] = value;
return true;
}
}
- if (target.__fields[prop] instanceof ComputedField) {
- if (target.__fields[prop].setterscript && value !== undefined && !(value instanceof ComputedField)) {
- return ScriptCast(target.__fields[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false;
+ 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 _setter(target, prop, value, receiver);
@@ -330,14 +312,13 @@ export function getter(target: any, prop: string | symbol, proxy: any): any {
// prettier-ignore
switch (prop) {
case 'then' : return undefined;
- case '__fields' : case '__id':
- case 'constructor': case 'toString': case 'valueOf':
- case 'factory': case 'serializeInfo':
+ case 'constructor': case 'toString': case 'valueOf':
+ case 'serializeInfo': case 'factory':
return target[prop];
- case AclSym : return target[AclSym];
- case $mobx: return target.__fields[prop];
- case LayoutSym: return target.__LAYOUT__;
- case HeightSym: case WidthSym: if (GetEffectiveAcl(target) === AclPrivate) return returnZero;
+ case DocAcl : return target[DocAcl];
+ case $mobx: return target.__fieldTuples[prop];
+ case DocLayout: return target.__LAYOUT__;
+ case Height: case Width: if (GetEffectiveAcl(target) === AclPrivate) return returnZero;
default :
if (typeof prop === 'symbol') return target[prop];
if (prop.startsWith('isMobX')) return target[prop];
@@ -351,7 +332,7 @@ export function getter(target: any, prop: string | symbol, proxy: any): any {
}
function getFieldImpl(target: any, prop: string | number, proxy: any, ignoreProto: boolean = false): any {
- const field = target.__fields[prop];
+ 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') {
@@ -451,7 +432,8 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
lastValue = ObjectField.MakeCopy(receiver[prop]);
},
prop: 'assign list',
- }
+ },
+ diff?.items
);
}
target[Update](op);