diff options
Diffstat (limited to 'src/new_fields/Doc.ts')
| -rw-r--r-- | src/new_fields/Doc.ts | 118 | 
1 files changed, 86 insertions, 32 deletions
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 2ad6ae5f0..1a00db1c1 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -1,6 +1,6 @@  import { observable, action } from "mobx"; -import { serializable, primitive, map, alias, list } from "serializr"; -import { autoObject, SerializationHelper, Deserializable } from "../client/util/SerializationHelper"; +import { serializable, primitive, map, alias, list, PropSchema, custom } from "serializr"; +import { autoObject, SerializationHelper, Deserializable, afterDocDeserialize } from "../client/util/SerializationHelper";  import { DocServer } from "../client/DocServer";  import { setter, getter, getField, updateFunction, deleteProperty, makeEditable, makeReadOnly } from "./util";  import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast, BoolCast, StrCast } from "./Types"; @@ -68,8 +68,17 @@ export function DocListCast(field: FieldResult): Doc[] {  export const WidthSym = Symbol("Width");  export const HeightSym = Symbol("Height"); +function fetchProto(doc: Doc) { +    const proto = doc.proto; +    if (proto instanceof Promise) { +        return proto; +    } +} + +let updatingFromServer = false; +  @scriptingGlobal -@Deserializable("doc").withFields(["id"]) +@Deserializable("Doc", fetchProto).withFields(["id"])  export class Doc extends RefField {      constructor(id?: FieldId, forceSave?: boolean) {          super(id); @@ -102,7 +111,7 @@ export class Doc extends RefField {      proto: Opt<Doc>;      [key: string]: FieldResult; -    @serializable(alias("fields", map(autoObject()))) +    @serializable(alias("fields", map(autoObject(), { afterDeserialize: afterDocDeserialize })))      private get __fields() {          return this.___fields;      } @@ -122,6 +131,9 @@ export class Doc extends RefField {      private ___fields: any = {};      private [Update] = (diff: any) => { +        if (updatingFromServer) { +            return; +        }          DocServer.UpdateField(this[Id], diff);      } @@ -134,16 +146,18 @@ export class Doc extends RefField {          return "invalid";      } -    public [HandleUpdate](diff: any) { +    public async [HandleUpdate](diff: any) {          const set = diff.$set;          if (set) {              for (const key in set) {                  if (!key.startsWith("fields.")) {                      continue;                  } -                const value = SerializationHelper.Deserialize(set[key]); +                const value = await SerializationHelper.Deserialize(set[key]);                  const fKey = key.substring(7); +                updatingFromServer = true;                  this[fKey] = value; +                updatingFromServer = false;              }          }          const unset = diff.$unset; @@ -153,7 +167,9 @@ export class Doc extends RefField {                      continue;                  }                  const fKey = key.substring(7); +                updatingFromServer = true;                  delete this[fKey]; +                updatingFromServer = false;              }          }      } @@ -244,7 +260,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;      } @@ -298,7 +314,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;      } @@ -314,20 +330,22 @@ export namespace Doc {      }      export function UpdateDocumentExtensionForField(doc: Doc, fieldKey: string) { -        let extensionDoc = doc[fieldKey + "_ext"]; -        if (extensionDoc === undefined) { +        let docExtensionForField = doc[fieldKey + "_ext"] as Doc; +        if (docExtensionForField === undefined) {              setTimeout(() => { -                let docExtensionForField = new Doc(doc[Id] + fieldKey, true); -                docExtensionForField.title = doc.title + ":" + fieldKey + ".ext"; -                docExtensionForField.extendsDoc = doc; +                docExtensionForField = new Doc(doc[Id] + fieldKey, true); +                docExtensionForField.title = fieldKey + ".ext"; +                docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends. +                docExtensionForField.type = DocumentType.EXTENSION;                  let proto: Doc | undefined = doc;                  while (proto && !Doc.IsPrototype(proto)) {                      proto = proto.proto;                  }                  (proto ? proto : doc)[fieldKey + "_ext"] = docExtensionForField;              }, 0); -        } else if (extensionDoc instanceof Doc && extensionDoc.extendsDoc === undefined) { -            setTimeout(() => (extensionDoc as Doc).extendsDoc = doc, 0); +        } else if (doc instanceof Doc) { // backward compatibility -- add fields for docs that don't have them already +            docExtensionForField.extendsDoc === undefined && setTimeout(() => docExtensionForField.extendsDoc = doc, 0); +            docExtensionForField.type === undefined && setTimeout(() => docExtensionForField.type = DocumentType.EXTENSION, 0);          }      }      export function MakeAlias(doc: Doc) { @@ -337,11 +355,25 @@ export namespace Doc {          return Doc.MakeDelegate(doc); // bcz?      } +    // +    // Determines whether the combination of the layoutDoc and dataDoc represents +    // a template relationship.  If so, the layoutDoc will be 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 WillExpandTemplateLayout(layoutDoc: Doc, dataDoc?: Doc) { +        return BoolCast(layoutDoc.isTemplate) && dataDoc && layoutDoc !== dataDoc && !(layoutDoc.layout instanceof Doc); +    } + +    // +    // Returns an expanded template layout for a target data document. +    // First it checks if an expanded layout already exists -- if so it will be stored on the dataDoc +    // using the template layout doc's id as the field key. +    // 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 +    //      export function expandTemplateLayout(templateLayoutDoc: Doc, dataDoc?: Doc) { -        let resolvedDataDoc = (templateLayoutDoc !== dataDoc) ? dataDoc : undefined; -        if (!dataDoc || !(templateLayoutDoc && !(Cast(templateLayoutDoc.layout, Doc) instanceof Doc) && resolvedDataDoc && resolvedDataDoc !== templateLayoutDoc)) { -            return templateLayoutDoc; -        } +        if (!WillExpandTemplateLayout(templateLayoutDoc, dataDoc) || !dataDoc) return templateLayoutDoc;          // if we have a data doc that doesn't match the layout, then we're rendering a template.          // ... 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. @@ -350,19 +382,18 @@ export namespace Doc {          if (expandedTemplateLayout instanceof Doc) {              return expandedTemplateLayout;          } -        if (expandedTemplateLayout === undefined && BoolCast(templateLayoutDoc.isTemplate)) { -            setTimeout(() => { -                let expandedDoc = Doc.MakeDelegate(templateLayoutDoc); -                expandedDoc.title = templateLayoutDoc.title + "[" + StrCast(dataDoc.title).match(/\.\.\.[0-9]*/) + "]"; -                expandedDoc.isExpandedTemplate = templateLayoutDoc; -                dataDoc[templateLayoutDoc[Id]] = expandedDoc; -            }, 0); +        let expandedLayoutFieldKey = "Layout[" + templateLayoutDoc[Id] + "]"; +        expandedTemplateLayout = dataDoc[expandedLayoutFieldKey]; +        if (expandedTemplateLayout instanceof Doc) { +            return expandedTemplateLayout; +        } +        if (expandedTemplateLayout === undefined) { +            setTimeout(() => +                dataDoc[expandedLayoutFieldKey] = Doc.MakeDelegate(templateLayoutDoc, undefined, "["+templateLayoutDoc.title + "]"), 0);          }          return templateLayoutDoc; // use the templateLayout when it's not a template or the expandedTemplate is pending.      } -    let _pendingExpansions: Map<string, boolean> = new Map(); -      export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc {          const copy = new Doc;          Object.keys(doc).forEach(key => { @@ -387,17 +418,32 @@ export namespace Doc {          return copy;      } -    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> { +    export function MakeDelegate(doc: Doc, id?: string, title?: string): Doc; +    export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc>; +    export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc> {          if (doc) {              const delegate = new Doc(id, true);              delegate.proto = doc; +            title && (delegate.title = title);              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) {          // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)          let backgroundLayout = StrCast(fieldTemplate.backgroundLayout); @@ -407,7 +453,6 @@ export namespace Doc {          }          let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);          if (backgroundLayout) { -            layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`);              backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);          }          let nw = Cast(fieldTemplate.nativeWidth, "number"); @@ -417,6 +462,7 @@ export namespace Doc {          layoutDelegate.layout = layout;          fieldTemplate.title = metaKey; +        fieldTemplate.templateField = metaKey;          fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;          fieldTemplate.backgroundLayout = backgroundLayout;          fieldTemplate.nativeWidth = nw; @@ -432,4 +478,12 @@ export namespace Doc {          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;      } +    export async function UseDetailLayout(d: Doc) { +        let miniLayout = await PromiseValue(d.miniLayout); +        let detailLayout = await PromiseValue(d.detailedLayout); +        if (miniLayout && d.layout === miniLayout && detailLayout) { +            d.layout = detailLayout; +            d.nativeWidth = d.nativeHeight = undefined; +        } +    }  }
\ No newline at end of file  | 
