diff options
Diffstat (limited to 'src/fields/Doc.ts')
-rw-r--r-- | src/fields/Doc.ts | 400 |
1 files changed, 228 insertions, 172 deletions
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 7cc7e0d2a..824aa014b 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -9,11 +9,40 @@ 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 { + 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(/^_/, '')); @@ -41,7 +69,7 @@ export namespace Field { 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); @@ -107,6 +135,7 @@ 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'); @@ -137,15 +166,16 @@ export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol }> // this recursively updates all protos as well. export function updateCachedAcls(doc: Doc) { if (!doc) return; + const target = (doc as any)?.__fields ?? 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 +237,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 +271,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 +321,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 +348,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 +389,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 +405,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 +534,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 +550,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 +598,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 +614,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 +691,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,25 +704,25 @@ 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. @@ -741,7 +769,7 @@ export namespace Doc { if (docAtKey instanceof Doc) { if (pruneDocs.includes(docAtKey)) { // prune doc and do nothing - } else if (!Doc.IsSystem(docAtKey) && (key.startsWith('layout') || ['context', 'annotationOn', 'proto'].includes(key) || ((key === 'anchor1' || key === 'anchor2') && doc.author === Doc.CurrentUserEmail))) { + } 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); @@ -759,10 +787,11 @@ 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, pruneDocs, cloneLinks)); } @@ -800,7 +829,7 @@ 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'], doc.context ? [DocCast(doc.context)] : [], 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()); linkedDocs.map((link: Doc) => LinkManager.Instance.addLink(link, true)); @@ -916,7 +945,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( @@ -955,7 +984,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) { @@ -976,41 +1005,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)); @@ -1031,7 +1059,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); @@ -1049,13 +1077,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; @@ -1067,9 +1095,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; @@ -1159,7 +1187,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 = ''; @@ -1169,37 +1197,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).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; } @@ -1269,8 +1297,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; } @@ -1302,8 +1330,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() { @@ -1336,16 +1364,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; } }); } @@ -1355,8 +1383,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; }); }); } @@ -1398,29 +1426,29 @@ export namespace Doc { } 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); } } @@ -1429,35 +1457,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])]; } } } @@ -1469,18 +1497,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; } @@ -1508,15 +1536,33 @@ 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)) { + 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'; @@ -1708,8 +1754,8 @@ export function IdToDoc(id: string) { ScriptingGlobals.add(function idToDoc(id: string): any { 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); @@ -1717,8 +1763,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); @@ -1742,6 +1788,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(); |