aboutsummaryrefslogtreecommitdiff
path: root/src/new_fields/Doc.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/new_fields/Doc.ts')
-rw-r--r--src/new_fields/Doc.ts296
1 files changed, 195 insertions, 101 deletions
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 1d529ac84..418863bcc 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -1,8 +1,8 @@
-import { observable, ObservableMap, runInAction } from "mobx";
+import { observable, ObservableMap, runInAction, action } from "mobx";
import { alias, map, serializable } from "serializr";
import { DocServer } from "../client/DocServer";
import { DocumentType } from "../client/documents/DocumentTypes";
-import { CompileScript, Scripting, scriptingGlobal } from "../client/util/Scripting";
+import { Scripting, scriptingGlobal } from "../client/util/Scripting";
import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from "../client/util/SerializationHelper";
import { Copy, HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, Update } from "./FieldSymbols";
import { List } from "./List";
@@ -13,6 +13,7 @@ import { listSpec } from "./Schema";
import { ComputedField } from "./ScriptField";
import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast, ToConstructor } from "./Types";
import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util";
+import { intersectRect } from "../Utils";
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
@@ -62,6 +63,10 @@ export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) {
return list ? Promise.all(list).then(() => list) : Promise.resolve(defaultValue);
}
+export async function DocCastAsync(field: FieldResult): Promise<Opt<Doc>> {
+ return Cast(field, Doc);
+}
+
export function DocListCast(field: FieldResult): Doc[] {
return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[];
}
@@ -143,7 +148,7 @@ export class Doc extends RefField {
private [Self] = this;
private [SelfProxy]: any;
- public [WidthSym] = () => NumCast(this[SelfProxy].width); // bcz: is this the right way to access width/height? it didn't work with : this.width
+ public [WidthSym] = () => NumCast(this[SelfProxy].width);
public [HeightSym] = () => NumCast(this[SelfProxy].height);
[ToScriptString]() {
@@ -300,15 +305,15 @@ export namespace Doc {
export function AreProtosEqual(doc?: Doc, other?: Doc) {
if (!doc || !other) return false;
let r = (doc === other);
- let r2 = (doc.proto === other);
- let r3 = (other.proto === doc);
- let r4 = (doc.proto === other.proto && other.proto !== undefined);
+ let r2 = (Doc.GetProto(doc) === other);
+ let r3 = (Doc.GetProto(other) === doc);
+ let r4 = (Doc.GetProto(doc) === Doc.GetProto(other) && Doc.GetProto(other) !== undefined);
return r || r2 || r3 || r4;
}
// gets the document's prototype or returns the document if it is a prototype
export function GetProto(doc: Doc) {
- return Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc);
+ return doc && (Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc));
}
export function GetDataDoc(doc: Doc): Doc {
let proto = Doc.GetProto(doc);
@@ -327,14 +332,31 @@ export namespace Doc {
return Array.from(results);
}
- export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) {
- if (target[key] === undefined) {
- Doc.GetProto(target)[key] = new List<Doc>();
+ export function IndexOf(toFind: Doc, list: Doc[]) {
+ return list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind));
+ }
+ export function RemoveDocFromList(listDoc: Doc, key: string, doc: Doc) {
+ if (listDoc[key] === undefined) {
+ Doc.GetProto(listDoc)[key] = new List<Doc>();
+ }
+ let list = Cast(listDoc[key], listSpec(Doc));
+ if (list) {
+ let ind = list.indexOf(doc);
+ if (ind !== -1) {
+ list.splice(ind, 1);
+ return true;
+ }
+ }
+ return false;
+ }
+ export function AddDocToList(listDoc: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) {
+ if (listDoc[key] === undefined) {
+ Doc.GetProto(listDoc)[key] = new List<Doc>();
}
- let list = Cast(target[key], listSpec(Doc));
+ let list = Cast(listDoc[key], listSpec(Doc));
if (list) {
if (allowDuplicates !== true) {
- let pind = list.reduce((l, d, i) => d instanceof Doc && Doc.AreProtosEqual(d, doc) ? i : l, -1);
+ let pind = list.reduce((l, d, i) => d instanceof Doc && d[Id] === doc[Id] ? i : l, -1);
if (pind !== -1) {
list.splice(pind, 1);
}
@@ -373,13 +395,13 @@ export namespace Doc {
}
//
- // Resolves a reference to a field by returning 'doc' if field extension is specified,
+ // Resolves a reference to a field by returning 'doc' if no field extension is specified,
// otherwise, it returns the extension document stored in doc.<fieldKey>_ext.
// This mechanism allows any fields to be extended with an extension document that can
// be used to capture field-specific metadata. For example, an image field can be extended
// to store annotations, ink, and other data.
//
- export function resolvedFieldDataDoc(doc: Doc, fieldKey: string, fieldExt: string) {
+ export function fieldExtensionDoc(doc: Doc, fieldKey: string, fieldExt: string = "yes") {
return fieldExt && doc[fieldKey + "_ext"] instanceof Doc ? doc[fieldKey + "_ext"] as Doc : doc;
}
@@ -396,26 +418,28 @@ export namespace Doc {
return docExtensionForField;
}
- export function UpdateDocumentExtensionForField(doc: Doc, fieldKey: string) {
+ export function UpdateDocumentExtensionForField(doc: Doc, fieldKey: string, immediate: boolean = false) {
let docExtensionForField = doc[fieldKey + "_ext"] as Doc;
if (docExtensionForField === undefined) {
- setTimeout(() => {
- CreateDocumentExtensionForField(doc, fieldKey);
- }, 0);
+ if (immediate) CreateDocumentExtensionForField(doc, fieldKey);
+ else setTimeout(() => CreateDocumentExtensionForField(doc, fieldKey), 0);
} else if (doc instanceof Doc) { // backward compatibility -- add fields for docs that don't have them already
docExtensionForField.extendsDoc === undefined && setTimeout(() => docExtensionForField.extendsDoc = doc, 0);
docExtensionForField.type === undefined && setTimeout(() => docExtensionForField.type = DocumentType.EXTENSION, 0);
}
}
+ export function MakeTitled(title: string) {
+ let doc = new Doc();
+ doc.title = title;
+ return doc;
+ }
export function MakeAlias(doc: Doc) {
let alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc) : Doc.MakeDelegate(doc);
- let aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
- let script = `return renameAlias(self, ${aliasNumber})`;
- //let script = "StrCast(self.title).replace(/\\([0-9]*\\)/, \"\") + `(${n})`";
- let compiled = CompileScript(script, { params: { this: "Doc" }, capturedVariables: { self: doc }, typecheck: false });
- if (compiled.compiled) {
- alias.title = new ComputedField(compiled);
+ if (alias.layout instanceof Doc) {
+ alias.layout = Doc.MakeAlias(alias.layout);
}
+ let aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
+ alias.title = ComputedField.MakeFunction(`renameAlias(this, ${aliasNumber})`);
return alias;
}
@@ -466,19 +490,42 @@ export namespace Doc {
let resolvedDataDoc = !doc.isTemplate && dataDoc !== doc && dataDoc ? Doc.GetDataDoc(dataDoc) : undefined;
if (resolvedDataDoc && Doc.WillExpandTemplateLayout(childDocLayout, resolvedDataDoc)) {
Doc.UpdateDocumentExtensionForField(resolvedDataDoc, fieldKey);
- let fieldExtensionDoc = Doc.resolvedFieldDataDoc(resolvedDataDoc, StrCast(childDocLayout.templateField, StrCast(childDocLayout.title)), "dummy");
+ let fieldExtensionDoc = Doc.fieldExtensionDoc(resolvedDataDoc, StrCast(childDocLayout.templateField, StrCast(childDocLayout.title)), "dummy");
layoutDoc = Doc.expandTemplateLayout(childDocLayout, fieldExtensionDoc !== resolvedDataDoc ? fieldExtensionDoc : undefined);
} else layoutDoc = Doc.expandTemplateLayout(childDocLayout, resolvedDataDoc);
return { layout: layoutDoc, data: resolvedDataDoc };
}
- export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc {
- const copy = new Doc;
+ export function Overwrite(doc: Doc, overwrite: Doc, copyProto: boolean = false): Doc {
Object.keys(doc).forEach(key => {
const field = ProxyField.WithoutProxy(() => doc[key]);
if (key === "proto" && copyProto) {
- if (field instanceof Doc) {
- copy[key] = Doc.MakeCopy(field);
+ if (doc.proto instanceof Doc && overwrite.proto instanceof Doc) {
+ overwrite[key] = Doc.Overwrite(doc[key]!, overwrite.proto);
+ }
+ } else {
+ if (field instanceof RefField) {
+ overwrite[key] = field;
+ } else if (field instanceof ObjectField) {
+ overwrite[key] = ObjectField.MakeCopy(field);
+ } else if (field instanceof Promise) {
+ debugger; //This shouldn't happend...
+ } else {
+ overwrite[key] = field;
+ }
+ }
+ });
+
+ return overwrite;
+ }
+
+ export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string): Doc {
+ const copy = new Doc(copyProtoId, true);
+ Object.keys(doc).forEach(key => {
+ const field = 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) {
@@ -510,21 +557,9 @@ export namespace Doc {
let _applyCount: number = 0;
export function ApplyTemplate(templateDoc: Doc) {
- if (!templateDoc) return undefined;
- let datadoc = new Doc();
- let otherdoc = Doc.MakeDelegate(datadoc);
- otherdoc.width = templateDoc[WidthSym]();
- otherdoc.height = templateDoc[HeightSym]();
- otherdoc.title = templateDoc.title + "(..." + _applyCount++ + ")";
- otherdoc.layout = Doc.MakeDelegate(templateDoc);
- otherdoc.miniLayout = StrCast(templateDoc.miniLayout);
- otherdoc.detailedLayout = otherdoc.layout;
- otherdoc.type = DocumentType.TEMPLATE;
- !templateDoc.nativeWidth && (otherdoc.nativeWidth = 0);
- !templateDoc.nativeHeight && (otherdoc.nativeHeight = 0);
- return otherdoc;
- }
- export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetData?: Doc) {
+ return !templateDoc ? undefined : ApplyTemplateTo(templateDoc, Doc.MakeDelegate(new Doc()), templateDoc.title + "(..." + _applyCount++ + ")");
+ }
+ export function ApplyTemplateTo(templateDoc: Doc, target: Doc, titleTarget: string | undefined = undefined) {
if (!templateDoc) {
target.layout = undefined;
target.nativeWidth = undefined;
@@ -533,80 +568,90 @@ export namespace Doc {
target.type = undefined;
return;
}
- let temp = Doc.MakeDelegate(templateDoc);
- target.nativeWidth = Doc.GetProto(target).nativeWidth = undefined;
- target.nativeHeight = Doc.GetProto(target).nativeHeight = undefined;
- !templateDoc.nativeWidth && (target.nativeWidth = 0);
- !templateDoc.nativeHeight && (target.nativeHeight = 0);
+
+ let layoutCustom = Doc.MakeTitled("layoutCustom");
+ let layoutCustomLayout = Doc.MakeDelegate(templateDoc);
+
+ titleTarget && (target.title = titleTarget);
+ target.type = DocumentType.TEMPLATE;
target.width = templateDoc.width;
target.height = templateDoc.height;
+ target.nativeWidth = templateDoc.nativeWidth ? templateDoc.nativeWidth : 0;
+ target.nativeHeight = templateDoc.nativeHeight ? templateDoc.nativeHeight : 0;
+ target.ignoreAspect = templateDoc.nativeWidth ? true : false;
target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy]();
- target.type = DocumentType.TEMPLATE;
- if (targetData && targetData.layout === target) {
- targetData.layout = temp;
- targetData.miniLayout = StrCast(templateDoc.miniLayout);
- targetData.detailedLayout = targetData.layout;
- } else {
- target.layout = temp;
- target.miniLayout = StrCast(templateDoc.miniLayout);
- target.detailedLayout = target.layout;
- }
+ target.layout = layoutCustomLayout;
+ target.backgroundLayout = layoutCustomLayout.backgroundLayout;
+
+ target.layoutNative = Cast(templateDoc.layoutNative, Doc) as Doc;
+ target.layoutCustom = layoutCustom;
+ return target;
}
- export function MakeTemplate(fieldTemplate: Doc, metaKey: string, templateDataDoc: Doc) {
+ export function MakeMetadataFieldTemplate(fieldTemplate: Doc, templateDataDoc: Doc, suppressTitle: boolean = false) {
// move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
+ let metadataFieldName = StrCast(fieldTemplate.title).replace(/^-/, "");
let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
let fieldLayoutDoc = fieldTemplate;
if (fieldTemplate.layout instanceof Doc) {
fieldLayoutDoc = Doc.MakeDelegate(fieldTemplate.layout);
}
- let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
if (backgroundLayout) {
- backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
+ backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metadataFieldName}"}`);
}
- let layoutDelegate = fieldTemplate.layout instanceof Doc ? fieldLayoutDoc : fieldTemplate;
- layoutDelegate.layout = layout;
-
- fieldTemplate.templateField = metaKey;
- fieldTemplate.title = metaKey;
+ fieldTemplate.templateField = metadataFieldName;
+ fieldTemplate.title = metadataFieldName;
fieldTemplate.isTemplate = true;
- fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;
fieldTemplate.backgroundLayout = backgroundLayout;
/* move certain layout properties from the original data doc to the template layout to avoid
inheriting them from the template's data doc which may also define these fields for its own use.
*/
- fieldTemplate.ignoreAspect = BoolCast(fieldTemplate.ignoreAspect);
+ fieldTemplate.ignoreAspect = fieldTemplate.ignoreAspect === undefined ? undefined : BoolCast(fieldTemplate.ignoreAspect);
fieldTemplate.singleColumn = BoolCast(fieldTemplate.singleColumn);
fieldTemplate.nativeWidth = Cast(fieldTemplate.nativeWidth, "number");
fieldTemplate.nativeHeight = Cast(fieldTemplate.nativeHeight, "number");
- fieldTemplate.showTitle = "title";
- setTimeout(() => fieldTemplate.proto = templateDataDoc);
+ fieldTemplate.panX = 0;
+ fieldTemplate.panY = 0;
+ fieldTemplate.scale = 1;
+ fieldTemplate.showTitle = suppressTitle ? undefined : "title";
+ let data = fieldTemplate.data;
+ setTimeout(action(() => {
+ !templateDataDoc[metadataFieldName] && data instanceof ObjectField && (Doc.GetProto(templateDataDoc)[metadataFieldName] = ObjectField.MakeCopy(data));
+ let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metadataFieldName}"}`);
+ let layoutDelegate = fieldTemplate.layout instanceof Doc ? fieldLayoutDoc : fieldTemplate;
+ layoutDelegate.layout = layout;
+ fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;
+ if (fieldTemplate.backgroundColor !== templateDataDoc.defaultBackgroundColor) fieldTemplate.defaultBackgroundColor = fieldTemplate.backgroundColor;
+ fieldTemplate.proto = templateDataDoc;
+ }), 0);
+ }
+
+ export function overlapping(doc: Doc, doc2: Doc, clusterDistance: number) {
+ var x2 = NumCast(doc2.x) - clusterDistance;
+ var y2 = NumCast(doc2.y) - clusterDistance;
+ var w2 = NumCast(doc2.width) + clusterDistance;
+ var h2 = NumCast(doc2.height) + clusterDistance;
+ var x = NumCast(doc.x) - clusterDistance;
+ var y = NumCast(doc.y) - clusterDistance;
+ var w = NumCast(doc.width) + clusterDistance;
+ var h = NumCast(doc.height) + clusterDistance;
+ return doc.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 });
+ }
+
+ export function isBrushedHighlightedDegree(doc: Doc) {
+ if (Doc.IsHighlighted(doc)) {
+ return 6;
+ }
+ else {
+ return Doc.IsBrushedDegree(doc);
+ }
}
- export function ToggleDetailLayout(d: Doc) {
- runInAction(async () => {
- let miniLayout = await PromiseValue(d.miniLayout);
- let detailLayout = await PromiseValue(d.detailedLayout);
- d.layout !== miniLayout ? miniLayout && (d.layout = d.miniLayout) : detailLayout && (d.layout = detailLayout);
- if (d.layout === detailLayout) Doc.GetProto(d).nativeWidth = Doc.GetProto(d).nativeHeight = undefined;
- });
- }
- export function UseDetailLayout(d: Doc) {
- runInAction(async () => {
- let detailLayout = await d.detailedLayout;
- if (detailLayout) {
- d.layout = detailLayout;
- d.nativeWidth = d.nativeHeight = undefined;
- if (detailLayout instanceof Doc) {
- let delegDetailLayout = Doc.MakeDelegate(detailLayout);
- d.layout = delegDetailLayout;
- delegDetailLayout.layout = await delegDetailLayout.detailedLayout;
- }
- }
- });
+ export class DocBrush {
+ @observable BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
}
-
+ const brushManager = new DocBrush();
export class DocData {
@observable _user_doc: Doc = undefined!;
@@ -616,24 +661,73 @@ export namespace Doc {
export function UserDoc(): Doc { return manager._user_doc; }
export function SetUserDoc(doc: Doc) { manager._user_doc = doc; }
export function IsBrushed(doc: Doc) {
- return manager.BrushedDoc.has(doc) || manager.BrushedDoc.has(Doc.GetDataDoc(doc));
+ return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetDataDoc(doc));
}
export function IsBrushedDegree(doc: Doc) {
- return manager.BrushedDoc.has(Doc.GetDataDoc(doc)) ? 2 : manager.BrushedDoc.has(doc) ? 1 : 0;
+ return brushManager.BrushedDoc.has(Doc.GetDataDoc(doc)) ? 2 : brushManager.BrushedDoc.has(doc) ? 1 : 0;
}
export function BrushDoc(doc: Doc) {
- manager.BrushedDoc.set(doc, true);
- manager.BrushedDoc.set(Doc.GetDataDoc(doc), true);
+ brushManager.BrushedDoc.set(doc, true);
+ brushManager.BrushedDoc.set(Doc.GetDataDoc(doc), true);
+ return doc;
}
export function UnBrushDoc(doc: Doc) {
- manager.BrushedDoc.delete(doc);
- manager.BrushedDoc.delete(Doc.GetDataDoc(doc));
+ brushManager.BrushedDoc.delete(doc);
+ brushManager.BrushedDoc.delete(Doc.GetDataDoc(doc));
+ return doc;
+ }
+
+ export function linkFollowUnhighlight() {
+ Doc.UnhighlightAll();
+ document.removeEventListener("pointerdown", linkFollowUnhighlight);
+ }
+
+ let dt = 0;
+ export function linkFollowHighlight(destDoc: Doc) {
+ linkFollowUnhighlight();
+ Doc.HighlightDoc(destDoc);
+ document.removeEventListener("pointerdown", linkFollowUnhighlight);
+ document.addEventListener("pointerdown", linkFollowUnhighlight);
+ let x = dt = Date.now();
+ window.setTimeout(() => dt === x && linkFollowUnhighlight(), 5000);
+ }
+
+ export class HighlightBrush {
+ @observable HighlightedDoc: Map<Doc, boolean> = new Map();
+ }
+ const highlightManager = new HighlightBrush();
+ export function IsHighlighted(doc: Doc) {
+ let IsHighlighted = highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetDataDoc(doc));
+ return IsHighlighted;
+ }
+ export function HighlightDoc(doc: Doc) {
+ runInAction(() => {
+ highlightManager.HighlightedDoc.set(doc, true);
+ highlightManager.HighlightedDoc.set(Doc.GetDataDoc(doc), true);
+ });
+ }
+ export function UnHighlightDoc(doc: Doc) {
+ runInAction(() => {
+ highlightManager.HighlightedDoc.set(doc, false);
+ highlightManager.HighlightedDoc.set(Doc.GetDataDoc(doc), false);
+ });
+ }
+ export function UnhighlightAll() {
+ let mapEntries = highlightManager.HighlightedDoc.keys();
+ let docEntry: IteratorResult<Doc>;
+ while (!(docEntry = mapEntries.next()).done) {
+ let targetDoc = docEntry.value;
+ targetDoc && Doc.UnHighlightDoc(targetDoc);
+ }
+
}
export function UnBrushAllDocs() {
manager.BrushedDoc.clear();
}
}
-Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(doc.title).replace(/\([0-9]*\)/, "") + `(${n})`; });
+Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; });
Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); });
+Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); });
Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); });
-Scripting.addGlobal(function aliasDocs(field: any) { return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d))); }); \ No newline at end of file
+Scripting.addGlobal(function aliasDocs(field: any) { return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d))); });
+Scripting.addGlobal(function docList(field: any) { return DocListCast(field); }); \ No newline at end of file