aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/util/Scripting.ts12
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx6
-rw-r--r--src/fields/ScriptField.ts9
-rw-r--r--src/new_fields/Doc.ts11
-rw-r--r--src/new_fields/Proxy.ts4
-rw-r--r--src/new_fields/util.ts36
6 files changed, 59 insertions, 19 deletions
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 40e2ad6bb..a59d9f201 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -39,7 +39,6 @@ export interface CompileError {
}
export type CompileResult = CompiledScript | CompileError;
-
function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult {
const errors = diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error);
if ((options.typecheck !== false && errors) || !script) {
@@ -64,10 +63,20 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
}
}
let thisParam = args.this || capturedVariables.this;
+ let batch: { end(): void } | undefined = undefined;
try {
+ if (!options.editable) {
+ batch = Doc.MakeReadOnly();
+ }
const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray);
+ if (batch) {
+ batch.end();
+ }
return { success: true, result };
} catch (error) {
+ if (batch) {
+ batch.end();
+ }
return { success: false, error };
}
};
@@ -133,6 +142,7 @@ export interface ScriptOptions {
params?: { [name: string]: string };
capturedVariables?: { [name: string]: Field };
typecheck?: boolean;
+ editable?: boolean;
}
export function CompileScript(script: string, options: ScriptOptions = {}): CompileResult {
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 3d626eef0..a4c14ae38 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -38,10 +38,11 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
}
public static SetField(doc: Doc, key: string, value: string) {
let eq = value.startsWith("=");
+ let target = eq ? doc : Doc.GetProto(doc);
value = eq ? value.substr(1) : value;
let dubEq = value.startsWith(":=");
value = dubEq ? value.substr(2) : value;
- let options: ScriptOptions = { addReturn: true };
+ let options: ScriptOptions = { addReturn: true, params: { this: "Doc" } };
if (dubEq) options.typecheck = false;
let script = CompileScript(value, options);
if (!script.compiled) {
@@ -49,12 +50,11 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
}
let field = new ComputedField(script);
if (!dubEq) {
- let res = script.run();
+ let res = script.run({ this: target });
if (!res.success) return false;
field = res.result;
}
if (Field.IsField(field, true)) {
- let target = eq ? doc : Doc.GetProto(doc);
target[key] = field;
return true;
}
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index ac46ccf90..dbca74720 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -3,7 +3,7 @@ import { CompiledScript, CompileScript } from "../client/util/Scripting";
import { Copy, ToScriptString, Parent, SelfProxy } from "../new_fields/FieldSymbols";
import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr";
import { Deserializable } from "../client/util/SerializationHelper";
-import { computed } from "mobx";
+import { Doc } from "../new_fields/Doc";
function optional(propSchema: PropSchema) {
return custom(value => {
@@ -23,6 +23,7 @@ const optionsSchema = createSimpleSchema({
requiredType: true,
addReturn: true,
typecheck: true,
+ readonly: true,
params: optional(map(primitive()))
});
@@ -86,9 +87,9 @@ export class ScriptField extends ObjectField {
@Deserializable("computed", deserializeScript)
export class ComputedField extends ScriptField {
- @computed
- get value() {
- const val = this._script.run({ this: (this[Parent] as any)[SelfProxy] });
+ //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) {
+ const val = this._script.run({ this: doc });
if (val.success) {
return val.result;
}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 9bacf49ba..cce4fff5d 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -2,7 +2,7 @@ import { observable, action } from "mobx";
import { serializable, primitive, map, alias, list } from "serializr";
import { autoObject, SerializationHelper, Deserializable } from "../client/util/SerializationHelper";
import { DocServer } from "../client/DocServer";
-import { setter, getter, getField, updateFunction, deleteProperty } from "./util";
+import { setter, getter, getField, updateFunction, deleteProperty, makeEditable, makeReadOnly } from "./util";
import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast } from "./Types";
import { listSpec } from "./Schema";
import { ObjectField } from "./ObjectField";
@@ -156,6 +156,15 @@ export namespace Doc {
// return Cast(field, ctor);
// });
// }
+ export function MakeReadOnly(): { end(): void } {
+ makeReadOnly();
+ return {
+ end() {
+ makeEditable();
+ }
+ };
+ }
+
export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldResult {
const self = doc[Self];
return getField(self, key, ignoreProto);
diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts
index 130ec066e..38d874a68 100644
--- a/src/new_fields/Proxy.ts
+++ b/src/new_fields/Proxy.ts
@@ -48,9 +48,8 @@ export class ProxyField<T extends RefField> extends ObjectField {
private failed = false;
private promise?: Promise<any>;
- value(callback?: ((field: T | undefined) => void)): T | undefined | FieldWaiting {
+ value(): T | undefined | FieldWaiting {
if (this.cache) {
- callback && callback(this.cache);
return this.cache;
}
if (this.failed) {
@@ -64,7 +63,6 @@ export class ProxyField<T extends RefField> extends ObjectField {
return field;
}));
}
- callback && this.promise.then(callback);
return this.promise;
}
}
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 8cb1db953..cc937c567 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -6,10 +6,13 @@ import { FieldValue } from "./Types";
import { RefField } from "./RefField";
import { ObjectField } from "./ObjectField";
import { action } from "mobx";
-import { Parent, OnUpdate, Update, Id, SelfProxy } from "./FieldSymbols";
+import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols";
import { ComputedField } from "../fields/ScriptField";
-export const setter = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean {
+function _readOnlySetter(): never {
+ throw new Error("Documents can't be modified in read-only mode");
+}
+const _setterImpl = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean {
if (SerializationHelper.IsSerializing()) {
target[prop] = value;
return true;
@@ -53,6 +56,20 @@ export const setter = action(function (target: any, prop: string | symbol | numb
return true;
});
+let _setter: (target: any, prop: string | symbol | number, value: any, receiver: any) => boolean = _setterImpl;
+
+export function makeReadOnly() {
+ _setter = _readOnlySetter;
+}
+
+export function makeEditable() {
+ _setter = _setterImpl;
+}
+
+export function setter(target: any, prop: string | symbol | number, value: any, receiver: any): boolean {
+ return _setter(target, prop, value, receiver);
+}
+
export function getter(target: any, prop: string | symbol | number, receiver: any): any {
if (typeof prop === "symbol") {
return target.__fields[prop] || target[prop];
@@ -60,25 +77,30 @@ export function getter(target: any, prop: string | symbol | number, receiver: an
if (SerializationHelper.IsSerializing()) {
return target[prop];
}
- return getField(target, prop);
+ return getFieldImpl(target, prop, receiver);
}
-export function getField(target: any, prop: string | number, ignoreProto: boolean = false): any {
+function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreProto: boolean = false): any {
+ receiver = receiver || target[SelfProxy];
const field = target.__fields[prop];
if (field instanceof ProxyField) {
return field.value();
}
if (field instanceof ComputedField) {
- return field.value;
+ return field.value(receiver);
}
if (field === undefined && !ignoreProto && prop !== "proto") {
- const proto = getField(target, "proto", true);
+ 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
if (proto instanceof Doc) {
- return proto[prop];
+ return getFieldImpl(proto[Self], prop, receiver, ignoreProto);
}
return undefined;
}
return field;
+
+}
+export function getField(target: any, prop: string | number, ignoreProto: boolean = false): any {
+ return getFieldImpl(target, prop, undefined, ignoreProto);
}
export function deleteProperty(target: any, prop: string | number | symbol) {