diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/DocServer.ts | 56 | ||||
-rw-r--r-- | src/client/documents/Documents.ts | 10 | ||||
-rw-r--r-- | src/client/util/LinkManager.ts | 13 | ||||
-rw-r--r-- | src/client/util/SerializationHelper.ts | 4 | ||||
-rw-r--r-- | src/client/views/TemplateMenu.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionTreeView.tsx | 28 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 31 | ||||
-rw-r--r-- | src/client/views/nodes/KeyValuePair.tsx | 17 | ||||
-rw-r--r-- | src/debug/Test.tsx | 88 | ||||
-rw-r--r-- | src/new_fields/Doc.ts | 13 | ||||
-rw-r--r-- | src/new_fields/Proxy.ts | 29 | ||||
-rw-r--r-- | src/new_fields/ScriptField.ts | 12 | ||||
-rw-r--r-- | src/new_fields/util.ts | 5 |
14 files changed, 153 insertions, 157 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index fc39fa364..87a87be92 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -124,13 +124,12 @@ export namespace DocServer { // future .proto calls on the Doc won't have to go farther than the cache to get their actual value. const deserializeField = getSerializedField.then(async fieldJson => { // deserialize - const field = await SerializationHelper.Deserialize(fieldJson, val => { - if (val !== undefined) { - _cache[id] = val; - } else { - delete _cache[id]; - } - }); + const field = await SerializationHelper.Deserialize(fieldJson); + if (field !== undefined) { + _cache[id] = field; + } else { + delete _cache[id]; + } return field; // either way, overwrite or delete any promises cached at this id (that we inserted as flags // to indicate that the field was in the process of being fetched). Now everything @@ -214,36 +213,37 @@ export namespace DocServer { // future .proto calls on the Doc won't have to go farther than the cache to get their actual value. const deserializeFields = getSerializedFields.then(async fields => { const fieldMap: { [id: string]: RefField } = {}; - // const protosToLoad: any = []; - const proms: Promise<RefField>[] = []; + const proms: Promise<void>[] = []; for (const field of fields) { if (field !== undefined) { // deserialize - let prom = SerializationHelper.Deserialize(field, val => { - if (val !== undefined) { - _cache[field.id] = field; + let prom = SerializationHelper.Deserialize(field).then(deserialized => { + fieldMap[field.id] = deserialized; + + //overwrite or delete any promises (that we inserted as flags + // to indicate that the field was in the process of being fetched). Now everything + // should be an actual value within or entirely absent from the cache. + if (deserialized !== undefined) { + _cache[field.id] = deserialized; } else { delete _cache[field.id]; } - }).then(deserialized => fieldMap[field.id] = deserialized); - proms.push(prom); + return deserialized; + }); + // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache) + // we set the value at the field's id to a promise that will resolve to the field. + // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method). + // The mapping in the .then call ensures that when other callers await these promises, they'll + // get the resolved field + _cache[field.id] = prom; // adds to a list of promises that will be awaited asynchronously - // protosToLoad.push(deserialized.proto); + proms.push(prom); } } await Promise.all(proms); - // this actually handles the loading of prototypes - // await Promise.all(protosToLoad); return fieldMap; }); - // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache) - // we set the value at the field's id to a promise that will resolve to the field. - // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method). - // The mapping in the .then call ensures that when other callers await these promises, they'll - // get the resolved field - requestedIds.forEach(id => _cache[id] = deserializeFields.then(fields => fields[id])); - // 5) at this point, all fields have a) been returned from the server and b) been deserialized into actual Field objects whose // prototype documents, if any, have also been fetched and cached. const fields = await deserializeFields; @@ -253,14 +253,6 @@ export namespace DocServer { // id to the soon-to-be-returned field mapping. requestedIds.forEach(id => { const field = fields[id]; - // either way, overwrite or delete any promises (that we inserted as flags - // to indicate that the field was in the process of being fetched). Now everything - // should be an actual value within or entirely absent from the cache. - if (field !== undefined) { - _cache[id] = field; - } else { - delete _cache[id]; - } map[id] = field; }); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5d1b6b382..09bafcf43 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -38,9 +38,11 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie import { LinkManager } from "../util/LinkManager"; import { DocumentManager } from "../util/DocumentManager"; import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox"; -import { Scripting } from "../util/Scripting"; +import { Scripting, CompileScript } from "../util/Scripting"; import { ButtonBox } from "../views/nodes/ButtonBox"; import { SchemaHeaderField, RandomPastel } from "../../new_fields/SchemaHeaderField"; +import { ComputedField } from "../../new_fields/ScriptField"; +import { ProxyField } from "../../new_fields/Proxy"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -193,6 +195,8 @@ export namespace Docs { * haven't been initialized, the newly initialized prototype document. */ export async function initialize(): Promise<void> { + ProxyField.initPlugin(); + ComputedField.initPlugin(); // non-guid string ids for each document prototype let prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix); // fetch the actual prototype documents from the server @@ -618,6 +622,10 @@ export namespace DocUtils { LinkManager.Instance.addLink(linkDocProto); + let script = `return links(this)};`; + let computed = CompileScript(script, { params: { this: "Doc" }, typecheck: false }); + computed.compiled && (Doc.GetProto(source).links = new ComputedField(computed)); + computed.compiled && (Doc.GetProto(target).links = new ComputedField(computed)); }, "make link"); return linkDoc; } diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index a647f22c1..448a8e9cf 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -6,6 +6,7 @@ import { List } from "../../new_fields/List"; import { Id } from "../../new_fields/FieldSymbols"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import { Docs } from "../documents/Documents"; +import { Scripting } from "./Scripting"; /* @@ -42,7 +43,12 @@ export class LinkManager { } public getAllLinks(): Doc[] { - return LinkManager.Instance.LinkManagerDoc ? LinkManager.Instance.LinkManagerDoc.allLinks ? DocListCast(LinkManager.Instance.LinkManagerDoc.allLinks) : [] : []; + let ldoc = LinkManager.Instance.LinkManagerDoc; + if (ldoc) { + let docs = DocListCast(ldoc.allLinks); + return docs; + } + return []; } public addLink(linkDoc: Doc): boolean { @@ -242,4 +248,7 @@ export class LinkManager { return Cast(linkDoc.anchor1, Doc, null); } } -}
\ No newline at end of file +} +Scripting.addGlobal(function links(doc: any) { + return new List(LinkManager.Instance.getAllRelatedLinks(doc)); +}); diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts index 13302be21..ff048f647 100644 --- a/src/client/util/SerializationHelper.ts +++ b/src/client/util/SerializationHelper.ts @@ -34,7 +34,7 @@ export namespace SerializationHelper { return json; } - export async function Deserialize(obj: any, cb: (val: any) => void = emptyFunction): Promise<any> { + export async function Deserialize(obj: any): Promise<any> { if (obj === undefined || obj === null) { return undefined; } @@ -57,7 +57,7 @@ export namespace SerializationHelper { } const type = serializationTypes[obj.__type]; - const value = await new Promise(res => cb(deserialize(type.ctor, obj, (err, result) => res(result)))); + const value = await new Promise(res => deserialize(type.ctor, obj, (err, result) => res(result))); if (type.afterDeserialize) { await type.afterDeserialize(value); } diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index c413650f0..e654a0644 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -39,7 +39,7 @@ export interface TemplateMenuProps { @observer export class TemplateMenu extends React.Component<TemplateMenuProps> { @observable private _hidden: boolean = true; - dragRef = React.createRef<HTMLDivElement>(); + dragRef = React.createRef<HTMLUListElement>(); constructor(props: TemplateMenuProps) { super(props); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 7f5d78313..02b2583cd 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -27,6 +27,7 @@ import "./CollectionTreeView.scss"; import React = require("react"); import { ComputedField } from '../../../new_fields/ScriptField'; import { KeyValueBox } from '../nodes/KeyValueBox'; +import { exportNamedDeclaration } from 'babel-types'; export interface TreeViewProps { @@ -79,11 +80,17 @@ class TreeView extends React.Component<TreeViewProps> { return splits.length > 1 ? splits[1].split("\"")[0] : "data"; } @computed get childDocs() { - let layout = this.props.document.layout as Doc; + let layout = this.props.document.layout instanceof Doc ? this.props.document.layout : undefined; return (this.props.dataDoc ? Cast(this.props.dataDoc[this.fieldKey], listSpec(Doc)) : undefined) || (layout ? Cast(layout[this.fieldKey], listSpec(Doc)) : undefined) || Cast(this.props.document[this.fieldKey], listSpec(Doc)); } + @computed get childLinks() { + let layout = this.props.document.layout instanceof Doc ? this.props.document.layout : undefined; + return (this.props.dataDoc ? Cast(this.props.dataDoc.links, listSpec(Doc)) : undefined) || + (layout instanceof Doc ? Cast(layout.links, listSpec(Doc)) : undefined) || + Cast(this.props.document.links, listSpec(Doc)); + } @computed get resolvedDataDoc() { if (this.props.dataDoc === undefined && this.props.document.layout instanceof Doc) { // if there is no dataDoc (ie, we're not rendering a template layout), but this document @@ -276,13 +283,15 @@ class TreeView extends React.Component<TreeViewProps> { noOverlays = (doc: Doc) => ({ title: "", caption: "" }); @computed get renderContent() { - if (this.treeViewExpandedView === this.fieldKey) { - let remDoc = (doc: Doc) => this.remove(doc, this.fieldKey); - let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc, addBefore, before); - return <ul key={this.fieldKey + "more"}> - {!this.childDocs ? (null) : - TreeView.GetChildElements(this.childDocs as Doc[], this.props.treeViewId, this.props.document.layout as Doc, - this.resolvedDataDoc, this.fieldKey, addDoc, remDoc, this.move, + const expandKey = this.treeViewExpandedView === this.fieldKey ? this.fieldKey : this.treeViewExpandedView === "links" ? "links" : undefined; + if (expandKey !== undefined) { + let remDoc = (doc: Doc) => this.remove(doc, expandKey); + let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before); + let docs = expandKey === "links" ? this.childLinks : this.childDocs; + return <ul key={expandKey + "more"}> + {!docs ? (null) : + TreeView.GetChildElements(docs as Doc[], this.props.treeViewId, this.props.document.layout as Doc, + this.resolvedDataDoc, expandKey, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth)} </ul >; @@ -334,7 +343,8 @@ class TreeView extends React.Component<TreeViewProps> { onPointerDown={action(() => { this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? "fields" : this.treeViewExpandedView === "fields" && this.props.document.layout ? "layout" : - this.childDocs ? this.fieldKey : "fields"; + this.treeViewExpandedView === "layout" && this.props.document.links ? "links" : + this.childDocs ? this.fieldKey : "fields"; this._collapsed = false; })}> {this.treeViewExpandedView} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 3ff816c46..232ac22c9 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -537,7 +537,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const width = Cast(viewDef.width, "number"); const height = Cast(viewDef.height, "number"); const fontSize = Cast(viewDef.fontSize, "number"); - if ([text, x, y, z, width, height].some(val => val === undefined)) { + if ([text, x, y, width, height].some(val => val === undefined)) { return undefined; } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 9a0615d68..17dc4184a 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,42 +1,39 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faImage, faFileAudio, faPaintBrush, faAsterisk } from '@fortawesome/free-solid-svg-icons'; -import { action, observable, computed, runInAction } from 'mobx'; +import { faEye } from '@fortawesome/free-regular-svg-icons'; +import { faAsterisk, faFileAudio, faImage, faPaintBrush } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, computed, observable, runInAction } from 'mobx'; import { observer } from "mobx-react"; import Lightbox from 'react-image-lightbox'; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app -import { Doc, HeightSym, WidthSym, DocListCast } from '../../../new_fields/Doc'; +import { Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc'; import { List } from '../../../new_fields/List'; import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema'; -import { Cast, FieldValue, NumCast, StrCast, BoolCast } from '../../../new_fields/Types'; -import { ImageField, AudioField } from '../../../new_fields/URLField'; +import { ComputedField } from '../../../new_fields/ScriptField'; +import { BoolCast, Cast, FieldValue, NumCast, StrCast } from '../../../new_fields/Types'; +import { AudioField, ImageField } from '../../../new_fields/URLField'; +import { RouteStore } from '../../../server/RouteStore'; import { Utils } from '../../../Utils'; +import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices'; +import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; +import { CompileScript } from '../../util/Scripting'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from "../../views/ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from '../DocComponent'; import { InkingControl } from '../InkingControl'; import { positionSchema } from './DocumentView'; +import FaceRectangles from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; import React = require("react"); -import { RouteStore } from '../../../server/RouteStore'; -import { Docs, DocumentType } from '../../documents/Documents'; -import { DocServer } from '../../DocServer'; -import { Font } from '@react-pdf/renderer'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { CognitiveServices, Service, Tag, Confidence } from '../../cognitive_services/CognitiveServices'; -import FaceRectangles from './FaceRectangles'; -import { faEye } from '@fortawesome/free-regular-svg-icons'; -import { ComputedField } from '../../../new_fields/ScriptField'; -import { CompileScript } from '../../util/Scripting'; -import { thisExpression } from 'babel-types'; var requestImageSize = require('../../util/request-image-size'); var path = require('path'); const { Howl } = require('howler'); -library.add(faImage, faEye, faPaintBrush); +library.add(faImage, faEye as any, faPaintBrush); library.add(faFileAudio, faAsterisk); diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 3775f0f47..534a42efc 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -1,22 +1,19 @@ import { action, observable } from 'mobx'; import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app -import { emptyFunction, returnFalse, returnZero, returnTrue, returnOne } from '../../../Utils'; -import { CompileScript, CompiledScript, ScriptOptions } from "../../util/Scripting"; +import { Doc, Field } from '../../../new_fields/Doc'; +import { emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils'; +import { Docs } from '../../documents/Documents'; import { Transform } from '../../util/Transform'; +import { undoBatch } from '../../util/UndoManager'; +import { CollectionDockingView } from '../collections/CollectionDockingView'; +import { ContextMenu } from '../ContextMenu'; import { EditableView } from "../EditableView"; import { FieldView, FieldViewProps } from './FieldView'; +import { KeyValueBox } from './KeyValueBox'; import "./KeyValueBox.scss"; import "./KeyValuePair.scss"; import React = require("react"); -import { Doc, Opt, Field } from '../../../new_fields/Doc'; -import { FieldValue } from '../../../new_fields/Types'; -import { KeyValueBox } from './KeyValueBox'; -import { DragManager, SetupDrag } from '../../util/DragManager'; -import { ContextMenu } from '../ContextMenu'; -import { Docs } from '../../documents/Documents'; -import { CollectionDockingView } from '../collections/CollectionDockingView'; -import { undoBatch } from '../../util/UndoManager'; // Represents one row in a key value plane diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx index 0dca4b4b1..79f87f4ac 100644 --- a/src/debug/Test.tsx +++ b/src/debug/Test.tsx @@ -1,81 +1,39 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { SerializationHelper } from '../client/util/SerializationHelper'; -import { createSchema, makeInterface, makeStrictInterface, listSpec } from '../new_fields/Schema'; -import { ImageField } from '../new_fields/URLField'; +import { DocServer } from '../client/DocServer'; import { Doc } from '../new_fields/Doc'; -import { List } from '../new_fields/List'; - -const schema1 = createSchema({ - hello: "number", - test: "string", - fields: "boolean", - url: ImageField, - testDoc: Doc -}); - -type TestDoc = makeInterface<[typeof schema1]>; -const TestDoc: (doc?: Doc) => TestDoc = makeInterface(schema1); - -const schema2 = createSchema({ - hello: ImageField, - test: "boolean", - fields: listSpec("number"), - url: "number", - testDoc: ImageField -}); - -const Test2Doc = makeStrictInterface(schema2); -type Test2Doc = makeStrictInterface<typeof schema2>; - -const assert = (bool: boolean) => { - if (!bool) throw new Error(); -}; +const protoId = "protoDoc"; +const delegateId = "delegateDoc"; class Test extends React.Component { - onClick = () => { - const url = new ImageField(new URL("http://google.com")); - const doc = new Doc(); - const doc2 = new Doc(); - doc.hello = 5; - doc.fields = "test"; - doc.test = "hello doc"; - doc.url = url; - //doc.testDoc = doc2; - + onCreateClick = () => { + const proto = new Doc(protoId, true); + const delegate = Doc.MakeDelegate(proto, delegateId); + } - const test1: TestDoc = TestDoc(doc); - assert(test1.hello === 5); - assert(test1.fields === undefined); - assert(test1.test === "hello doc"); - assert(test1.url === url); - assert(test1.testDoc === doc2); - test1.myField = 20; - assert(test1.myField === 20); + onReadClick = async () => { + console.log("reading"); + const docs = await DocServer.GetRefFields([delegateId, protoId]); + console.log("done"); + console.log(docs); + } - const test2: Test2Doc = Test2Doc(doc); - assert(test2.hello === undefined); - // assert(test2.fields === "test"); - assert(test2.test === undefined); - assert(test2.url === undefined); - assert(test2.testDoc === undefined); - test2.url = 35; - assert(test2.url === 35); - const l = new List<Doc>(); - //TODO push, and other array functions don't go through the proxy - l.push(doc2); - //TODO currently length, and any other string fields will get serialized - doc.list = l; - console.log(l.slice()); + onDeleteClick = () => { + DocServer.DeleteDocuments([protoId, delegateId]); } render() { - return <div><button onClick={this.onClick}>Click me</button> - {/* <input onKeyPress={this.onEnter}></input> */} - </div>; + return ( + <div> + <button onClick={this.onCreateClick}>Create Docs</button> + <button onClick={this.onReadClick}>Read Docs</button> + <button onClick={this.onDeleteClick}>Delete Docs</button> + </div> + ); } } +DocServer.init(window.location.protocol, window.location.hostname, 4321, "test"); ReactDOM.render( <Test />, document.getElementById('root') diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 84b8589dd..b70951040 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -12,7 +12,7 @@ import { scriptingGlobal } from "../client/util/Scripting"; import { List } from "./List"; import { DocumentType } from "../client/documents/Documents"; import { ComputedField } from "./ScriptField"; -import { PrefetchProxy } from "./Proxy"; +import { PrefetchProxy, ProxyField } from "./Proxy"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -420,7 +420,7 @@ export namespace Doc { export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc { const copy = new Doc; Object.keys(doc).forEach(key => { - const field = doc[key]; + const field = ProxyField.WithoutProxy(() => doc[key]); if (key === "proto" && copyProto) { if (field instanceof Doc) { copy[key] = Doc.MakeCopy(field); @@ -431,7 +431,7 @@ export namespace Doc { } else if (field instanceof ObjectField) { copy[key] = ObjectField.MakeCopy(field); } else if (field instanceof Promise) { - field.then(f => (copy[key] === undefined) && (copy[key] = f)); //TODO what should we do here? + debugger; //This shouldn't happend... } else { copy[key] = field; } @@ -525,17 +525,14 @@ export namespace Doc { } export function UseDetailLayout(d: Doc) { runInAction(async () => { - let detailLayout1 = await PromiseValue(d.detailedLayout); - let detailLayout = await PromiseValue(d.detailedLayout); + let detailLayout = await d.detailedLayout; if (detailLayout) { d.layout = detailLayout; d.nativeWidth = d.nativeHeight = undefined; if (detailLayout instanceof Doc) { let delegDetailLayout = Doc.MakeDelegate(detailLayout) as Doc; d.layout = delegDetailLayout; - let subDetailLayout1 = await PromiseValue(delegDetailLayout.detailedLayout); - let subDetailLayout = await PromiseValue(delegDetailLayout.detailedLayout); - delegDetailLayout.layout = subDetailLayout; + delegDetailLayout.layout = await delegDetailLayout.detailedLayout; } } }); diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts index b3e8d6467..c6292e37c 100644 --- a/src/new_fields/Proxy.ts +++ b/src/new_fields/Proxy.ts @@ -7,6 +7,7 @@ import { RefField } from "./RefField"; import { ObjectField } from "./ObjectField"; import { Id, Copy, ToScriptString } from "./FieldSymbols"; import { scriptingGlobal } from "../client/util/Scripting"; +import { Plugins } from "./util"; @Deserializable("proxy") export class ProxyField<T extends RefField> extends ObjectField { @@ -68,6 +69,34 @@ export class ProxyField<T extends RefField> extends ObjectField { } } +export namespace ProxyField { + let useProxy = true; + export function DisableProxyFields() { + useProxy = false; + } + + export function EnableProxyFields() { + useProxy = true; + } + + export function WithoutProxy<T>(fn: () => T) { + DisableProxyFields(); + try { + return fn(); + } finally { + EnableProxyFields(); + } + } + + export function initPlugin() { + Plugins.addGetterPlugin((doc, _, value) => { + if (useProxy && value instanceof ProxyField) { + return { value: value.value() }; + } + }); + } +} + function prefetchValue(proxy: PrefetchProxy<RefField>) { return proxy.value() as any; } diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts index 6d52525b8..83fb52d07 100644 --- a/src/new_fields/ScriptField.ts +++ b/src/new_fields/ScriptField.ts @@ -137,9 +137,11 @@ export namespace ComputedField { } } - Plugins.addGetterPlugin((doc, _, value) => { - if (useComputed && value instanceof ComputedField) { - return { value: value.value(doc), shouldReturn: true }; - } - }); + export function initPlugin() { + 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 2ebfb9e71..c6f693f7f 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -14,7 +14,7 @@ function _readOnlySetter(): never { export interface GetterResult { value: FieldResult; - shouldReturn: boolean; + shouldReturn?: boolean; } export type GetterPlugin = (receiver: any, prop: string | number, currentValue: any) => GetterResult | undefined; const getterPlugins: GetterPlugin[] = []; @@ -103,9 +103,6 @@ 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]; let field = target.__fields[prop]; - if (field instanceof ProxyField) { - return field.value(); - } for (const plugin of getterPlugins) { const res = plugin(receiver, prop, field); if (res === undefined) continue; |