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.ts226
1 files changed, 157 insertions, 69 deletions
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 5a8a6e4b6..56b97e42f 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -7,9 +7,8 @@ import { DocServer } from '../client/DocServer';
import { DocumentType } from '../client/documents/DocumentTypes';
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 { undoable, UndoManager } from '../client/util/UndoManager';
+import { undoable } from '../client/util/UndoManager';
import { decycle } from '../decycler/decycler';
import * as JSZipUtils from '../JSZipUtils';
import { DashColor, incrementTitleCopy, intersectRect, Utils } from '../Utils';
@@ -21,6 +20,7 @@ import {
AclPrivate,
AclReadonly,
Animation,
+ AudioPlay,
CachedUpdates,
DirectLinks,
DocAcl,
@@ -48,9 +48,9 @@ import { FieldId, RefField } from './RefField';
import { RichTextField } from './RichTextField';
import { listSpec } from './Schema';
import { ComputedField, ScriptField } from './ScriptField';
-import { Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Types';
+import { BoolCast, Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Types';
import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField';
-import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, containedFieldChangedHandler } from './util';
+import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions } from './util';
import JSZip = require('jszip');
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
@@ -157,7 +157,26 @@ export function updateCachedAcls(doc: Doc) {
@scriptingGlobal
@Deserializable('Doc', updateCachedAcls, ['id'])
export class Doc extends RefField {
- @observable public static CurrentlyLoading: Doc[];
+ @observable public static RecordingEvent = 0;
+
+ // this isn't really used at the moment, but is intended to indicate whether ink stroke are passed through a gesture recognizer
+ static GetRecognizeGestures() {
+ return BoolCast(Doc.UserDoc()._recognizeGestures);
+ }
+ static SetRecognizeGestures(show: boolean) {
+ Doc.UserDoc()._recognizeGestures = show;
+ }
+
+ //
+ // This controls whether fontIconButtons will display labels under their icons or not
+ //
+ static GetShowIconLabels() {
+ return BoolCast(Doc.UserDoc()._showLabel);
+ }
+ static SetShowIconLabels(show: boolean) {
+ Doc.UserDoc()._showLabel = show;
+ }
+ @observable public static CurrentlyLoading: Doc[] = []; // this assignment doesn't work. the actual assignment happens in DocumentManager's constructor
// removes from currently loading display
@action
public static removeCurrentlyLoading(doc: Doc) {
@@ -170,9 +189,6 @@ export class Doc extends RefField {
// adds doc to currently loading display
@action
public static addCurrentlyLoading(doc: Doc) {
- if (!Doc.CurrentlyLoading) {
- Doc.CurrentlyLoading = [];
- }
if (Doc.CurrentlyLoading.indexOf(doc) === -1) {
Doc.CurrentlyLoading.push(doc);
}
@@ -205,6 +221,9 @@ export class Doc extends RefField {
public static get MyContextMenuBtns() {
return DocCast(Doc.UserDoc().myContextMenuBtns);
}
+ public static get MyTopBarBtns() {
+ return DocCast(Doc.UserDoc().myTopBarBtns);
+ }
public static get MyRecentlyClosed() {
return DocCast(Doc.UserDoc().myRecentlyClosed);
}
@@ -238,22 +257,6 @@ export class Doc extends RefField {
public static get MyFilesystem() {
return DocCast(Doc.UserDoc().myFilesystem);
}
- public static get MyFileOrphans() {
- return DocCast(Doc.UserDoc().myFileOrphans);
- }
- public static AddFileOrphan(doc: Doc) {
- if (
- doc &&
- Doc.MyFileOrphans instanceof Doc &&
- Doc.IsDataProto(doc) &&
- !Doc.IsSystem(doc) &&
- ![DocumentType.CONFIG, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(doc.type as any) &&
- !doc.isFolder &&
- !doc.annotationOn
- ) {
- Doc.AddDocToList(Doc.MyFileOrphans, undefined, doc);
- }
- }
public static get MyTools() {
return DocCast(Doc.UserDoc().myTools);
}
@@ -347,6 +350,7 @@ export class Doc extends RefField {
@observable public [DocAcl]: { [key: string]: symbol } = {};
@observable public [DocCss]: number = 0; // incrementer denoting a change to CSS layout
@observable public [DirectLinks] = new ObservableSet<Doc>();
+ @observable public [AudioPlay]: any; // meant to store sound object from Howl
@observable public [Animation]: Opt<Doc>;
@observable public [Highlight]: boolean = false;
static __Anim(Doc: Doc) {
@@ -388,7 +392,7 @@ export class Doc extends RefField {
} else {
return Cast(layoutField, Doc, null);
}
- return Cast(self[renderFieldKey + '-layout[' + templateLayoutDoc[Id] + ']'], Doc, null) || templateLayoutDoc;
+ return Cast(self[renderFieldKey + '_layout[' + templateLayoutDoc[Id] + ']'], Doc, null) || templateLayoutDoc;
}
return undefined;
}
@@ -400,6 +404,12 @@ export class Doc extends RefField {
public static set noviceMode(val) {
Doc.UserDoc().noviceMode = val;
}
+ public static get IsSharingEnabled() {
+ return Doc.UserDoc().isSharingEnabled as boolean;
+ }
+ public static set IsSharingEnabled(val) {
+ Doc.UserDoc().isSharingEnabled = val;
+ }
public static get defaultAclPrivate() {
return Doc.UserDoc().defaultAclPrivate;
}
@@ -526,6 +536,13 @@ export namespace Doc {
export function IsDelegateField(doc: Doc, fieldKey: string) {
return doc && Get(doc, fieldKey, true) !== undefined;
}
+ //
+ // this will write the value to the key on either the data doc or the embedding doc. The choice
+ // of where to write it is based on:
+ // 1) if the embedding Doc already has this field defined on it, then it will be written to the embedding
+ // 2) if the data doc has the field, then it's written there.
+ // 3) if neither already has the field, then 'defaultProto' determines whether to write it to the data doc (or the embedding)
+ //
export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) {
if (key.startsWith('_')) key = key.substring(1);
const hasProto = Doc.GetProto(doc) !== doc ? Doc.GetProto(doc) : undefined;
@@ -573,7 +590,7 @@ export namespace Doc {
// compare whether documents or their protos match
export function AreProtosEqual(doc?: Doc, other?: Doc) {
- return doc && other && Doc.GetProto(doc) === Doc.GetProto(other);
+ return doc && other && (doc === other || Doc.GetProto(doc) === Doc.GetProto(other));
}
// Gets the data document for the document. Note: this is mis-named -- it does not specifically
@@ -701,7 +718,8 @@ export namespace Doc {
}
export function BestEmbedding(doc: Doc) {
- const bestEmbedding = Doc.GetProto(doc) ? DocListCast(doc.proto_embeddings).find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail) : doc;
+ const bestEmbedding = Doc.GetProto(doc) ? [doc, ...DocListCast(doc.proto_embeddings)].find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail) : doc;
+ bestEmbedding && Doc.AddDocToList(Doc.GetProto(doc), 'protoEmbeddings', doc);
return bestEmbedding ?? Doc.MakeEmbedding(doc);
}
@@ -782,7 +800,6 @@ export namespace Doc {
copy.cloneOf = doc;
cloneMap.set(doc[Id], copy);
- Doc.AddFileOrphan(copy);
return copy;
}
export function repairClone(clone: Doc, cloneMap: Map<string, Doc>, visited: Set<Doc>) {
@@ -917,7 +934,7 @@ export namespace Doc {
// If it doesn't find the expanded layout, then it makes a delegate of the template layout and
// saves it on the data doc indexed by the template layout's id.
//
- const expandedLayoutFieldKey = templateField + '-layout[' + templateLayoutDoc[Id] + ']';
+ const expandedLayoutFieldKey = templateField + '_layout[' + templateLayoutDoc[Id] + ']';
let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
if (templateLayoutDoc.resolvedDataDoc instanceof Promise) {
@@ -985,6 +1002,59 @@ export namespace Doc {
return overwrite;
}
+ export function FindReferences(infield: Doc | List<any>, references: Set<Doc>, system: boolean | undefined) {
+ if (infield instanceof List<any>) {
+ infield.forEach(val => (val instanceof Doc || val instanceof List) && FindReferences(val, references, system));
+ return;
+ }
+ const doc = infield as Doc;
+ if (references.has(doc)) {
+ references.add(doc);
+ return;
+ }
+ const excludeLists = ['My Recently Closed', 'My Header Bar', 'My Dashboards'].includes(StrCast(doc.title));
+ if (system !== undefined && ((system && !Doc.IsSystem(doc)) || (!system && Doc.IsSystem(doc)))) return;
+ references.add(doc);
+ Object.keys(doc).forEach(key => {
+ if (key === 'proto') {
+ if (doc.proto instanceof Doc) {
+ Doc.FindReferences(doc.proto, references, system);
+ }
+ } else {
+ const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+ const field = key === 'author' ? Doc.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]);
+ if (field instanceof RefField) {
+ if (field instanceof Doc) {
+ if (key === 'myLinkDatabase') {
+ field instanceof Doc && references.add(field);
+ // skip docs that have been closed and are scheduled for garbage collection
+ } else {
+ Doc.FindReferences(field, references, system);
+ }
+ }
+ } else if (cfield instanceof ComputedField) {
+ } else if (field instanceof ObjectField) {
+ if (field instanceof Doc) {
+ Doc.FindReferences(field, references, system);
+ } else if (field instanceof List) {
+ !excludeLists && Doc.FindReferences(field, references, system);
+ } else if (field instanceof ProxyField) {
+ if (key === 'myLinkDatabase') {
+ field instanceof Doc && references.add(field);
+ // skip docs that have been closed and are scheduled for garbage collection
+ } else {
+ Doc.FindReferences(field.value, references, system);
+ }
+ } else if (field instanceof PrefetchProxy) {
+ Doc.FindReferences(field.value, references, system);
+ }
+ } else if (field instanceof Promise) {
+ debugger; //This shouldn't happend...
+ }
+ }
+ });
+ }
+
export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string, retitle = false): Doc {
const copy = new Doc(copyProtoId, true);
updateCachedAcls(copy);
@@ -1025,7 +1095,6 @@ export namespace Doc {
if (retitle) {
copy.title = incrementTitleCopy(StrCast(copy.title));
}
- Doc.AddFileOrphan(copy);
return copy;
}
@@ -1044,7 +1113,6 @@ export namespace Doc {
if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DocData], 'proto_embeddings', delegate);
title && (delegate.title = title);
delegate[Initializing] = false;
- Doc.AddFileOrphan(delegate);
return delegate;
}
return undefined;
@@ -1177,7 +1245,7 @@ export namespace Doc {
// the document containing the view layout information - will be the Document itself unless the Document has
// 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);
+ const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, 'data')}_layout[` + layout[Id] + ']'], Doc, null);
return overrideLayout || doc[DocLayout] || doc;
}
export function SetLayout(doc: Doc, layout: Doc | string) {
@@ -1309,8 +1377,13 @@ export namespace Doc {
}
export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) {
- 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';
+ const linkAnchor2 = DocCast(linkDoc.link_anchor_2);
+ const linkAnchor1 = DocCast(linkDoc.link_anchor_1);
+ if (linkDoc.link_matchEmbeddings) {
+ return [linkAnchor2, linkAnchor2.annotationOn].includes(anchorDoc) ? '2' : '1';
+ }
+ if (Doc.AreProtosEqual(linkAnchor2, anchorDoc) || Doc.AreProtosEqual(linkAnchor2.annotationOn as Doc, anchorDoc)) return '2';
+ return Doc.AreProtosEqual(linkAnchor1, anchorDoc) || Doc.AreProtosEqual(linkAnchor1.annotationOn as Doc, anchorDoc) ? '1' : '2';
}
export function linkFollowUnhighlight() {
@@ -1328,9 +1401,9 @@ export namespace Doc {
UnhighlightWatchers.push(watcher);
} else watcher();
}
- export function linkFollowHighlight(destDoc: Doc | Doc[], dataAndDisplayDocs = true, presEffect?: Doc) {
+ export function linkFollowHighlight(destDoc: Doc | Doc[], dataAndDisplayDocs = true, presentation_effect?: Doc) {
linkFollowUnhighlight();
- (destDoc instanceof Doc ? [destDoc] : destDoc).forEach(doc => Doc.HighlightDoc(doc, dataAndDisplayDocs, presEffect));
+ (destDoc instanceof Doc ? [destDoc] : destDoc).forEach(doc => Doc.HighlightDoc(doc, dataAndDisplayDocs, presentation_effect));
document.removeEventListener('pointerdown', linkFollowUnhighlight);
document.addEventListener('pointerdown', linkFollowUnhighlight);
if (UnhighlightTimer) clearTimeout(UnhighlightTimer);
@@ -1345,11 +1418,11 @@ export namespace Doc {
if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return false;
return doc[Highlight] || Doc.GetProto(doc)[Highlight];
}
- export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true, presEffect?: Doc) {
+ export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true, presentation_effect?: Doc) {
runInAction(() => {
highlightedDocs.add(doc);
doc[Highlight] = true;
- doc[Animation] = presEffect;
+ doc[Animation] = presentation_effect;
if (dataAndDisplayDocs) {
highlightedDocs.add(Doc.GetProto(doc));
Doc.GetProto(doc)[Highlight] = true;
@@ -1390,18 +1463,36 @@ export namespace Doc {
const isTransparent = (color: string) => color !== '' && DashColor(color).alpha() !== 1;
return isTransparent(StrCast(doc[key]));
}
+ if (key === '-linkedTo') {
+ // links are not a field value, so handled here. value is an expression of form ([field=]idToDoc("..."))
+ const allLinks = LinkManager.Instance.getAllRelatedLinks(doc);
+ const matchLink = (value: string, anchor: Doc) => {
+ const linkedToExp = value?.split('=');
+ if (linkedToExp.length === 1) return Field.toScriptString(anchor) === value;
+ return Field.toScriptString(DocCast(anchor[linkedToExp[0]])) === linkedToExp[1];
+ };
+ // prettier-ignore
+ return (value === Doc.FilterNone && !allLinks.length) ||
+ (value === Doc.FilterAny && !!allLinks.length) ||
+ (allLinks.some(link => matchLink(value,DocCast(link.link_anchor_1)) ||
+ matchLink(value,DocCast(link.link_anchor_2)) ));
+ }
if (typeof value === 'string') {
value = value.replace(`,${Utils.noRecursionHack}`, '');
}
const fieldVal = doc[key];
+ // prettier-ignore
+ if ((value === Doc.FilterAny && fieldVal !== undefined) ||
+ (value === Doc.FilterNone && fieldVal === undefined)) {
+ return true;
+ }
if (Cast(fieldVal, listSpec('string'), []).length) {
- const vals = Cast(fieldVal, listSpec('string'), []);
+ const vals = StrListCast(fieldVal);
const docs = vals.some(v => (v as any) instanceof Doc);
if (docs) return value === Field.toString(fieldVal as Field);
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) || (value === String.fromCharCode(127) + '--undefined--' && fieldVal === undefined); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
+ return Field.toString(fieldVal as Field).includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
}
export function deiconifyView(doc: Doc) {
@@ -1414,24 +1505,40 @@ export namespace Doc {
prevLayout === 'icon' && (doc.deiconifyLayout = undefined);
doc.layout_fieldKey = deiconify || 'layout';
}
- export function setDocRangeFilter(container: Opt<Doc>, key: string, range?: number[]) {
+ export function setDocRangeFilter(container: Opt<Doc>, key: string, range?: readonly number[], modifiers?: 'remove') {
+ //, modifiers: 'remove' | 'set'
if (!container) return;
+
const childFiltersByRanges = Cast(container._childFiltersByRanges, listSpec('string'), []);
+
for (let i = 0; i < childFiltersByRanges.length; i += 3) {
if (childFiltersByRanges[i] === key) {
+ console.log('this is key inside childfilters by range ' + key);
childFiltersByRanges.splice(i, 3);
+ console.log('this is child filters by range ' + childFiltersByRanges);
break;
}
}
if (range !== undefined) {
+ console.log('in doc.ts in set range filter');
childFiltersByRanges.push(key);
childFiltersByRanges.push(range[0].toString());
childFiltersByRanges.push(range[1].toString());
container._childFiltersByRanges = new List<string>(childFiltersByRanges);
+ console.log('this is child filters by range ' + childFiltersByRanges[0] + ',' + childFiltersByRanges[1] + ',' + childFiltersByRanges[2]);
+ console.log('this is new list ' + container._childFiltersByRange);
}
+
+ if (modifiers) {
+ childFiltersByRanges.splice(0, 3);
+ container._childFiltersByRanges = new List<string>(childFiltersByRanges);
+ }
+ console.log('this is child filters by range END' + childFiltersByRanges[0] + ',' + childFiltersByRanges[1] + ',' + childFiltersByRanges[2]);
}
export const FilterSep = '::';
+ export const FilterAny = '--any--';
+ export const FilterNone = '--undefined--';
// filters document in a container collection:
// all documents with the specified value for the specified key are included/excluded
@@ -1443,8 +1550,8 @@ export namespace Doc {
runInAction(() => {
for (let i = 0; i < childFilters.length; i++) {
const fields = childFilters[i].split(FilterSep); // split key:value:modifier
- if (fields[0] === key && (fields[1] === value || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) {
- if (fields[2] === modifiers && modifiers && fields[1] === value) {
+ if (fields[0] === key && (fields[1] === value.toString() || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) {
+ if (fields[2] === modifiers && modifiers && fields[1] === value.toString()) {
if (toggle) modifiers = 'remove';
else return;
}
@@ -1542,7 +1649,7 @@ export namespace Doc {
case DocumentType.COMPARISON: return 'columns';
case DocumentType.RTF: return 'sticky-note';
case DocumentType.COL:
- const folder: IconProp = isOpen === true ? 'folder-open' : isOpen === false ? 'folder' : 'question';
+ const folder: IconProp = isOpen === true ? 'folder-open' : isOpen === false ? 'folder' : doc?.title==='Untitled Collection'? 'object-group': 'chalkboard';
const chevron: IconProp = isOpen === true ? 'chevron-down' : isOpen === false ? 'chevron-right' : 'question';
return !doc?.isFolder ? folder : chevron;
case DocumentType.WEB: return 'globe-asia';
@@ -1558,6 +1665,10 @@ export namespace Doc {
case DocumentType.PDF: return 'file-pdf';
case DocumentType.LINK: return 'link';
case DocumentType.MAP: return 'map-marker-alt';
+ case DocumentType.DATAVIZ: return 'chart-bar';
+ case DocumentType.EQUATION: return 'calculator';
+ case DocumentType.SIMULATION: return 'rocket';
+ case DocumentType.CONFIG: return 'question-circle';
default: return 'question';
}
}
@@ -1765,24 +1876,6 @@ ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) {
ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) {
return Doc.AreProtosEqual(doc1, doc2);
});
-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();
-});
ScriptingGlobals.add(function DOC(id: string) {
console.log("Can't parse a document id in a script");
return 'invalid';
@@ -1797,12 +1890,7 @@ ScriptingGlobals.add(function activePresentationItem() {
const curPres = Doc.ActivePresentation;
return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
});
-ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
- const docs = SelectionManager.Views()
- .map(dv => dv.props.Document)
- .filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)));
- return docs.length ? new List(docs) : prevValue;
-});
+
ScriptingGlobals.add(function setDocFilter(container: Doc, key: string, value: any, modifiers: 'match' | 'check' | 'x' | 'remove') {
Doc.setDocFilter(container, key, value, modifiers);
});