aboutsummaryrefslogtreecommitdiff
path: root/src/new_fields
diff options
context:
space:
mode:
Diffstat (limited to 'src/new_fields')
-rw-r--r--src/new_fields/Doc.ts395
-rw-r--r--src/new_fields/InkField.ts1
-rw-r--r--src/new_fields/ObjectField.ts1
-rw-r--r--src/new_fields/RichTextField.ts16
-rw-r--r--src/new_fields/RichTextUtils.ts43
-rw-r--r--src/new_fields/Schema.ts2
-rw-r--r--src/new_fields/ScriptField.ts13
-rw-r--r--src/new_fields/URLField.ts2
-rw-r--r--src/new_fields/documentSchemas.ts59
-rw-r--r--src/new_fields/util.ts29
10 files changed, 339 insertions, 222 deletions
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 5f78636a9..85926e393 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -1,22 +1,23 @@
-import { observable, ObservableMap, runInAction } from "mobx";
+import { action, computed, observable, ObservableMap, runInAction } from "mobx";
+import { computedFn } from "mobx-utils";
import { alias, map, serializable } from "serializr";
import { DocServer } from "../client/DocServer";
import { DocumentType } from "../client/documents/DocumentTypes";
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, ToString, Update } from "./FieldSymbols";
+import { UndoManager } from "../client/util/UndoManager";
+import { intersectRect } from "../Utils";
+import { HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update, Copy } from "./FieldSymbols";
import { List } from "./List";
import { ObjectField } from "./ObjectField";
import { PrefetchProxy, ProxyField } from "./Proxy";
import { FieldId, RefField } from "./RefField";
+import { RichTextField } from "./RichTextField";
import { listSpec } from "./Schema";
-import { ComputedField, ScriptField } from "./ScriptField";
-import { BoolCast, Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
+import { ComputedField } from "./ScriptField";
+import { Cast, FieldValue, NumCast, StrCast, ToConstructor, ScriptCast } from "./Types";
import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util";
-import { intersectRect } from "../Utils";
-import { UndoManager } from "../client/util/UndoManager";
-import { computedFn } from "mobx-utils";
-import { RichTextField } from "./RichTextField";
+import { Docs } from "../client/documents/Documents";
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
@@ -47,7 +48,7 @@ export namespace Field {
} else if (field instanceof RefField) {
return field[ToString]();
}
- return "(null)";
+ return "";
}
export function IsField(field: any): field is Field;
export function IsField(field: any, includeUndefined: true): field is Field | undefined;
@@ -89,6 +90,7 @@ export function DocListCast(field: FieldResult): Doc[] {
export const WidthSym = Symbol("Width");
export const HeightSym = Symbol("Height");
export const DataSym = Symbol("Data");
+export const LayoutSym = Symbol("Layout");
export const UpdatingFromServer = Symbol("UpdatingFromServer");
const CachedUpdates = Symbol("Cached updates");
@@ -110,8 +112,16 @@ export class Doc extends RefField {
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) => key in target.__fields,
- ownKeys: target => Object.keys(target.__fields),
+ ownKeys: target => {
+ const obj = {} as any;
+ Object.assign(obj, target.___fields);
+ runInAction(() => obj.__LAYOUT__ = target.__LAYOUT__);
+ return Object.keys(obj);
+ },
getOwnPropertyDescriptor: (target, prop) => {
+ if (prop.toString() === "__LAYOUT__") {
+ return Reflect.getOwnPropertyDescriptor(target, prop);
+ }
if (prop in target.__fields) {
return {
configurable: true,//TODO Should configurable be true?
@@ -135,10 +145,7 @@ export class Doc extends RefField {
[key: string]: FieldResult;
@serializable(alias("fields", map(autoObject(), { afterDeserialize: afterDocDeserialize })))
- private get __fields() {
- return this.___fields;
- }
-
+ private get __fields() { return this.___fields; }
private set __fields(value) {
this.___fields = value;
for (const key in value) {
@@ -156,25 +163,37 @@ export class Doc extends RefField {
private [UpdatingFromServer]: boolean = false;
private [Update] = (diff: any) => {
- if (this[UpdatingFromServer]) {
- return;
- }
- DocServer.UpdateField(this[Id], diff);
+ !this[UpdatingFromServer] && DocServer.UpdateField(this[Id], diff);
}
private [Self] = this;
private [SelfProxy]: any;
public [WidthSym] = () => NumCast(this[SelfProxy]._width);
public [HeightSym] = () => NumCast(this[SelfProxy]._height);
- public get [DataSym]() { return Cast(this[SelfProxy].resolvedDataDoc, Doc, null) || this[SelfProxy]; }
-
- [ToScriptString]() {
- return "invalid";
- }
- [ToString]() {
- return "Doc";
+ public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; }
+ public get [DataSym]() {
+ const self = this[SelfProxy];
+ return self.resolvedDataDoc && !self.isTemplateForField ? self :
+ Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self);
+ }
+ @computed get __LAYOUT__() {
+ const templateLayoutDoc = Cast(Doc.LayoutField(this[SelfProxy]), Doc, null);
+ if (templateLayoutDoc) {
+ let renderFieldKey: any;
+ const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layoutKey, "layout")];
+ if (typeof layoutField === "string") {
+ renderFieldKey = layoutField.split("'")[1];
+ } else {
+ return Cast(layoutField, Doc, null);
+ }
+ return Cast(this[SelfProxy][renderFieldKey + "-layout[" + templateLayoutDoc[Id] + "]"], Doc, null) || templateLayoutDoc;
+ }
+ return undefined;
}
+ [ToScriptString]() { return `DOC-"${this[Self][Id]}"-`; }
+ [ToString]() { return `Doc(${this.title})`; }
+
private [CachedUpdates]: { [key: string]: () => void | Promise<any> } = {};
public static CurrentUserEmail: string = "";
public async [HandleUpdate](diff: any) {
@@ -260,8 +279,7 @@ export namespace Doc {
export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldResult {
try {
- const self = doc[Self];
- return getField(self, key, ignoreProto);
+ return getField(doc[Self], key, ignoreProto);
} catch {
return doc;
}
@@ -330,9 +348,15 @@ export namespace Doc {
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 && (Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || 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').
+ export function GetProto(doc: Doc): Doc {
+ if (doc instanceof Promise) {
+ console.log("GetProto: error: got Promise insead of Doc");
+ }
+ const proto = doc && (Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc));
+ return proto === doc ? proto : Doc.GetProto(proto);
}
export function GetDataDoc(doc: Doc): Doc {
const proto = Doc.GetProto(doc);
@@ -356,7 +380,8 @@ export namespace Doc {
index = allowProtos && index !== -1 ? index : list.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, toFind)) ? i : p, -1);
return index; // list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind));
}
- export function RemoveDocFromList(listDoc: Doc, key: string, doc: Doc) {
+ export function RemoveDocFromList(listDoc: Doc, fieldKey: string | undefined, doc: Doc) {
+ const key = fieldKey ? fieldKey : Doc.LayoutFieldKey(listDoc);
if (listDoc[key] === undefined) {
Doc.GetProto(listDoc)[key] = new List<Doc>();
}
@@ -370,7 +395,8 @@ export namespace Doc {
}
return false;
}
- export function AddDocToList(listDoc: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) {
+ export function AddDocToList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) {
+ const key = fieldKey ? fieldKey : Doc.LayoutFieldKey(listDoc);
if (listDoc[key] === undefined) {
Doc.GetProto(listDoc)[key] = new List<Doc>();
}
@@ -416,37 +442,39 @@ export namespace Doc {
return bounds;
}
- export function MakeTitled(title: string) {
- const doc = new Doc();
- doc.title = title;
- return doc;
- }
- export function MakeAlias(doc: Doc) {
- const alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc) : Doc.MakeDelegate(doc);
- const layout = Doc.Layout(alias);
- if (layout instanceof Doc && layout !== alias) {
+ export function MakeAlias(doc: Doc, id?: string) {
+ const alias = !GetT(doc, "isPrototype", "boolean", true) ? 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));
}
+ alias.aliasOf = doc;
alias.title = ComputedField.MakeFunction(`renameAlias(this, ${Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1})`);
return alias;
}
//
- // Determines whether the combination of the layoutDoc and dataDoc represents
- // a template relationship : there is a dataDoc and it doesn't match the layoutDoc an
- // the lyouatDoc's layout is layout string (not a document)
+ // Determines whether the layout needs to be expanded (as a template).
+ // template expansion is rquired when the layout is a template doc/field and there's a datadoc which isn't equal to the layout template
//
export function WillExpandTemplateLayout(layoutDoc: Doc, dataDoc?: Doc) {
- return layoutDoc.isTemplateForField && dataDoc && layoutDoc !== dataDoc && !(Doc.LayoutField(layoutDoc) instanceof Doc);
+ return (layoutDoc.isTemplateForField || layoutDoc.isTemplateDoc) && dataDoc && layoutDoc !== dataDoc;
}
//
// Returns an expanded template layout for a target data document if there is a template relationship
// between the two. If so, the layoutDoc is expanded into a new document that inherits the properties
// of the original layout while allowing for individual layout properties to be overridden in the expanded layout.
- //
- export function expandTemplateLayout(templateLayoutDoc: Doc, dataDoc?: Doc) {
- if (!WillExpandTemplateLayout(templateLayoutDoc, dataDoc) || !dataDoc) return templateLayoutDoc;
+ // templateArgs should be equivalent to the layout key that generates the template since that's where the template parameters are stored in ()'s at the end of the key.
+ // NOTE: the template will have references to "@params" -- the template arguments will be assigned to the '@params' field
+ // so that when the @params key is accessed, it will be rewritten as the key that is stored in the 'params' field and
+ // the derefence will then occur on the rootDocument (the original document).
+ // in the future, field references could be written as @<someparam> and then arguments would be passed in the layout key as:
+ // layout_mytemplate(somparam=somearg).
+ // then any references to @someparam would be rewritten as accesses to 'somearg' on the rootDocument
+ export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc, templateArgs?: string) {
+ const args = templateArgs?.match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1].replace("()", "") || StrCast(templateLayoutDoc.PARAMS);
+ if (!args && !WillExpandTemplateLayout(templateLayoutDoc, targetDoc) || !targetDoc) return templateLayoutDoc;
const templateField = StrCast(templateLayoutDoc.isTemplateForField); // the field that the template renders
// First it checks if an expanded layout already exists -- if so it will be stored on the dataDoc
@@ -454,44 +482,47 @@ 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 expandedTemplateLayout = dataDoc?.[expandedLayoutFieldKey];
- if (expandedTemplateLayout === undefined) {
- setTimeout(() => {
- if (!dataDoc[expandedLayoutFieldKey]) {
- const newLayoutDoc = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]");
- dataDoc[expandedLayoutFieldKey] = newLayoutDoc;
- newLayoutDoc.resolvedDataDoc = dataDoc;
- if (dataDoc[templateField] === undefined && templateLayoutDoc[templateField] instanceof List && Cast(templateLayoutDoc[templateField], listSpec(Doc), []).length) {
- dataDoc[templateField] = ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"] as List)`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc: templateLayoutDoc });
+ const params = args.split("=").length > 1 ? args.split("=")[0] : "PARAMS";
+ const layoutFielddKey = Doc.LayoutFieldKey(templateLayoutDoc);
+ const expandedLayoutFieldKey = (templateField || layoutFielddKey) + "-layout[" + templateLayoutDoc[Id] + (args ? `(${args})` : "") + "]";
+ let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
+ if (templateLayoutDoc.resolvedDataDoc instanceof Promise) {
+ expandedTemplateLayout = undefined;
+ }
+ else if (expandedTemplateLayout === undefined) {
+ if (templateLayoutDoc.resolvedDataDoc === Doc.GetProto(targetDoc) && templateLayoutDoc.PARAMS === StrCast(targetDoc.PARAMS)) {
+ 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
+ setTimeout(action(() => {
+ if (!targetDoc[expandedLayoutFieldKey]) {
+ const newLayoutDoc = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]");
+ // the template's arguments are stored in params which is derefenced to find
+ // the actual field key where the parameterized template data is stored.
+ newLayoutDoc[params] = args !== "..." ? args : ""; // ... signifies the layout has sub template(s) -- so we have to expand the layout for them so that they can get the correct 'rootDocument' field, but we don't need to reassign their params. it would be better if the 'rootDocument' field could be passed dynamically to avoid have to create instances
+ newLayoutDoc.rootDocument = targetDoc;
+ targetDoc[expandedLayoutFieldKey] = newLayoutDoc;
+ const dataDoc = Doc.GetProto(targetDoc);
+ newLayoutDoc.resolvedDataDoc = dataDoc;
+ if (dataDoc[templateField] === undefined && templateLayoutDoc[templateField] instanceof List) {
+ dataDoc[templateField] = ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"] as List)`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc });
+ }
}
- }
- }, 0);
+ }), 0);
+ }
}
- return expandedTemplateLayout instanceof Doc ? expandedTemplateLayout : undefined; // layout is undefined if the expandedTemplate is pending.
+ return expandedTemplateLayout instanceof Doc ? expandedTemplateLayout : undefined; // layout is undefined if the expandedTemplateLayout is pending.
}
// if the childDoc is a template for a field, then this will return the expanded layout with its data doc.
// otherwise, it just returns the childDoc
export function GetLayoutDataDocPair(containerDoc: Doc, containerDataDoc: Opt<Doc>, childDoc: Doc) {
- const resolvedDataDoc = containerDataDoc === containerDoc || !containerDataDoc ? undefined : Doc.GetDataDoc(containerDataDoc);
- return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc), data: resolvedDataDoc };
- }
- export function CreateDocumentExtensionForField(doc: Doc, fieldKey: string) {
- let proto: Doc | undefined = doc;
- while (proto && !Doc.IsPrototype(proto) && proto.proto) {
- proto = proto.proto;
- }
- let docExtensionForField = ((proto || doc)[fieldKey + "_ext"] as Doc);
- if (!docExtensionForField) {
- docExtensionForField = new Doc(doc[Id] + fieldKey, true);
- docExtensionForField.title = fieldKey + ".ext"; // courtesy field--- shouldn't be needed except maybe for debugging
- docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends.
- docExtensionForField.extendsField = fieldKey; // this can be used by search to map matches on the extension doc back to the field that was extended.
- docExtensionForField.type = DocumentType.EXTENSION;
- (proto || doc)[fieldKey + "_ext"] = new PrefetchProxy(docExtensionForField);
+ if (!childDoc || childDoc instanceof Promise || !Doc.GetProto(childDoc)) {
+ console.log("No, no, no!");
+ return { layout: childDoc, data: childDoc };
}
- return docExtensionForField;
+ const resolvedDataDoc = (Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField && !childDoc.PARAMS) ? undefined : containerDataDoc);
+ return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc, "(" + StrCast(containerDoc.PARAMS) + ")"), data: resolvedDataDoc };
}
export function Overwrite(doc: Doc, overwrite: Doc, copyProto: boolean = false): Doc {
@@ -534,7 +565,8 @@ export namespace Doc {
} else if (cfield instanceof ComputedField) {
copy[key] = ComputedField.MakeFunction(cfield.script.originalScript);
} else if (field instanceof ObjectField) {
- copy[key] = ObjectField.MakeCopy(field);
+ copy[key] = key.includes("layout[") && doc[key] instanceof Doc ? Doc.MakeCopy(doc[key] as Doc, false) :
+ doc[key] instanceof Doc ? doc[key] : ObjectField.MakeCopy(field);
} else if (field instanceof Promise) {
debugger; //This shouldn't happend...
} else {
@@ -561,32 +593,32 @@ export namespace Doc {
let _applyCount: number = 0;
export function ApplyTemplate(templateDoc: Doc) {
if (templateDoc) {
- const applied = ApplyTemplateTo(templateDoc, Doc.MakeDelegate(new Doc()), "layoutCustom", templateDoc.title + "(..." + _applyCount++ + ")");
- applied && (Doc.GetProto(applied).layout = applied.layout);
+ const target = Doc.MakeDelegate(new Doc());
+ const targetKey = StrCast(templateDoc.layoutKey, "layout");
+ const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + "(..." + _applyCount++ + ")");
+ target.layoutKey = targetKey;
+ applied && (Doc.GetProto(applied).type = templateDoc.type);
return applied;
}
return undefined;
}
- export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetKey: string, titleTarget: string | undefined = undefined) {
+ export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetKey: string, titleTarget: string | undefined) {
if (!templateDoc) {
target.layout = undefined;
target._nativeWidth = undefined;
target._nativeHeight = undefined;
- target.onClick = undefined;
target.type = undefined;
return;
}
- if ((target[targetKey] as Doc)?.proto !== templateDoc) {
- const layoutCustomLayout = Doc.MakeDelegate(templateDoc);
-
- titleTarget && (Doc.GetProto(target).title = titleTarget);
- Doc.GetProto(target).type = DocumentType.TEMPLATE;
- target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy]();
-
- Doc.GetProto(target)[targetKey] = new PrefetchProxy(layoutCustomLayout);
+ if (!Doc.AreProtosEqual(target[targetKey] as Doc, templateDoc)) {
+ if (target.resolvedDataDoc) {
+ target[targetKey] = new PrefetchProxy(templateDoc);
+ } else {
+ titleTarget && (Doc.GetProto(target).title = titleTarget);
+ Doc.GetProto(target)[targetKey] = new PrefetchProxy(templateDoc);
+ }
}
- target.layoutKey = targetKey;
return target;
}
@@ -597,37 +629,38 @@ export namespace Doc {
export function MakeMetadataFieldTemplate(templateField: Doc, templateDoc: Opt<Doc>): boolean {
// find the metadata field key that this template field doc will display (indicated by its title)
- const metadataFieldKey = StrCast(templateField.title).replace(/^-/, "");
+ const metadataFieldKey = StrCast(templateField.isTemplateForField) || StrCast(templateField.title).replace(/^-/, "");
// update the original template to mark it as a template
templateField.isTemplateForField = metadataFieldKey;
templateField.title = metadataFieldKey;
+ const templateFieldValue = templateField[metadataFieldKey] || templateField[Doc.LayoutFieldKey(templateField)];
+ const templateCaptionValue = templateField.caption;
// move any data that the template field had been rendering over to the template doc so that things will still be rendered
// when the template field is adjusted to point to the new metadatafield key.
// note 1: if the template field contained a list of documents, each of those documents will be converted to templates as well.
// note 2: this will not overwrite any field that already exists on the template doc at the field key
- if (!templateDoc?.[metadataFieldKey] && templateField.data instanceof ObjectField) {
- Cast(templateField.data, listSpec(Doc), [])?.map(d => d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc));
- (Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateField.data));
+ if (!templateDoc?.[metadataFieldKey] && templateFieldValue instanceof ObjectField) {
+ Cast(templateFieldValue, listSpec(Doc), [])?.map(d => d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc));
+ (Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateFieldValue));
}
- if (templateField.data instanceof RichTextField && templateField.data.Text) {
- templateField._textTemplate = ComputedField.MakeFunction(`copyField(this.${metadataFieldKey})`, { this: Doc.name });
+ if (templateCaptionValue instanceof RichTextField && (templateCaptionValue.Text || templateCaptionValue.Data.toString().includes("dashField"))) {
+ templateField["caption-textTemplate"] = ComputedField.MakeFunction(`copyField(this.caption)`, { this: Doc.name });
+ }
+ if (templateFieldValue instanceof RichTextField && (templateFieldValue.Text || templateFieldValue.Data.toString().includes("dashField"))) {
+ templateField[metadataFieldKey + "-textTemplate"] = ComputedField.MakeFunction(`copyField(this.${metadataFieldKey})`, { this: Doc.name });
}
// get the layout string that the template uses to specify its layout
const templateFieldLayoutString = StrCast(Doc.LayoutField(Doc.Layout(templateField)));
- // change itto render the target metadata field instead of what it was rendering before and assign it to the template field layout document.
+ // change it to render the target metadata field instead of what it was rendering before and assign it to the template field layout document.
Doc.Layout(templateField).layout = templateFieldLayoutString.replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldKey}'}`);
// assign the template field doc a delegate of any extension document that was previously used to render the template field (since extension doc's carry rendering informatino)
Doc.Layout(templateField)[metadataFieldKey + "_ext"] = Doc.MakeDelegate(templateField[templateFieldLayoutString?.split("'")[1] + "_ext"] as Doc);
- if (templateField.backgroundColor !== templateDoc?.defaultBackgroundColor) {
- templateField.defaultBackgroundColor = templateField.backgroundColor;
- }
-
return true;
}
@@ -665,11 +698,14 @@ export namespace Doc {
}
// the document containing the view layout information - will be the Document itself unless the Document has
- // a layout field. In that case, all layout information comes from there unless overriden by Document
- export function Layout(doc: Doc) { return Doc.LayoutField(doc) instanceof Doc ? Doc.LayoutField(doc) as Doc : 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;
+ }
export function SetLayout(doc: Doc, layout: Doc | string) { doc[StrCast(doc.layoutKey, "layout")] = layout; }
export function LayoutField(doc: Doc) { return doc[StrCast(doc.layoutKey, "layout")]; }
- export function LayoutFieldKey(doc: Doc) { return StrCast(Doc.Layout(doc).layout).split("'")[1]; }
+ export function LayoutFieldKey(doc: Doc): string { return StrCast(Doc.Layout(doc).layout).split("'")[1]; }
const manager = new DocData();
export function SearchQuery(): string { return manager._searchQuery; }
export function SetSearchQuery(query: string) { runInAction(() => manager._searchQuery = query); }
@@ -677,26 +713,26 @@ export namespace Doc {
export function SetUserDoc(doc: Doc) { manager._user_doc = doc; }
export function IsBrushed(doc: Doc) {
return computedFn(function IsBrushed(doc: Doc) {
- return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetDataDoc(doc));
+ return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc));
})(doc);
}
// don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
export function IsBrushedDegreeUnmemoized(doc: Doc) {
- return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetDataDoc(doc)) ? 1 : 0;
+ return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? 1 : 0;
}
export function IsBrushedDegree(doc: Doc) {
return computedFn(function IsBrushDegree(doc: Doc) {
- return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetDataDoc(doc)) ? 1 : 0;
+ return Doc.IsBrushedDegreeUnmemoized(doc);
})(doc);
}
export function BrushDoc(doc: Doc) {
brushManager.BrushedDoc.set(doc, true);
- brushManager.BrushedDoc.set(Doc.GetDataDoc(doc), true);
+ brushManager.BrushedDoc.set(Doc.GetProto(doc), true);
return doc;
}
export function UnBrushDoc(doc: Doc) {
brushManager.BrushedDoc.delete(doc);
- brushManager.BrushedDoc.delete(Doc.GetDataDoc(doc));
+ brushManager.BrushedDoc.delete(Doc.GetProto(doc));
return doc;
}
@@ -709,14 +745,14 @@ export namespace Doc {
document.removeEventListener("pointerdown", linkFollowUnhighlight);
}
- let dt = 0;
+ let _lastDate = 0;
export function linkFollowHighlight(destDoc: Doc, dataAndDisplayDocs = true) {
linkFollowUnhighlight();
Doc.HighlightDoc(destDoc, dataAndDisplayDocs);
document.removeEventListener("pointerdown", linkFollowUnhighlight);
document.addEventListener("pointerdown", linkFollowUnhighlight);
- const x = dt = Date.now();
- window.setTimeout(() => dt === x && linkFollowUnhighlight(), 5000);
+ const lastDate = _lastDate = Date.now();
+ window.setTimeout(() => _lastDate === lastDate && linkFollowUnhighlight(), 5000);
}
export class HighlightBrush {
@@ -724,18 +760,18 @@ export namespace Doc {
}
const highlightManager = new HighlightBrush();
export function IsHighlighted(doc: Doc) {
- return highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetDataDoc(doc));
+ return highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetProto(doc));
}
export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true) {
runInAction(() => {
highlightManager.HighlightedDoc.set(doc, true);
- dataAndDisplayDocs && highlightManager.HighlightedDoc.set(Doc.GetDataDoc(doc), true);
+ dataAndDisplayDocs && highlightManager.HighlightedDoc.set(Doc.GetProto(doc), true);
});
}
export function UnHighlightDoc(doc: Doc) {
runInAction(() => {
highlightManager.HighlightedDoc.set(doc, false);
- highlightManager.HighlightedDoc.set(Doc.GetDataDoc(doc), false);
+ highlightManager.HighlightedDoc.set(Doc.GetProto(doc), false);
});
}
export function UnhighlightAll() {
@@ -763,7 +799,7 @@ export namespace Doc {
}
export function matchFieldValue(doc: Doc, key: string, value: any): boolean {
- const fieldVal = doc[key] ? doc[key] : doc[key + "_ext"];
+ const fieldVal = doc[key];
if (Cast(fieldVal, listSpec("string"), []).length) {
const vals = Cast(fieldVal, listSpec("string"), []);
return vals.some(v => v === value);
@@ -771,6 +807,99 @@ export namespace Doc {
const fieldStr = Field.toString(fieldVal as Field);
return fieldStr === value;
}
+
+ export function deiconifyView(doc: any) {
+ StrCast(doc.layoutKey).split("_")[1] === "icon" && setNativeView(doc);
+ }
+
+ export function setNativeView(doc: any) {
+ const prevLayout = StrCast(doc.layoutKey).split("_")[1];
+ const deiconify = prevLayout === "icon" && StrCast(doc.deiconifyLayout) ? "layout_" + StrCast(doc.deiconifyLayout) : "";
+ prevLayout === "icon" && (doc.deiconifyLayout = undefined);
+ doc.layoutKey = deiconify || "layout";
+ }
+ export function setDocFilterRange(target: Doc, key: string, range?: number[]) {
+ const docRangeFilters = Cast(target._docRangeFilters, listSpec("string"), []);
+ for (let i = 0; i < docRangeFilters.length; i += 3) {
+ if (docRangeFilters[i] === key) {
+ docRangeFilters.splice(i, 3);
+ break;
+ }
+ }
+ if (range !== undefined) {
+ docRangeFilters.push(key);
+ docRangeFilters.push(range[0].toString());
+ docRangeFilters.push(range[1].toString());
+ target._docRangeFilters = new List<string>(docRangeFilters);
+ }
+ }
+
+ export function aliasDocs(field: any) {
+ return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d)));
+ }
+
+ // filters document in a container collection:
+ // all documents with the specified value for the specified key are included/excluded
+ // based on the modifiers :"check", "x", undefined
+ export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) {
+ const docFilters = Cast(container._docFilters, listSpec("string"), []);
+ for (let i = 0; i < docFilters.length; i += 3) {
+ if (docFilters[i] === key && docFilters[i + 1] === value) {
+ docFilters.splice(i, 3);
+ break;
+ }
+ }
+ if (typeof modifiers === "string") {
+ docFilters.push(key);
+ docFilters.push(value);
+ docFilters.push(modifiers);
+ container._docFilters = new List<string>(docFilters);
+ }
+ }
+ 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])];
+ }
+ }
+ }
+
+ export function freezeNativeDimensions(layoutDoc: Doc, width: number, height: number): void {
+ layoutDoc._autoHeight = false;
+ if (!layoutDoc._nativeWidth) {
+ layoutDoc._nativeWidth = NumCast(layoutDoc._width, width);
+ layoutDoc._nativeHeight = NumCast(layoutDoc._height, height);
+ }
+ }
+ export function assignDocToField(doc: Doc, field: string, id: string) {
+ DocServer.GetRefField(id).then(layout => layout instanceof Doc && (doc[field] = layout));
+ return id;
+ }
+
+ export async function addFieldEnumerations(doc: Opt<Doc>, enumeratedFieldKey: string, enumerations: { title: string, _backgroundColor?: string, color?: string }[]) {
+ let optionsCollection = await DocServer.GetRefField(enumeratedFieldKey);
+ if (!(optionsCollection instanceof Doc)) {
+ optionsCollection = Docs.Create.StackingDocument([], { title: `${enumeratedFieldKey} field set` }, enumeratedFieldKey);
+ Doc.AddDocToList((Doc.UserDoc().fieldTypes as Doc), "data", optionsCollection as Doc);
+ }
+ const options = optionsCollection as Doc;
+ const targetDoc = doc && Doc.GetProto(Cast(doc.rootDocument, Doc, null) || doc);
+ const docFind = `options.data.find(doc => doc.title === (this.rootDocument||this)["${enumeratedFieldKey}"])?`;
+ targetDoc && (targetDoc.backgroundColor = ComputedField.MakeFunction(docFind + `._backgroundColor || "white"`, undefined, { options }));
+ targetDoc && (targetDoc.color = ComputedField.MakeFunction(docFind + `.color || "black"`, undefined, { options }));
+ targetDoc && (targetDoc.borderRounding = ComputedField.MakeFunction(docFind + `.borderRounding`, undefined, { options }));
+ enumerations.map(enumeration => {
+ const found = DocListCast(options.data).find(d => d.title === enumeration.title);
+ if (found) {
+ found._backgroundColor = enumeration._backgroundColor || found._backgroundColor;
+ found._color = enumeration.color || found._color;
+ } else {
+ Doc.AddDocToList(options, "data", Docs.Create.TextDocument(enumeration.title, enumeration));
+ }
+ });
+ return optionsCollection;
+ }
}
Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; });
@@ -780,28 +909,24 @@ Scripting.addGlobal(function setChildDetailedLayout(target: any, source: any) {
Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); });
Scripting.addGlobal(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); });
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))); });
+Scripting.addGlobal(function aliasDocs(field: any) { return Doc.aliasDocs(field); });
Scripting.addGlobal(function docList(field: any) { return DocListCast(field); });
Scripting.addGlobal(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); });
+Scripting.addGlobal(function deiconifyView(doc: any) { Doc.deiconifyView(doc); });
Scripting.addGlobal(function undo() { return UndoManager.Undo(); });
Scripting.addGlobal(function redo() { return UndoManager.Redo(); });
+Scripting.addGlobal(function DOC(id: string) { console.log("Can't parse a document id in a script"); return "invalid"; });
+Scripting.addGlobal(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); });
+Scripting.addGlobal(function curPresentationItem() {
+ const curPres = Doc.UserDoc().curPresentation as Doc;
+ return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
+});
Scripting.addGlobal(function selectDoc(doc: any) { Doc.UserDoc().SelectedDocs = new List([doc]); });
Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
- const docs = DocListCast(Doc.UserDoc().SelectedDocs).filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCUMENT && d.type !== DocumentType.KVP && (!excludeCollections || !Cast(d.data, listSpec(Doc), null)));
+ const docs = DocListCast(Doc.UserDoc().SelectedDocs).
+ filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCUMENT && d.type !== DocumentType.KVP &&
+ (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)));
return docs.length ? new List(docs) : prevValue;
});
-Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: string) {
- const docFilters = Cast(container._docFilter, listSpec("string"), []);
- for (let i = 0; i < docFilters.length; i += 3) {
- if (docFilters[i] === key && docFilters[i + 1] === value) {
- docFilters.splice(i, 3);
- break;
- }
- }
- if (modifiers !== undefined) {
- docFilters.push(key);
- docFilters.push(value);
- docFilters.push(modifiers);
- container._docFilter = new List<string>(docFilters);
- }
-}); \ No newline at end of file
+Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); });
+Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); }); \ No newline at end of file
diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts
index 4a44b4f55..bb93de5ac 100644
--- a/src/new_fields/InkField.ts
+++ b/src/new_fields/InkField.ts
@@ -8,7 +8,6 @@ export enum InkTool {
Pen,
Highlighter,
Eraser,
- Scrubber,
Stamp
}
diff --git a/src/new_fields/ObjectField.ts b/src/new_fields/ObjectField.ts
index 566104b40..9aa1c9b04 100644
--- a/src/new_fields/ObjectField.ts
+++ b/src/new_fields/ObjectField.ts
@@ -1,4 +1,3 @@
-import { Doc } from "./Doc";
import { RefField } from "./RefField";
import { OnUpdate, Parent, Copy, ToScriptString, ToString } from "./FieldSymbols";
import { Scripting } from "../client/util/Scripting";
diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts
index 7b6ce9b98..ad4a5a252 100644
--- a/src/new_fields/RichTextField.ts
+++ b/src/new_fields/RichTextField.ts
@@ -33,20 +33,8 @@ export class RichTextField extends ObjectField {
return this.Text;
}
- [ToPlainText]() {
- // Because we're working with plain text, just concatenate all paragraphs
- let content = JSON.parse(this.Data).doc.content;
- let paragraphs = content.filter((item: any) => item.type === "paragraph");
-
- // Functions to flatten ProseMirror paragraph objects (and their components) to plain text
- // While this function already exists in state.doc.textBeteen(), it doesn't account for newlines
- let blockText = (block: any) => block.text;
- let concatenateParagraph = (p: any) => (p.content ? p.content.map(blockText).join(joiner) : "") + delimiter;
-
- // Concatentate paragraphs and string the result together
- let textParagraphs: string[] = paragraphs.map(concatenateParagraph);
- let plainText = textParagraphs.join(joiner);
- return plainText.substring(0, plainText.length - 1);
+ public static DashField(fieldKey: string) {
+ return new RichTextField(`{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"${fieldKey}","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}`, "");
}
} \ No newline at end of file
diff --git a/src/new_fields/RichTextUtils.ts b/src/new_fields/RichTextUtils.ts
index c50f8cc48..c211b3d3c 100644
--- a/src/new_fields/RichTextUtils.ts
+++ b/src/new_fields/RichTextUtils.ts
@@ -1,5 +1,5 @@
import { EditorState, Transaction, TextSelection } from "prosemirror-state";
-import { Node, Fragment, Mark, MarkType } from "prosemirror-model";
+import { Node, Fragment, Mark } from "prosemirror-model";
import { RichTextField } from "./RichTextField";
import { docs_v1 } from "googleapis";
import { GoogleApiClientUtils } from "../client/apis/google_docs/GoogleApiClientUtils";
@@ -17,6 +17,7 @@ import { Id } from "./FieldSymbols";
import { DocumentView } from "../client/views/nodes/DocumentView";
import { AssertionError } from "assert";
import { Networking } from "../client/Network";
+import { extname } from "path";
export namespace RichTextUtils {
@@ -113,6 +114,7 @@ export namespace RichTextUtils {
width: number;
title: string;
url: string;
+ agnostic: string;
}
const parseInlineObjects = async (document: docs_v1.Schema$Document): Promise<Map<string, ImageTemplate>> => {
@@ -123,12 +125,10 @@ export namespace RichTextUtils {
const objects = Object.keys(inlineObjects).map(objectId => inlineObjects[objectId]);
const mediaItems: MediaItem[] = objects.map(object => {
const embeddedObject = object.inlineObjectProperties!.embeddedObject!;
- const baseUrl = embeddedObject.imageProperties!.contentUri!;
- const filename = `upload_${Utils.GenerateGuid()}.png`;
- return { baseUrl, filename };
+ return { baseUrl: embeddedObject.imageProperties!.contentUri! };
});
- const uploads = await Networking.PostToServer("/googlePhotosMediaDownload", { mediaItems });
+ const uploads = await Networking.PostToServer("/googlePhotosMediaGet", { mediaItems });
if (uploads.length !== mediaItems.length) {
throw new AssertionError({ expected: mediaItems.length, actual: uploads.length, message: "Error with internally uploading inlineObjects!" });
@@ -136,16 +136,17 @@ export namespace RichTextUtils {
for (let i = 0; i < objects.length; i++) {
const object = objects[i];
- const { fileNames } = uploads[i];
+ const { accessPaths } = uploads[i];
+ const { agnostic, _m } = accessPaths;
const embeddedObject = object.inlineObjectProperties!.embeddedObject!;
const size = embeddedObject.size!;
const width = size.width!.magnitude!;
- const url = Utils.fileUrl(fileNames.clean);
inlineObjectMap.set(object.objectId!, {
title: embeddedObject.title || `Imported Image from ${document.title}`,
width,
- url
+ url: Utils.prepend(_m.client),
+ agnostic: Utils.prepend(agnostic.client)
});
}
}
@@ -156,7 +157,6 @@ export namespace RichTextUtils {
interface MediaItem {
baseUrl: string;
- filename: string;
}
export const Import = async (documentId: GoogleApiClientUtils.Docs.DocumentId, textNote: Doc): Promise<Opt<GoogleApiClientUtils.Docs.ImportResult>> => {
@@ -268,19 +268,19 @@ export namespace RichTextUtils {
};
const imageNode = (schema: any, image: ImageTemplate, textNote: Doc) => {
- const { url: src, width } = image;
+ const { url: src, width, agnostic } = image;
let docid: string;
- const guid = Utils.GenerateDeterministicGuid(src);
+ const guid = Utils.GenerateDeterministicGuid(agnostic);
const backingDocId = StrCast(textNote[guid]);
if (!backingDocId) {
- const backingDoc = Docs.Create.ImageDocument(src, { _width: 300, _height: 300 });
- DocumentView.makeCustomViewClicked(backingDoc, undefined, Docs.Create.FreeformDocument);
+ const backingDoc = Docs.Create.ImageDocument(agnostic, { _width: 300, _height: 300 });
+ DocumentView.makeCustomViewClicked(backingDoc, Docs.Create.FreeformDocument);
docid = backingDoc[Id];
textNote[guid] = docid;
} else {
docid = backingDocId;
}
- return schema.node("image", { src, width, docid, float: null, location: "onRight" });
+ return schema.node("image", { src, agnostic, width, docid, float: null, location: "onRight" });
};
const textNode = (schema: any, run: docs_v1.Schema$TextRun) => {
@@ -403,7 +403,7 @@ export namespace RichTextUtils {
let exported = (await Cast(linkDoc.anchor2, Doc))!;
if (!exported.customLayout) {
exported = Doc.MakeAlias(exported);
- DocumentView.makeCustomViewClicked(exported, undefined, Docs.Create.FreeformDocument);
+ DocumentView.makeCustomViewClicked(exported, Docs.Create.FreeformDocument);
linkDoc.anchor2 = exported;
}
url = Utils.shareUrl(exported[Id]);
@@ -436,7 +436,7 @@ export namespace RichTextUtils {
const width = attrs.width;
requests.push(await EncodeImage({
startIndex: position + nodeSize - 1,
- uri: attrs.src,
+ uri: attrs.agnostic,
width: Number(typeof width === "string" ? width.replace("px", "") : width)
}));
}
@@ -499,15 +499,18 @@ export namespace RichTextUtils {
};
};
- const EncodeImage = async (information: ImageInformation) => {
- const source = [Docs.Create.ImageDocument(information.uri)];
+ const EncodeImage = async ({ uri, width, startIndex }: ImageInformation) => {
+ if (!uri) {
+ return {};
+ }
+ const source = [Docs.Create.ImageDocument(uri)];
const baseUrls = await GooglePhotos.Transactions.UploadThenFetch(source);
if (baseUrls) {
return {
insertInlineImage: {
uri: baseUrls[0],
- objectSize: { width: { magnitude: information.width, unit: "PT" } },
- location: { index: information.startIndex }
+ objectSize: { width: { magnitude: width, unit: "PT" } },
+ location: { index: startIndex }
}
};
}
diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts
index 3f0ff4284..72bce283d 100644
--- a/src/new_fields/Schema.ts
+++ b/src/new_fields/Schema.ts
@@ -33,7 +33,7 @@ export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFu
get(target: any, prop, receiver) {
const field = receiver.doc[prop];
if (prop in schema) {
- const desc = (schema as any)[prop];
+ const desc = prop === "proto" ? Doc : (schema as any)[prop]; // bcz: proto doesn't appear in schemas ... maybe it should?
if (typeof desc === "object" && "defaultVal" in desc && "type" in desc) {//defaultSpec
return Cast(field, desc.type, desc.defaultVal);
} else if (typeof desc === "function" && !ObjectField.isPrototypeOf(desc) && !RefField.isPrototypeOf(desc)) {
diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts
index f8a8d1226..148886848 100644
--- a/src/new_fields/ScriptField.ts
+++ b/src/new_fields/ScriptField.ts
@@ -7,6 +7,7 @@ import { Doc, Field } from "../new_fields/Doc";
import { Plugins } from "./util";
import { computedFn } from "mobx-utils";
import { ProxyField } from "./Proxy";
+import { Cast } from "./Types";
function optional(propSchema: PropSchema) {
return custom(value => {
@@ -106,7 +107,7 @@ export class ScriptField extends ObjectField {
}
public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Field }) {
const compiled = CompileScript(script, {
- params: { this: Doc.name, _last_: "any", ...params },
+ params: { this: Doc.name, self: Doc.name, _last_: "any", ...params },
typecheck: false,
editable: true,
addReturn: addReturn,
@@ -114,13 +115,13 @@ export class ScriptField extends ObjectField {
});
return compiled;
}
- public static MakeFunction(script: string, params: object = {}) {
- const compiled = ScriptField.CompileScript(script, params, true);
+ public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }) {
+ const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
return compiled.compiled ? new ScriptField(compiled) : undefined;
}
- public static MakeScript(script: string, params: object = {}) {
- const compiled = ScriptField.CompileScript(script, params, false);
+ public static MakeScript(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }) {
+ const compiled = ScriptField.CompileScript(script, params, false, capturedVariables);
return compiled.compiled ? new ScriptField(compiled) : undefined;
}
}
@@ -130,7 +131,7 @@ export class ScriptField extends ObjectField {
export class ComputedField extends ScriptField {
_lastComputedResult: any;
//TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc
- value = computedFn((doc: Doc) => this._lastComputedResult = this.script.run({ this: doc, _last_: this._lastComputedResult }, console.log).result);
+ value = computedFn((doc: Doc) => this._lastComputedResult = this.script.run({ this: doc, self: Cast(doc.rootDocument, Doc, null) || doc, _last_: this._lastComputedResult }, console.log).result);
public static MakeScript(script: string, params: object = {}) {
const compiled = ScriptField.CompileScript(script, params, false);
return compiled.compiled ? new ComputedField(compiled) : undefined;
diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts
index cfab36906..fb71160ca 100644
--- a/src/new_fields/URLField.ts
+++ b/src/new_fields/URLField.ts
@@ -49,3 +49,5 @@ export const nullAudio = "https://actions.google.com/sounds/v1/alarms/beep_short
@scriptingGlobal @Deserializable("pdf") export class PdfField extends URLField { }
@scriptingGlobal @Deserializable("web") export class WebField extends URLField { }
@scriptingGlobal @Deserializable("youtube") export class YoutubeField extends URLField { }
+@scriptingGlobal @Deserializable("webcam") export class WebCamField extends URLField { }
+
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index 4a5c1fdb0..91ea32bee 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -4,39 +4,49 @@ import { Doc } from "./Doc";
import { DateField } from "./DateField";
export const documentSchema = createSchema({
- layout: "string", // this is the native layout string for the document. templates can be added using other fields and setting layoutKey below (see layout_custom as an example)
+ type: "string", // enumerated type of document -- should be template-specific (ie, start with an '_')
+ layout: "string", // this is the native layout string for the document. templates can be added using other fields and setting layoutKey below
layoutKey: "string", // holds the field key for the field that actually holds the current lyoat
- layout_custom: Doc, // used to hold a custom layout (there's nothing special about this field .. any field could hold a custom layout that can be selected by setting 'layoutKey')
title: "string", // document title (can be on either data document or layout)
- _dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias" or "copy")
- _nativeWidth: "number", // native width of document which determines how much document contents are scaled when the document's width is set
- _nativeHeight: "number", // "
- _width: "number", // width of document in its container's coordinate system
- _height: "number", // "
+ dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias" or "copy")
+ childDropAction: "string", // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy")
+ _autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents
+ _nativeWidth: "number", // native width of document which determines how much document contents are scaled when the document's width is set
+ _nativeHeight: "number", // "
+ _width: "number", // width of document in its container's coordinate system
+ _height: "number", // "
+ _xPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
+ _yPadding: "number", // pixels of padding on top/bottom of collectionfreeformview contents when fitToBox is set
+ _xMargin: "number", // margin added on left/right of most documents to add separation from their container
+ _yMargin: "number", // margin added on top/bottom of most documents to add separation from their container
+ _showCaption: "string", // whether editable caption text is overlayed at the bottom of the document
+ _showTitle: "string", // the fieldkey whose contents should be displayed at the top of the document
+ _showTitleHover: "string", // the showTitle should be shown only on hover
+ _showAudio: "boolean", // whether to show the audio record icon on documents
_freeformLayoutEngine: "string",// the string ID for the layout engine to use to layout freeform view documents
- _LODdisable: "boolean", // whether to disbale LOD switching for CollectionFreeFormViews
+ _LODdisable: "boolean", // whether to disbale LOD switching for CollectionFreeFormViews
+ _pivotField: "string", // specifies which field should be used as the timeline/pivot axis
+ _replacedChrome: "string", // what the default chrome is replaced with. Currently only supports the value of 'replaced' for PresBox's.
+ _chromeStatus: "string", // determines the state of the collection chrome. values allowed are 'replaced', 'enabled', 'disabled', 'collapsed'
+ _freezeOnDrop: "boolean", // whether a document without native dimensions should have its width/height frozen as native dimensions on drop. Used by Timeline view to make sure documents are scaled to fit the display thumbnail
color: "string", // foreground color of document
backgroundColor: "string", // background color of document
opacity: "number", // opacity of document
- creationDate: DateField, // when the document was created
+ creationDate: DateField, // when the document was created
links: listSpec(Doc), // computed (readonly) list of links associated with this document
- removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped
onClick: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
- onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
- onPointerUp: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
+ onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
+ onPointerUp: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped.
dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document.
- ignoreAspect: "boolean", // whether aspect ratio should be ignored when laying out or manipulating the document
- autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents
+ removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped
isTemplateForField: "string",// when specifies a field key, then the containing document is a template that renders the specified field
isBackground: "boolean", // whether document is a background element and ignores input events (can only selet with marquee)
- type: "string", // enumerated type of document
+ dontSelect: "boolean", // whether document should be selected when clicked (usually set to false for buttons)
treeViewOpen: "boolean", // flag denoting whether the documents sub-tree (contents) is visible or hidden
treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree
- preventTreeViewOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
- currentTimecode: "number", // current play back time of a temporal document (video / audio)
- summarizedDocs: listSpec(Doc), // documents that are summarized by this document (and which will typically be opened by clicking this document)
- maximizedDocs: listSpec(Doc), // documents to maximize when clicking this document (generally this document will be an icon)
+ treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
+ currentTimecode: "number", // current play back time of a temporal document (video / audio)
maximizeLocation: "string", // flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab)
lockedPosition: "boolean", // whether the document can be moved (dragged)
lockedTransform: "boolean", // whether the document can be panned/zoomed
@@ -44,22 +54,15 @@ export const documentSchema = createSchema({
borderRounding: "string", // border radius rounding of document
searchFields: "string", // the search fields to display when this document matches a search in its metadata
heading: "number", // the logical layout 'heading' of this document (used by rule provider to stylize h1 header elements, from h2, etc)
- showCaption: "string", // whether editable caption text is overlayed at the bottom of the document
- showTitle: "string", // the fieldkey whose contents should be displayed at the top of the document
- showTitleHover: "string", // the showTitle should be shown only on hover
isButton: "boolean", // whether document functions as a button (overiding native interactions of its content)
ignoreClick: "boolean", // whether documents ignores input clicks (but does not ignore manipulation and other events)
- isAnimating: "string", // whether the document is in the midst of animating between two layouts (used by icons to de/iconify documents). value is undefined|"min"|"max"
- animateToDimensions: listSpec("number"), // layout information about the target rectangle a document is animating towards
scrollToLinkID: "string", // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc.
strokeWidth: "number",
fontSize: "string",
fitToBox: "boolean", // whether freeform view contents should be zoomed/panned to fill the area of the document view
- xPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
- yPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
- LODarea: "number", // area (width*height) where CollectionFreeFormViews switch from a label to rendering contents
letterSpacing: "string",
- textTransform: "string"
+ textTransform: "string",
+ childTemplateName: "string" // the name of a template to use to override the layoutKey when rendering a document in DocumentBox
});
export const positionSchema = createSchema({
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 2cedda7a6..8c719ccd8 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -1,5 +1,5 @@
import { UndoManager } from "../client/util/UndoManager";
-import { Doc, Field, FieldResult, UpdatingFromServer } from "./Doc";
+import { Doc, Field, FieldResult, UpdatingFromServer, LayoutSym } from "./Doc";
import { SerializationHelper } from "../client/util/SerializationHelper";
import { ProxyField, PrefetchProxy } from "./Proxy";
import { RefField } from "./RefField";
@@ -7,14 +7,14 @@ import { ObjectField } from "./ObjectField";
import { action, trace } from "mobx";
import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols";
import { DocServer } from "../client/DocServer";
-import { props } from "bluebird";
function _readOnlySetter(): never {
throw new Error("Documents can't be modified in read-only mode");
}
+const tracing = false;
export function TraceMobx() {
- //trace();
+ tracing && trace();
}
export interface GetterResult {
@@ -100,19 +100,17 @@ export function makeEditable() {
_setter = _setterImpl;
}
-let layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox",
- "LODdisable", "dropAction", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"];
+const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox",
+ "LODdisable", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"];
export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
let prop = in_prop;
- if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" &&
- ((prop as string).startsWith("_") || layoutProps.includes(prop))) {
+ if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) {
if (!prop.startsWith("_")) {
console.log(prop + " is deprecated - switch to _" + prop);
prop = "_" + prop;
}
- const resolvedLayout = getFieldImpl(target, getFieldImpl(target, "layoutKey", receiver), receiver);
- if (resolvedLayout instanceof Doc) {
- resolvedLayout[prop] = value;
+ if (target.__LAYOUT__) {
+ target.__LAYOUT__[prop] = value;
return true;
}
}
@@ -121,16 +119,15 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
export function getter(target: any, in_prop: string | symbol | number, receiver: any): any {
let prop = in_prop;
- if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" &&
- ((prop as string).startsWith("_") || layoutProps.includes(prop))) {
+ if (prop === LayoutSym) {
+ return target.__LAYOUT__;
+ }
+ if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) {
if (!prop.startsWith("_")) {
console.log(prop + " is deprecated - switch to _" + prop);
prop = "_" + prop;
}
- const resolvedLayout = getFieldImpl(target, getFieldImpl(target, "layoutKey", receiver), receiver);
- if (resolvedLayout instanceof Doc) {
- return resolvedLayout[prop];
- }
+ if (target.__LAYOUT__) return target.__LAYOUT__[prop];
}
if (prop === "then") {//If we're being awaited
return undefined;