aboutsummaryrefslogtreecommitdiff
path: root/src/new_fields
diff options
context:
space:
mode:
authorSam Wilkins <abdullah_ahmed@brown.edu>2019-07-23 19:12:15 -0400
committerSam Wilkins <abdullah_ahmed@brown.edu>2019-07-23 19:12:15 -0400
commit0ef6d9b5a3f90552562f4a6392967887d8805cc3 (patch)
treea40f95ecf9ae19d836a7bd324ed650d78fc7c3c0 /src/new_fields
parent7d9958f23b2b69f59bb108b33ec014b52ad41c99 (diff)
parent86971952237b8bd01a23b52db662740126bd8477 (diff)
merge
Diffstat (limited to 'src/new_fields')
-rw-r--r--src/new_fields/Doc.ts60
-rw-r--r--src/new_fields/InkField.ts4
-rw-r--r--src/new_fields/ScriptField.ts32
-rw-r--r--src/new_fields/util.ts28
4 files changed, 101 insertions, 23 deletions
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 1dd721396..5ae0753d8 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -10,14 +10,16 @@ import { RefField, FieldId } from "./RefField";
import { ToScriptString, SelfProxy, Parent, OnUpdate, Self, HandleUpdate, Update, Id } from "./FieldSymbols";
import { scriptingGlobal } from "../client/util/Scripting";
import { List } from "./List";
+import { DocumentType } from "../client/documents/Documents";
+import { ComputedField } from "./ScriptField";
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
const onDelegate = Object.keys(doc).includes(key);
- let field = FieldValue(doc[key]);
+ let field = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
if (Field.IsField(field)) {
- return (onDelegate ? "=" : "") + Field.toScriptString(field);
+ return (onDelegate ? "=" : "") + (field instanceof ComputedField ? `:=${field.script.originalScript}` : Field.toScriptString(field));
}
return "";
}
@@ -242,7 +244,7 @@ export namespace Doc {
let r = (doc === other);
let r2 = (doc.proto === other);
let r3 = (other.proto === doc);
- let r4 = (doc.proto === other.proto);
+ let r4 = (doc.proto === other.proto && other.proto !== undefined);
return r || r2 || r3 || r4;
}
@@ -296,7 +298,7 @@ export namespace Doc {
x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
};
- }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE });
+ }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE });
return bounds;
}
@@ -316,7 +318,7 @@ export namespace Doc {
if (extensionDoc === undefined) {
setTimeout(() => {
let docExtensionForField = new Doc(doc[Id] + fieldKey, true);
- docExtensionForField.title = "Extension of " + doc.title + "'s field:" + fieldKey;
+ docExtensionForField.title = doc.title + ":" + fieldKey + ".ext";
docExtensionForField.extendsDoc = doc;
let proto: Doc | undefined = doc;
while (proto && !Doc.IsPrototype(proto)) {
@@ -344,18 +346,23 @@ export namespace Doc {
// ... which means we change the layout to be an expanded view of the template layout.
// This allows the view override the template's properties and be referenceable as its own document.
- let expandedTemplateLayout = templateLayoutDoc["_expanded_" + dataDoc[Id]];
+ let expandedTemplateLayout = dataDoc[templateLayoutDoc[Id]];
+ if (expandedTemplateLayout instanceof Doc) {
+ return expandedTemplateLayout;
+ }
+ expandedTemplateLayout = dataDoc[templateLayoutDoc.title + templateLayoutDoc[Id]];
if (expandedTemplateLayout instanceof Doc) {
return expandedTemplateLayout;
}
if (expandedTemplateLayout === undefined && BoolCast(templateLayoutDoc.isTemplate)) {
setTimeout(() => {
- templateLayoutDoc["_expanded_" + dataDoc[Id]] = Doc.MakeDelegate(templateLayoutDoc);
- (templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).title = templateLayoutDoc.title + " applied to " + dataDoc.title;
- (templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).isExpandedTemplate = templateLayoutDoc;
+ let expandedDoc = Doc.MakeDelegate(templateLayoutDoc);
+ expandedDoc.title = templateLayoutDoc.title + "[" + StrCast(dataDoc.title).match(/\.\.\.[0-9]*/) + "]";
+ expandedDoc.isExpandedTemplate = templateLayoutDoc;
+ dataDoc[templateLayoutDoc.title + templateLayoutDoc[Id]] = expandedDoc;
}, 0);
}
- return templateLayoutDoc;
+ return templateLayoutDoc; // use the templateLayout when it's not a template or the expandedTemplate is pending.
}
export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc {
@@ -385,12 +392,26 @@ export namespace Doc {
export function MakeDelegate(doc: Doc, id?: string): Doc;
export function MakeDelegate(doc: Opt<Doc>, id?: string): Opt<Doc>;
export function MakeDelegate(doc: Opt<Doc>, id?: string): Opt<Doc> {
- if (!doc) {
- return undefined;
+ if (doc) {
+ const delegate = new Doc(id, true);
+ delegate.proto = doc;
+ return delegate;
}
- const delegate = new Doc(id, true);
- delegate.proto = doc;
- return delegate;
+ return undefined;
+ }
+
+ let _applyCount: number = 0;
+ export function ApplyTemplate(templateDoc: Doc) {
+ if (!templateDoc) return undefined;
+ let otherdoc = new Doc();
+ 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;
+ return otherdoc;
}
export function MakeTemplate(fieldTemplate: Doc, metaKey: string, proto: Doc) {
@@ -418,6 +439,13 @@ export namespace Doc {
fieldTemplate.nativeHeight = nh;
fieldTemplate.isTemplate = true;
fieldTemplate.showTitle = "title";
- fieldTemplate.proto = proto;
+ setTimeout(() => fieldTemplate.proto = proto);
+ }
+
+ export async function ToggleDetailLayout(d: Doc) {
+ 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;
}
} \ No newline at end of file
diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts
index 4e3b7abe0..39c6c8ce3 100644
--- a/src/new_fields/InkField.ts
+++ b/src/new_fields/InkField.ts
@@ -2,7 +2,7 @@ import { Deserializable } from "../client/util/SerializationHelper";
import { serializable, custom, createSimpleSchema, list, object, map } from "serializr";
import { ObjectField } from "./ObjectField";
import { Copy, ToScriptString } from "./FieldSymbols";
-import { deepCopy } from "../Utils";
+import { DeepCopy } from "../Utils";
export enum InkTool {
None,
@@ -39,7 +39,7 @@ export class InkField extends ObjectField {
}
[Copy]() {
- return new InkField(deepCopy(this.inkData));
+ return new InkField(DeepCopy(this.inkData));
}
[ToScriptString]() {
diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts
index e2994ed70..e5ec34f57 100644
--- a/src/new_fields/ScriptField.ts
+++ b/src/new_fields/ScriptField.ts
@@ -4,6 +4,8 @@ import { Copy, ToScriptString, Parent, SelfProxy } from "./FieldSymbols";
import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr";
import { Deserializable } from "../client/util/SerializationHelper";
import { Doc } from "../new_fields/Doc";
+import { Plugins } from "./util";
+import { computedFn } from "mobx-utils";
function optional(propSchema: PropSchema) {
return custom(value => {
@@ -86,11 +88,39 @@ export class ScriptField extends ObjectField {
@Deserializable("computed", deserializeScript)
export class ComputedField extends ScriptField {
//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(doc: Doc) {
+ value = computedFn((doc: Doc) => {
const val = this.script.run({ this: doc });
if (val.success) {
return val.result;
}
return undefined;
+ });
+}
+
+export namespace ComputedField {
+ let useComputed = true;
+ export function DisableComputedFields() {
+ useComputed = false;
+ }
+
+ export function EnableComputedFields() {
+ useComputed = true;
+ }
+
+ export const undefined = "__undefined";
+
+ export function WithoutComputed<T>(fn: () => T) {
+ DisableComputedFields();
+ try {
+ return fn();
+ } finally {
+ EnableComputedFields();
+ }
}
+
+ Plugins.addGetterPlugin((doc, _, value) => {
+ if (useComputed && value instanceof ComputedField) {
+ return { value: value.value(doc), shouldReturn: true };
+ }
+ });
} \ No newline at end of file
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 47e467041..b59ec9b9a 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 } from "./Doc";
+import { Doc, Field, FieldResult } from "./Doc";
import { SerializationHelper } from "../client/util/SerializationHelper";
import { ProxyField } from "./Proxy";
import { RefField } from "./RefField";
@@ -11,6 +11,20 @@ import { ComputedField } from "./ScriptField";
function _readOnlySetter(): never {
throw new Error("Documents can't be modified in read-only mode");
}
+
+export interface GetterResult {
+ value: FieldResult;
+ shouldReturn: boolean;
+}
+export type GetterPlugin = (receiver: any, prop: string | number, currentValue: any) => GetterResult | undefined;
+const getterPlugins: GetterPlugin[] = [];
+
+export namespace Plugins {
+ export function addGetterPlugin(plugin: GetterPlugin) {
+ getterPlugins.push(plugin);
+ }
+}
+
const _setterImpl = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean {
//console.log("-set " + target[SelfProxy].title + "(" + target[SelfProxy][prop] + ")." + prop.toString() + " = " + value);
if (SerializationHelper.IsSerializing()) {
@@ -85,12 +99,18 @@ export function getter(target: any, prop: string | symbol | number, receiver: an
function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreProto: boolean = false): any {
receiver = receiver || target[SelfProxy];
- const field = target.__fields[prop];
+ let field = target.__fields[prop];
if (field instanceof ProxyField) {
return field.value();
}
- if (field instanceof ComputedField) {
- return field.value(receiver);
+ for (const plugin of getterPlugins) {
+ const res = plugin(receiver, prop, field);
+ if (res === undefined) continue;
+ if (res.shouldReturn) {
+ return res.value;
+ } else {
+ field = res.value;
+ }
}
if (field === undefined && !ignoreProto && prop !== "proto") {
const proto = getFieldImpl(target, "proto", receiver, true);//TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters