diff options
Diffstat (limited to 'src/new_fields/Doc.ts')
| -rw-r--r-- | src/new_fields/Doc.ts | 157 |
1 files changed, 94 insertions, 63 deletions
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 3055af1bf..7f7263cf1 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -4,28 +4,54 @@ import { autoObject, SerializationHelper, Deserializable } from "../client/util/ import { DocServer } from "../client/DocServer"; import { setter, getter, getField, updateFunction, deleteProperty } from "./util"; import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast } from "./Types"; -import { UndoManager, undoBatch } from "../client/util/UndoManager"; import { listSpec } from "./Schema"; -import { List } from "./List"; -import { ObjectField, Parent, OnUpdate } from "./ObjectField"; -import { RefField, FieldId, Id, HandleUpdate } from "./RefField"; -import { Docs } from "../client/documents/Documents"; - -export function IsField(field: any): field is Field { - return (typeof field === "string") - || (typeof field === "number") - || (typeof field === "boolean") - || (field instanceof ObjectField) - || (field instanceof RefField); +import { ObjectField } from "./ObjectField"; +import { RefField, FieldId } from "./RefField"; +import { ToScriptString, SelfProxy, Parent, OnUpdate, Self, HandleUpdate, Update, Id } from "./FieldSymbols"; + +export namespace Field { + export function toScriptString(field: Field): string { + if (typeof field === "string") { + return `"${field}"`; + } else if (typeof field === "number" || typeof field === "boolean") { + return String(field); + } else { + return field[ToScriptString](); + } + } + export function IsField(field: any): field is Field; + export function IsField(field: any, includeUndefined: true): field is Field | undefined; + export function IsField(field: any, includeUndefined: boolean = false): field is Field | undefined { + return (typeof field === "string") + || (typeof field === "number") + || (typeof field === "boolean") + || (field instanceof ObjectField) + || (field instanceof RefField) + || (includeUndefined && field === undefined); + } } export type Field = number | string | boolean | ObjectField | RefField; export type Opt<T> = T | undefined; export type FieldWaiting<T extends RefField = RefField> = T extends undefined ? never : Promise<T | undefined>; export type FieldResult<T extends Field = Field> = Opt<T> | FieldWaiting<Extract<T, RefField>>; -export const Update = Symbol("Update"); -export const Self = Symbol("Self"); -const SelfProxy = Symbol("SelfProxy"); +/** + * Cast any field to either a List of Docs or undefined if the given field isn't a List of Docs. + * If a default value is given, that will be returned instead of undefined. + * If a default value is given, the returned value should not be modified as it might be a temporary value. + * If no default value is given, and the returned value is not undefined, it can be safely modified. + */ +export function DocListCastAsync(field: FieldResult): Promise<Doc[] | undefined>; +export function DocListCastAsync(field: FieldResult, defaultValue: Doc[]): Promise<Doc[]>; +export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) { + const list = Cast(field, listSpec(Doc)); + return list ? Promise.all(list).then(() => list) : Promise.resolve(defaultValue); +} + +export function DocListCast(field: FieldResult): Doc[] { + return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; +} + export const WidthSym = Symbol("Width"); export const HeightSym = Symbol("Height"); @@ -36,6 +62,7 @@ export class Doc extends RefField { const doc = new Proxy<this>(this, { set: setter, 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), getOwnPropertyDescriptor: (target, prop) => { @@ -43,6 +70,7 @@ export class Doc extends RefField { return { configurable: true,//TODO Should configurable be true? enumerable: true, + value: target.__fields[prop] }; } return Reflect.getOwnPropertyDescriptor(target, prop); @@ -85,11 +113,14 @@ export class Doc extends RefField { private [Self] = this; private [SelfProxy]: any; - public [WidthSym] = () => NumCast(this.__fields.width); // bcz: is this the right way to access width/height? it didn't work with : this.width - public [HeightSym] = () => NumCast(this.__fields.height); + public [WidthSym] = () => NumCast(this[SelfProxy].width); // bcz: is this the right way to access width/height? it didn't work with : this.width + public [HeightSym] = () => NumCast(this[SelfProxy].height); + + [ToScriptString]() { + return "invalid"; + } public [HandleUpdate](diff: any) { - console.log(diff); const set = diff.$set; if (set) { for (const key in set) { @@ -119,11 +150,15 @@ export namespace Doc { const self = doc[Self]; return getField(self, key, ignoreProto); } - export function GetT<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): T | null | undefined { - return Cast(Get(doc, key, ignoreProto), ctor) as T | null | undefined; + export function GetT<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): FieldResult<T> { + return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult<T>; + } + export function IsPrototype(doc: Doc) { + return GetT(doc, "isPrototype", "boolean", true); } export async function SetOnPrototype(doc: Doc, key: string, value: Field) { - const proto = doc.proto; + const proto = Object.getOwnPropertyNames(doc).indexOf("isPrototype") === -1 ? doc.proto : doc; + if (proto) { proto[key] = value; } @@ -141,23 +176,47 @@ export namespace Doc { for (const key in fields) { if (fields.hasOwnProperty(key)) { const value = fields[key]; - if (value !== undefined) { - doc[key] = value; - } + // Do we want to filter out undefineds? + // if (value !== undefined) { + doc[key] = value; + // } } } return doc; } - export function MakeAlias(doc: Doc) { - const alias = new Doc; + // compare whether documents or their protos match + export function AreProtosEqual(doc: Doc, other: Doc) { + let r = (doc === other); + let r2 = (doc.proto === other); + let r3 = (other.proto === doc); + let r4 = (doc.proto === other.proto); + return r || r2 || r3 || r4; + } - PromiseValue(Cast(doc.proto, Doc)).then(proto => { - if (proto) { - alias.proto = proto; - } - }); + // gets the document's prototype or returns the document if it is a prototype + export function GetProto(doc: Doc) { + return Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : doc.proto!; + } + + export function allKeys(doc: Doc): string[] { + const results: Set<string> = new Set; + let proto: Doc | undefined = doc; + while (proto) { + Object.keys(proto).forEach(key => results.add(key)); + proto = proto.proto; + } + + return Array.from(results); + } + + + export function MakeAlias(doc: Doc) { + const alias = new Doc; + if (!GetT(doc, "isPrototype", "boolean", true)) { + alias.proto = doc.proto; + } return alias; } @@ -182,42 +241,14 @@ export namespace Doc { return copy; } - export function MakeLink(source: Doc, target: Doc) { - UndoManager.RunInBatch(() => { - let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); - //let linkDoc = new Doc; - linkDoc.title = "-link name-"; - linkDoc.linkDescription = ""; - linkDoc.linkTags = "Default"; - - linkDoc.linkedTo = target; - linkDoc.linkedFrom = source; - - let linkedFrom = Cast(target.linkedFromDocs, listSpec(Doc)); - if (!linkedFrom) { - target.linkedFromDocs = linkedFrom = new List<Doc>(); - } - linkedFrom.push(linkDoc); - - let linkedTo = Cast(source.linkedToDocs, listSpec(Doc)); - if (!linkedTo) { - source.linkedToDocs = linkedTo = new List<Doc>(); - } - linkedTo.push(linkDoc); - return linkDoc; - }, "make link"); - } - - export function MakeDelegate(doc: Doc): Doc; - export function MakeDelegate(doc: Opt<Doc>): Opt<Doc>; - export function MakeDelegate(doc: Opt<Doc>): Opt<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; } - const delegate = new Doc(); - //TODO Does this need to be doc[Self]? + const delegate = new Doc(id, true); delegate.proto = doc; return delegate; } - export const Prototype = Symbol("Prototype"); }
\ No newline at end of file |
