aboutsummaryrefslogtreecommitdiff
path: root/src/fields/Doc.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/fields/Doc.ts')
-rw-r--r--src/fields/Doc.ts617
1 files changed, 321 insertions, 296 deletions
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 6c808c145..92ae64567 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -9,13 +9,42 @@ import { LinkManager } from '../client/util/LinkManager';
import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals';
import { SelectionManager } from '../client/util/SelectionManager';
import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from '../client/util/SerializationHelper';
-import { UndoManager } from '../client/util/UndoManager';
+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 { InkTool } from './InkField';
-import { List } from './List';
+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';
import { PrefetchProxy, ProxyField } from './Proxy';
import { FieldId, RefField } from './RefField';
@@ -23,21 +52,24 @@ import { RichTextField } from './RichTextField';
import { listSpec } from './Schema';
import { ComputedField, ScriptField } from './ScriptField';
import { Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Types';
-import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from './URLField';
+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);
+ const onDelegate = Object.keys(doc).includes(key.replace(/^_/, ''));
const field = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
- return !Field.IsField(field) ? '' : (onDelegate ? '=' : '') + (field instanceof ComputedField ? `:=${field.script.originalScript}` : field instanceof ScriptField ? `$=${field.script.originalScript}` : Field.toScriptString(field));
+ return !Field.IsField(field)
+ ? key.startsWith('_')
+ ? '='
+ : ''
+ : (onDelegate ? '=' : '') + (field instanceof ComputedField ? `:=${field.script.originalScript}` : field instanceof ScriptField ? `$=${field.script.originalScript}` : Field.toScriptString(field));
}
export function toScriptString(field: Field): string {
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}"`;
+ return !field.includes('`') ? `\`${field}\`` : `"${field}"`;
case 'number':
case 'boolean':
return String(field);
@@ -89,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,
@@ -137,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;
}
}
@@ -207,6 +217,15 @@ export class Doc extends RefField {
public static get MyTrails() {
return DocCast(Doc.ActiveDashboard?.myTrails);
}
+ public static IsInMyOverlay(doc: Doc) {
+ return DocListCast(Doc.MyOverlayDocs?.data).includes(doc);
+ }
+ public static AddToMyOverlay(doc: Doc) {
+ Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc);
+ }
+ public static RemFromMyOverlay(doc: Doc) {
+ Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, doc);
+ }
public static get MyOverlayDocs() {
return DocCast(Doc.UserDoc().myOverlayDocs);
}
@@ -232,9 +251,9 @@ export class Doc extends RefField {
if (
doc &&
Doc.MyFileOrphans instanceof Doc &&
- Doc.IsPrototype(doc) &&
+ Doc.IsDataProto(doc) &&
!Doc.IsSystem(doc) &&
- ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(doc.type as any) &&
+ ![DocumentType.CONFIG, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(doc.type as any) &&
!doc.isFolder &&
!doc.annotationOn
) {
@@ -282,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: () => {
@@ -314,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;
@@ -361,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]() {
+ public get [DocData](): Doc {
const self = this[SelfProxy];
return self.resolvedDataDoc && !self.isTemplateForField ? self : Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self);
}
@@ -377,7 +385,7 @@ export class Doc extends RefField {
const templateLayoutDoc = Cast(Doc.LayoutField(this[SelfProxy]), Doc, null);
if (templateLayoutDoc) {
let renderFieldKey: any;
- const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layoutKey, 'layout')];
+ const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layout_fieldKey, 'layout')];
if (typeof layoutField === 'string') {
renderFieldKey = layoutField.split("fieldKey={'")[1].split("'")[0]; //layoutField.split("'")[1];
} else {
@@ -506,14 +514,14 @@ export namespace Doc {
export function GetT<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): FieldResult<T> {
return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult<T>;
}
- export function IsPrototype(doc: Doc) {
- return GetT(doc, 'isPrototype', 'boolean', true);
+ export function IsDataProto(doc: Doc) {
+ return GetT(doc, 'isDataDoc', 'boolean', true);
}
export function IsBaseProto(doc: Doc) {
- return GetT(doc, 'baseProto', 'boolean', true);
+ return GetT(doc, 'isBaseProto', 'boolean', true);
}
export function IsSystem(doc: Doc) {
- return GetT(doc, 'system', 'boolean', true);
+ return GetT(doc, 'isSystem', 'boolean', true);
}
export function IsDelegateField(doc: Doc, fieldKey: string) {
return doc && Get(doc, fieldKey, true) !== undefined;
@@ -522,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;
}
@@ -570,9 +578,9 @@ export namespace Doc {
// Gets the data document for the document. Note: this is mis-named -- it does not specifically
// 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 base prototype ('isPrototype').
+ // 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, 'isPrototype', '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 {
@@ -586,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);
@@ -663,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),
@@ -676,36 +684,36 @@ export namespace Doc {
return bounds;
}
- export function MakeAlias(doc: Doc, id?: string) {
- const alias = !GetT(doc, 'isPrototype', 'boolean', true) && doc.proto ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
- const layout = Doc.LayoutField(alias);
- if (layout instanceof Doc && layout !== alias && layout === Doc.Layout(alias)) {
- Doc.SetLayout(alias, Doc.MakeAlias(layout));
+ export function MakeEmbedding(doc: Doc, id?: string) {
+ const embedding = !GetT(doc, 'isDataDoc', 'boolean', true) && doc.proto ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
+ const layout = Doc.LayoutField(embedding);
+ if (layout instanceof Doc && layout !== embedding && layout === Doc.Layout(embedding)) {
+ Doc.SetLayout(embedding, Doc.MakeEmbedding(layout));
}
- alias.aliasOf = doc;
- alias.aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
- alias.title = ComputedField.MakeFunction(`renameAlias(this)`);
- alias.author = Doc.CurrentUserEmail;
+ embedding.createdFrom = doc;
+ embedding.proto_embeddingId = Doc.GetProto(doc).proto_embeddingId = NumCast(Doc.GetProto(doc).proto_embeddingId) + 1;
+ embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`);
+ embedding.author = Doc.CurrentUserEmail;
- Doc.AddDocToList(Doc.GetProto(doc)[DataSym], 'aliases', alias);
+ Doc.AddDocToList(Doc.GetProto(doc)[DocData], 'proto_embeddings', embedding);
- return alias;
+ return embedding;
}
- export function BestAlias(doc: Doc) {
- const bestAlias = Doc.GetProto(doc) ? DocListCast(doc.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail) : doc;
- return bestAlias ?? Doc.MakeAlias(doc);
+ export function BestEmbedding(doc: Doc) {
+ const bestEmbedding = Doc.GetProto(doc) ? DocListCast(doc.proto_embeddings).find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail) : doc;
+ return bestEmbedding ?? Doc.MakeEmbedding(doc);
}
// this lists out all the tag ids that can be in a RichTextField that might contain document ids.
// if a document is cloned, we need to make sure to clone all of these referenced documents as well;
export const DocsInTextFieldIds = ['audioId', 'textId', 'anchorId', 'docId'];
- export async function makeClone(doc: Doc, cloneMap: Map<string, Doc>, linkMap: Map<string, Doc>, rtfs: { copy: Doc; key: string; field: RichTextField }[], exclusions: string[], cloneLinks: boolean): Promise<Doc> {
+ export async function makeClone(doc: Doc, cloneMap: Map<string, Doc>, linkMap: Map<string, Doc>, rtfs: { copy: Doc; key: string; field: RichTextField }[], exclusions: string[], pruneDocs: Doc[], cloneLinks: boolean): Promise<Doc> {
if (Doc.IsBaseProto(doc)) return doc;
if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!;
const copy = new Doc(undefined, true);
cloneMap.set(doc[Id], copy);
- const filter = [...exclusions, ...Cast(doc.cloneFieldFilter, listSpec('string'), [])];
+ const filter = [...exclusions, ...StrListCast(doc.cloneFieldFilter)];
await Promise.all(
Object.keys(doc).map(async key => {
if (filter.includes(key)) return;
@@ -716,7 +724,7 @@ export namespace Doc {
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, cloneLinks)));
+ const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks)));
assignKey(new List<Doc>(clones));
} else {
assignKey(ObjectField.MakeCopy(field));
@@ -725,15 +733,13 @@ export namespace Doc {
const docidsearch = new RegExp('(' + DocsInTextFieldIds.map(exp => '(' + exp + ')').join('|') + ')":"([a-z-A-Z0-9_]*)"', 'g');
const rawdocids = field.Data.match(docidsearch);
const docids = rawdocids?.map((str: string) =>
- DocsInTextFieldIds.reduce((output, exp) => {
- return output.replace(new RegExp(`${exp}":`, 'g'), '');
- }, str)
+ 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 && docs.map(doc => Doc.makeClone(doc, cloneMap, linkMap, rtfs, exclusions, cloneLinks));
+ docs?.map(doc => doc && Doc.makeClone(doc, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks));
rtfs.push({ copy, key, field });
}
}
@@ -741,16 +747,17 @@ export namespace Doc {
};
const docAtKey = doc[key];
if (docAtKey instanceof Doc) {
- if (!Doc.IsSystem(docAtKey) && (key.startsWith('layout') || key === 'annotationOn' || key === 'proto' || ((key === 'anchor1' || key === 'anchor2') && doc.author === Doc.CurrentUserEmail))) {
- assignKey(await Doc.makeClone(docAtKey, cloneMap, linkMap, rtfs, exclusions, cloneLinks));
+ if (pruneDocs.includes(docAtKey)) {
+ // prune doc and do nothing
+ } else if (!Doc.IsSystem(docAtKey) && (key.startsWith('layout') || ['embedContainer', 'annotationOn', 'proto'].includes(key) || ((key === 'link_anchor_1' || key === 'link_anchor_2') && doc.author === Doc.CurrentUserEmail))) {
+ assignKey(await Doc.makeClone(docAtKey, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks));
} else {
assignKey(docAtKey);
}
} else if (field instanceof RefField) {
assignKey(field);
} else if (cfield instanceof ComputedField) {
- assignKey(cfield[Copy]());
- // ComputedField.MakeFunction(cfield.script.originalScript));
+ assignKey(cfield[Copy]()); // ComputedField.MakeFunction(cfield.script.originalScript));
} else if (field instanceof ObjectField) {
await copyObjectField(field);
} else if (field instanceof Promise) {
@@ -760,12 +767,13 @@ export namespace Doc {
}
})
);
- Array.from(doc[DirectLinksSym]).forEach(async link => {
+ Array.from(doc[DirectLinks]).forEach(async link => {
if (
cloneLinks ||
- ((cloneMap.has(DocCast(link.anchor1)?.[Id]) || cloneMap.has(DocCast(DocCast(link.anchor1)?.annotationOn)?.[Id])) && (cloneMap.has(DocCast(link.anchor2)?.[Id]) || cloneMap.has(DocCast(DocCast(link.anchor2)?.annotationOn)?.[Id])))
+ ((cloneMap.has(DocCast(link.link_anchor_1)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_1)?.annotationOn)?.[Id])) &&
+ (cloneMap.has(DocCast(link.link_anchor_2)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_2)?.annotationOn)?.[Id])))
) {
- linkMap.set(link[Id], await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, cloneLinks));
+ linkMap.set(link[Id], await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks));
}
});
Doc.SetInPlace(copy, 'title', 'CLONE: ' + doc.title, true);
@@ -801,11 +809,9 @@ export namespace Doc {
export async function MakeClone(doc: Doc, cloneLinks = 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'], cloneLinks);
+ const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.embedContainer ? [DocCast(doc.embedContainer)] : [], cloneLinks);
const repaired = new Set<Doc>();
const linkedDocs = Array.from(linkMap.values());
- const clonedDocs = [...Array.from(cloneMap.values()), ...linkedDocs];
- clonedDocs.map(clone => Doc.repairClone(clone, cloneMap, repaired));
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) => {
@@ -816,97 +822,80 @@ export namespace Doc {
const mapped = cloneMap.get(id);
return href + (mapped ? mapped[Id] : id);
};
- const regex = `(${Doc.localServerPath()})([^"]*)`;
- const re = new RegExp(regex, 'g');
+ const re = new RegExp(`(${Doc.localServerPath()})([^"]*)`, 'g');
const docidsearch = new RegExp('(' + DocsInTextFieldIds.map(exp => `"${exp}":`).join('|') + ')"([^"]+)"', 'g');
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, repaired));
return { clone: copy, map: cloneMap, linkMap };
}
- export async function Zip(doc: Doc) {
- // 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();
+ export async function Zip(doc: Doc, zipFilename = 'dashExport.zip') {
const { clone, map, linkMap } = await Doc.MakeClone(doc);
- clone.LINKS = new List<Doc>(Array.from(linkMap.values()));
- const proms = [] as string[];
+ const proms = new Set<string>();
function replacer(key: any, value: any) {
if (key && ['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined;
- else if (value instanceof Doc) {
- if (key !== 'field' && Number.isNaN(Number(key))) {
- const __fields = value[FieldsSym]();
- return { id: value[Id], __type: 'Doc', fields: __fields };
- } else {
- return { fieldId: value[Id], __type: 'proxy' };
- }
- } else if (value instanceof ScriptField) return { script: value.script, __type: 'script' };
- else if (value instanceof RichTextField) return { Data: value.Data, Text: value.Text, __type: 'RichTextField' };
- else if (value instanceof ImageField) {
+ if (value instanceof ImageField) {
const extension = value.url.href.replace(/.*\./, '');
- proms.push(value.url.href.replace('.' + extension, '_o.' + extension));
- return { url: value.url.href, __type: 'image' };
- } else if (value instanceof PdfField) {
- proms.push(value.url.href);
- return { url: value.url.href, __type: 'pdf' };
- } else if (value instanceof AudioField) return { url: value.url.href, __type: 'audio' };
- else if (value instanceof VideoField) return { url: value.url.href, __type: 'video' };
- else if (value instanceof WebField) return { url: value.url.href, __type: 'web' };
- else if (value instanceof MapField) return { url: value.url.href, __type: 'map' };
- else if (value instanceof DateField) return { date: value.toString(), __type: 'date' };
- else if (value instanceof ProxyField) return { fieldId: value.fieldId, __type: 'proxy' };
- else if (value instanceof Array && key !== 'fields') return { fields: value, __type: 'list' };
- else if (value instanceof ComputedField) return { script: value.script, __type: 'computed' };
- else return value;
+ proms.add(value.url.href.replace('.' + extension, '_o.' + extension));
+ return SerializationHelper.Serialize(value);
+ }
+ if (value instanceof PdfField || value instanceof AudioField || value instanceof VideoField) {
+ proms.add(value.url.href);
+ return SerializationHelper.Serialize(value);
+ }
+ 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]));
- const docString = JSON.stringify({ id: clone[Id], docs }, decycle(replacer));
-
- let generateZIP = (proms: string[]) => {
- var zip = new JSZip();
- var count = 0;
- var zipFilename = 'dashExport.zip';
-
- proms
- .filter(url => url.startsWith(window.location.origin))
- .forEach((url, i) => {
- var filename = proms[i].replace(window.location.origin + '/', '').replace(/\//g, '%%%');
- // loading a file and add it in a zip file
- JSZipUtils.getBinaryContent(url, function (err: any, data: any) {
- if (err) {
- throw err; // or handle the error
- }
- zip.file(filename, data, { binary: true });
- count++;
- if (count == proms.length) {
- zip.file('doc.json', docString);
- zip.generateAsync({ type: 'blob' }).then(function (content) {
- saveAs(content, zipFilename);
- });
- }
- });
- });
- };
- generateZIP(proms);
- const zip = new JSZip();
-
- zip.file('doc.json', docString);
-
- // // Generate a directory within the Zip file structure
- // var img = zip.folder("images");
-
- // // Add a file to the directory, in this case an image with data URI as contents
- // img.file("smile.gif", imgData, {base64: true});
+ Array.from(linkMap.entries()).forEach(l => (links[l[0]] = l[1]));
+ const jsonDocs = JSON.stringify({ id: clone[Id], docs, links }, decycle(replacer));
- // Generate the zip file asynchronously
- zip.generateAsync({ type: 'blob' }).then((content: any) => {
- // Force down of the Zip file
- saveAs(content, doc.title + '.zip'); // glr: Possibly change the name of the document to match the title?
- });
+ const zip = new JSZip();
+ var count = 0;
+ const promArr = Array.from(proms).filter(url => url.startsWith(window.location.origin));
+ 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(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();
+ }
+ });
+ });
}
const _pendingMap: Map<string, boolean> = new Map();
@@ -936,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(
@@ -963,7 +952,7 @@ export namespace Doc {
// otherwise, it just returns the childDoc
export function GetLayoutDataDocPair(containerDoc: Doc, containerDataDoc: Opt<Doc>, childDoc: Doc) {
if (!childDoc || childDoc instanceof Promise || !Doc.GetProto(childDoc)) {
- console.log('No, no, no!');
+ console.log('Warning: GetLayoutDataDocPair childDoc not defined');
return { layout: childDoc, data: childDoc };
}
const resolvedDataDoc = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField) ? undefined : containerDataDoc;
@@ -975,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) {
@@ -996,41 +985,40 @@ export namespace Doc {
export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string, retitle = false): Doc {
const copy = new Doc(copyProtoId, true);
updateCachedAcls(copy);
- const exclude = Cast(doc.cloneFieldFilter, listSpec('string'), []);
- 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...
+ const exclude = [...StrListCast(doc.cloneFieldFilter), 'dragFactory_count', 'cloneFieldFilter'];
+ 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).context = undefined;
- Doc.GetProto(copy).aliases = new List<Doc>([copy]);
+ Doc.GetProto(copy).embedContainer = undefined;
+ Doc.GetProto(copy).proto_embeddings = new List<Doc>([copy]);
} else {
- Doc.AddDocToList(Doc.GetProto(copy)[DataSym], 'aliases', copy);
+ Doc.AddDocToList(Doc.GetProto(copy)[DocData], 'proto_embeddings', copy);
}
- copy.context = undefined;
+ copy.embedContainer = undefined;
Doc.defaultAclPrivate && (copy['acl-Public'] = 'Not Shared');
if (retitle) {
copy.title = incrementTitleCopy(StrCast(copy.title));
@@ -1051,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], 'aliases', delegate);
+ if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DocData], 'proto_embeddings', delegate);
title && (delegate.title = title);
delegate[Initializing] = false;
Doc.AddFileOrphan(delegate);
@@ -1069,13 +1057,13 @@ export namespace Doc {
delegateProto[Initializing] = true;
delegateProto.proto = doc;
delegateProto.author = Doc.CurrentUserEmail;
- delegateProto.isPrototype = true;
+ delegateProto.isDataDoc = true;
title && (delegateProto.title = title);
const delegate = new Doc(id, true);
delegate[Initializing] = true;
delegate.proto = delegateProto;
delegate.author = Doc.CurrentUserEmail;
- Doc.AddDocToList(delegateProto[DataSym], 'aliases', delegate);
+ Doc.AddDocToList(delegateProto[DocData], 'proto_embeddings', delegate);
delegate[Initializing] = false;
delegateProto[Initializing] = false;
return delegate;
@@ -1087,9 +1075,9 @@ export namespace Doc {
const proto = new Doc();
proto.author = Doc.CurrentUserEmail;
const target = Doc.MakeDelegate(proto);
- const targetKey = StrCast(templateDoc.layoutKey, 'layout');
+ const targetKey = StrCast(templateDoc.layout_fieldKey, 'layout');
const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + '(...' + _applyCount++ + ')');
- target.layoutKey = targetKey;
+ target.layout_fieldKey = targetKey;
applied && (Doc.GetProto(applied).type = templateDoc.type);
Doc.defaultAclPrivate && (applied['acl-Public'] = 'Not Shared');
return applied;
@@ -1179,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 = '';
@@ -1189,37 +1177,37 @@ 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.layoutKey, 'layout')] = layout;
+ doc[StrCast(doc.layout_fieldKey, 'layout')] = layout;
}
export function LayoutField(doc: Doc) {
- return doc[StrCast(doc.layoutKey, 'layout')];
+ return doc[StrCast(doc.layout_fieldKey, 'layout')];
}
export function LayoutFieldKey(doc: Doc): string {
- return StrCast(Doc.Layout(doc)[StrCast(doc.layoutKey, 'layout')]).split("'")[1]; // bcz: TODO check on this . used to always reference 'layout', now it uses the layout speicfied by the current layoutKey
+ return StrCast(Doc.Layout(doc).layout).split("'")[1]; // bcz: TODO check on this . used to always reference 'layout', now it uses the layout speicfied by the current layout_fieldKey
}
export function NativeAspect(doc: Doc, dataDoc?: Doc, useDim?: boolean) {
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) {
- doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '-nativeWidth'] = width;
+ doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '_nativeWidth'] = width;
}
export function SetNativeHeight(doc: Doc, height: number | undefined, fieldKey?: string) {
- doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '-nativeHeight'] = height;
+ doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '_nativeHeight'] = height;
}
- const manager = new DocData();
+ const manager = new UserDocData();
export function SearchQuery(): string {
return manager._searchQuery;
}
@@ -1287,8 +1275,8 @@ export namespace Doc {
const lastBrushed = Array.from(brushManager.BrushedDoc.keys()).lastElement();
if (lastBrushed) {
for (const link of LinkManager.Instance.getAllDirectLinks(lastBrushed)) {
- const a1 = Cast(link.anchor1, Doc, null);
- const a2 = Cast(link.anchor2, Doc, null);
+ const a1 = Cast(link.link_anchor_1, Doc, null);
+ const a2 = Cast(link.link_anchor_2, Doc, null);
if (Doc.AreProtosEqual(a1, doc) || Doc.AreProtosEqual(a2, doc) || Doc.AreProtosEqual(Cast(a1.annotationOn, Doc, null), doc) || Doc.AreProtosEqual(Cast(a2.annotationOn, Doc, null), doc)) {
return DocBrushStatus.linkHighlighted;
}
@@ -1320,8 +1308,8 @@ export namespace Doc {
}
export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) {
- if (linkDoc.anchor2 === anchorDoc || (linkDoc.anchor2 as Doc).annotationOn) return '2';
- return Doc.AreProtosEqual(anchorDoc, (linkDoc.anchor1 as Doc).annotationOn as Doc) || Doc.AreProtosEqual(anchorDoc, linkDoc.anchor1 as Doc) ? '1' : '2';
+ if (linkDoc.link_anchor_2 === anchorDoc || (linkDoc.link_anchor_2 as Doc).annotationOn) return '2';
+ return Doc.AreProtosEqual(anchorDoc, (linkDoc.link_anchor_1 as Doc).annotationOn as Doc) || Doc.AreProtosEqual(anchorDoc, linkDoc.link_anchor_1 as Doc) ? '1' : '2';
}
export function linkFollowUnhighlight() {
@@ -1354,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;
}
});
}
@@ -1373,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;
});
});
}
@@ -1404,7 +1392,7 @@ export namespace Doc {
if (typeof value === 'string') {
value = value.replace(`,${Utils.noRecursionHack}`, '');
}
- const fieldVal = key === '#' ? (StrCast(doc.tags).includes(':#' + value + ':') ? StrCast(doc.tags) : undefined) : doc[key];
+ const fieldVal = doc[key];
if (Cast(fieldVal, listSpec('string'), []).length) {
const vals = Cast(fieldVal, listSpec('string'), []);
const docs = vals.some(v => (v as any) instanceof Doc);
@@ -1412,33 +1400,33 @@ export namespace Doc {
return vals.some(v => v.includes(value)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
}
const fieldStr = Field.toString(fieldVal as Field);
- return fieldStr.includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
+ return fieldStr.includes(value) || (value === String.fromCharCode(127) + '--undefined--' && fieldVal === undefined); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
}
export function deiconifyView(doc: Doc) {
- StrCast(doc.layoutKey).split('_')[1] === 'icon' && setNativeView(doc);
+ StrCast(doc.layout_fieldKey).split('_')[1] === 'icon' && setNativeView(doc);
}
export function setNativeView(doc: any) {
- const prevLayout = StrCast(doc.layoutKey).split('_')[1];
+ const prevLayout = StrCast(doc.layout_fieldKey).split('_')[1];
const deiconify = prevLayout === 'icon' && StrCast(doc.deiconifyLayout) ? 'layout_' + StrCast(doc.deiconifyLayout) : '';
prevLayout === 'icon' && (doc.deiconifyLayout = undefined);
- doc.layoutKey = deiconify || 'layout';
+ doc.layout_fieldKey = deiconify || 'layout';
}
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);
}
}
@@ -1447,35 +1435,35 @@ export namespace Doc {
// 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 = Cast(container[filterField], listSpec('string'), []);
+ 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(':'); // 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 + ':' + value + ':' + 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])];
}
}
}
@@ -1487,18 +1475,18 @@ export namespace Doc {
export function toggleNativeDimensions(layoutDoc: Doc, contentScale: number, panelWidth: number, panelHeight: number) {
runInAction(() => {
if (Doc.NativeWidth(layoutDoc) || Doc.NativeHeight(layoutDoc)) {
- layoutDoc._viewScale = NumCast(layoutDoc._viewScale, 1) * contentScale;
+ layoutDoc._freeform_scale = NumCast(layoutDoc._freeform_scale, 1) * contentScale;
layoutDoc._nativeWidth = undefined;
layoutDoc._nativeHeight = undefined;
- layoutDoc._forceReflow = undefined;
+ layoutDoc._layout_forceReflow = undefined;
layoutDoc._nativeHeightUnfrozen = undefined;
layoutDoc._nativeDimModifiable = undefined;
} else {
- layoutDoc._autoHeight = false;
+ layoutDoc._layout_autoHeight = false;
if (!Doc.NativeWidth(layoutDoc)) {
layoutDoc._nativeWidth = NumCast(layoutDoc._width, panelWidth);
layoutDoc._nativeHeight = NumCast(layoutDoc._height, panelHeight);
- layoutDoc._forceReflow = true;
+ layoutDoc._layout_forceReflow = true;
layoutDoc._nativeHeightUnfrozen = true;
layoutDoc._nativeDimModifiable = true;
}
@@ -1526,6 +1514,24 @@ export namespace Doc {
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))
+ .map(d => DocCast(d))
+ .filter(d => d);
+ const docs = clone ? (await Promise.all(Doc.MakeClones(list, false))).map(res => res.clone) : list;
+ if (ptx !== undefined && pty !== undefined && newPoint !== undefined) {
+ const firstx = list.length ? NumCast(list[0].x) + ptx - newPoint[0] : 0;
+ const firsty = list.length ? NumCast(list[0].y) + pty - newPoint[1] : 0;
+ docs.map(doc => {
+ doc.x = NumCast(doc.x) - firstx;
+ doc.y = NumCast(doc.y) - firsty;
+ });
+ }
+ undoable(addDocument, 'Paste Doc')(docs); // embedContainer gets set in addDocument
+ });
+ }
+
// prettier-ignore
export function toIcon(doc?: Doc, isOpen?: boolean) {
switch (StrCast(doc?.type)) {
@@ -1553,20 +1559,26 @@ export namespace Doc {
}
}
- export async function importDocument(file: File) {
+ ///
+ // imports a previously exported zip file which contains a set of documents and their assets (eg, images, videos)
+ // the 'remap' parameter determines whether the ids of the documents loaded should be kept as they were, or remapped to new ids
+ // 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 formData = new FormData();
if (file) {
formData.append('file', file);
- formData.append('remap', 'true');
+ formData.append('remap', remap.toString());
const response = await fetch(upload, { method: 'POST', body: formData });
const json = await response.json();
- console.log(json);
if (json !== 'error') {
- await DocServer.GetRefFields(json.docids as string[]);
+ const docs = await DocServer.GetRefFields(json.docids as string[]);
const doc = DocCast(await DocServer.GetRefField(json.id));
- (await DocListCastAsync(doc?.LINKS))?.forEach(link => LinkManager.Instance.addLink(link));
- doc.LINKS = undefined;
+ 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));
return doc;
}
}
@@ -1714,11 +1726,14 @@ export namespace Doc {
}
}
+export function IdToDoc(id: string) {
+ return DocCast(DocServer.GetCachedRefField(id));
+}
ScriptingGlobals.add(function idToDoc(id: string): any {
- return DocServer.GetCachedRefField(id);
+ return IdToDoc(id);
});
-ScriptingGlobals.add(function renameAlias(doc: any) {
- return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, '') + `(${doc.aliasNumber})`;
+ScriptingGlobals.add(function renameEmbedding(doc: any) {
+ return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`;
});
ScriptingGlobals.add(function getProto(doc: any) {
return Doc.GetProto(doc);
@@ -1726,8 +1741,8 @@ ScriptingGlobals.add(function getProto(doc: any) {
ScriptingGlobals.add(function getDocTemplate(doc?: any) {
return Doc.getDocTemplate(doc);
});
-ScriptingGlobals.add(function getAlias(doc: any) {
- return Doc.MakeAlias(doc);
+ScriptingGlobals.add(function getEmbedding(doc: any) {
+ return Doc.MakeEmbedding(doc);
});
ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) {
return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto);
@@ -1751,6 +1766,16 @@ ScriptingGlobals.add(function undo() {
SelectionManager.DeselectAll();
return UndoManager.Undo();
});
+
+export function ShowUndoStack() {
+ SelectionManager.DeselectAll();
+ var buffer = '';
+ UndoManager.undoStack.forEach((batch, i) => {
+ buffer += 'Batch => ' + UndoManager.undoStackNames[i] + '\n';
+ ///batch.forEach(undo => (buffer += ' ' + undo.prop + '\n'));
+ });
+ alert(buffer);
+}
ScriptingGlobals.add(function redo() {
SelectionManager.DeselectAll();
return UndoManager.Redo();