From c93c3e1970c6ecc91b60f1782e82b1bfdc7fef30 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Wed, 13 Feb 2019 04:50:56 -0500
Subject: A lot of stuff working, a lot of other stuff not working

---
 src/client/views/nodes/FieldView.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/client/views/nodes/FieldView.tsx')

diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 12371eb2e..08de53e1c 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -10,7 +10,6 @@ import { ImageField } from "../../../fields/ImageField";
 import { Key } from "../../../fields/Key";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { ImageBox } from "./ImageBox";
-import { DocumentView } from "./DocumentView";
 
 //
 // these properties get assigned through the render() method of the DocumentView when it creates this node.
@@ -20,12 +19,13 @@ import { DocumentView } from "./DocumentView";
 export interface FieldViewProps {
     fieldKey: Key;
     doc: Document;
-    DocumentViewForField: Opt<DocumentView>
+    isSelected: () => boolean;
+    isTopMost: boolean;
 }
 
 @observer
 export class FieldView extends React.Component<FieldViewProps> {
-    public static LayoutString(fieldType: string) { return `<${fieldType} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} />`; }
+    public static LayoutString(fieldType: string) { return `<${fieldType} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} isSelected={isSelected} isTopMost={isTopMost} />`; }
     @computed
     get field(): FieldValue<Field> {
         const { doc, fieldKey } = this.props;
-- 
cgit v1.2.3-70-g09d2


From 38ca0618dcdeb57d5a91557b392c1aff095868be Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Fri, 22 Feb 2019 03:01:30 -0500
Subject: Added CollectionView Switched sub-collections to not inherit from
 CollectionViewBase

---
 src/client/Server.ts                               |   5 +-
 src/client/documents/Documents.ts                  |  81 +++++---------
 src/client/views/Main.tsx                          |   8 +-
 .../views/collections/CollectionDockingView.tsx    |  11 +-
 .../views/collections/CollectionFreeFormView.tsx   |  25 ++---
 .../views/collections/CollectionSchemaView.tsx     |  13 ++-
 src/client/views/collections/CollectionView.tsx    | 116 +++++++++++++++++++++
 .../views/collections/CollectionViewBase.tsx       |  61 -----------
 src/client/views/nodes/DocumentView.tsx            |  13 ++-
 src/client/views/nodes/FieldView.tsx               |   3 +-
 src/fields/BasicField.ts                           |   6 +-
 src/fields/Document.ts                             |   9 ++
 src/fields/KeyStore.ts                             |   1 +
 src/fields/ListField.ts                            |  19 +++-
 14 files changed, 212 insertions(+), 159 deletions(-)
 create mode 100644 src/client/views/collections/CollectionView.tsx
 delete mode 100644 src/client/views/collections/CollectionViewBase.tsx

(limited to 'src/client/views/nodes/FieldView.tsx')

diff --git a/src/client/Server.ts b/src/client/Server.ts
index 3e61729ab..06ac22c61 100644
--- a/src/client/Server.ts
+++ b/src/client/Server.ts
@@ -60,13 +60,16 @@ export class Server {
         });
     }
 
-    public static GetDocumentField(doc: Document, key: Key) {
+    public static GetDocumentField(doc: Document, key: Key, callback?: (field: Field) => void) {
         let field = doc._proxies.get(key.Id);
         if (field) {
             this.GetField(field,
                 action((fieldfromserver: Opt<Field>) => {
                     if (fieldfromserver) {
                         doc.fields.set(key.Id, { key, field: fieldfromserver });
+                        if (callback) {
+                            callback(fieldfromserver);
+                        }
                     }
                 }));
         }
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 920068273..bfa6cb7a9 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -11,6 +11,7 @@ import { ImageField } from "../../fields/ImageField";
 import { ImageBox } from "../views/nodes/ImageBox";
 import { CollectionFreeFormView } from "../views/collections/CollectionFreeFormView";
 import { FieldId } from "../../fields/Field";
+import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
 
 interface DocumentOptions {
     x?: number;
@@ -24,12 +25,10 @@ interface DocumentOptions {
 
 export namespace Documents {
     export function initProtos(callback: () => void) {
-        Server.GetFields([collectionProtoId, textProtoId, imageProtoId, schemaProtoId, dockProtoId], (fields) => {
+        Server.GetFields([collectionProtoId, textProtoId, imageProtoId], (fields) => {
             collectionProto = fields[collectionProtoId] as Document;
             imageProto = fields[imageProtoId] as Document;
             textProto = fields[textProtoId] as Document;
-            dockProto = fields[dockProtoId] as Document;
-            schemaProto = fields[schemaProtoId] as Document;
             callback()
         });
     }
@@ -83,52 +82,6 @@ export namespace Documents {
         return doc;
     }
 
-    let schemaProto: Document;
-    const schemaProtoId = "schemaProto";
-    function GetSchemaPrototype(): Document {
-        if (!schemaProto) {
-            schemaProto = new Document(schemaProtoId);
-            schemaProto.Set(KeyStore.X, new NumberField(0));
-            schemaProto.Set(KeyStore.Y, new NumberField(0));
-            schemaProto.Set(KeyStore.Width, new NumberField(300));
-            schemaProto.Set(KeyStore.Height, new NumberField(150));
-            schemaProto.Set(KeyStore.Layout, new TextField(CollectionSchemaView.LayoutString()));
-            schemaProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data]));
-        }
-        return schemaProto;
-    }
-
-    export function SchemaDocument(documents: Array<Document>, options: DocumentOptions = {}): Document {
-        let doc = GetSchemaPrototype().MakeDelegate();
-        setupOptions(doc, options);
-        doc.Set(KeyStore.Data, new ListField(documents));
-        return doc;
-    }
-
-
-    let dockProto: Document;
-    const dockProtoId = "dockProto";
-    function GetDockPrototype(): Document {
-        if (!dockProto) {
-            dockProto = new Document();
-            dockProto.Set(KeyStore.X, new NumberField(0));
-            dockProto.Set(KeyStore.Y, new NumberField(0));
-            dockProto.Set(KeyStore.Width, new NumberField(300));
-            dockProto.Set(KeyStore.Height, new NumberField(150));
-            dockProto.Set(KeyStore.Layout, new TextField(CollectionDockingView.LayoutString()));
-            dockProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data]));
-        }
-        return dockProto;
-    }
-
-    export function DockDocument(config: string, options: DocumentOptions = {}, id?: string): Document {
-        let doc = GetDockPrototype().MakeDelegate(id);
-        setupOptions(doc, options);
-        doc.SetText(KeyStore.Data, config);
-        return doc;
-    }
-
-
     let imageProto: Document;
     const imageProtoId = "imageProto";
     function GetImagePrototype(): Document {
@@ -141,7 +94,8 @@ export namespace Documents {
             imageProto.Set(KeyStore.NativeHeight, new NumberField(300));
             imageProto.Set(KeyStore.Width, new NumberField(300));
             imageProto.Set(KeyStore.Height, new NumberField(300));
-            imageProto.Set(KeyStore.Layout, new TextField(CollectionFreeFormView.LayoutString("AnnotationsKey")));
+            imageProto.Set(KeyStore.Layout, new TextField(CollectionView.LayoutString("AnnotationsKey")));
+            imageProto.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform)
             imageProto.Set(KeyStore.BackgroundLayout, new TextField(ImageBox.LayoutString()));
             // imageProto.SetField(KeyStore.Layout, new TextField('<div style={"background-image: " + {Data}} />'));
             imageProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data, KeyStore.Annotations]));
@@ -165,23 +119,36 @@ export namespace Documents {
     function GetCollectionPrototype(): Document {
         if (!collectionProto) {
             collectionProto = new Document(collectionProtoId);
-            collectionProto.Set(KeyStore.X, new NumberField(0));
-            collectionProto.Set(KeyStore.Y, new NumberField(0));
             collectionProto.Set(KeyStore.Scale, new NumberField(1));
             collectionProto.Set(KeyStore.PanX, new NumberField(0));
             collectionProto.Set(KeyStore.PanY, new NumberField(0));
-            collectionProto.Set(KeyStore.Width, new NumberField(300));
-            collectionProto.Set(KeyStore.Height, new NumberField(300));
-            collectionProto.Set(KeyStore.Layout, new TextField(CollectionFreeFormView.LayoutString("DataKey")));
+            collectionProto.Set(KeyStore.Layout, new TextField(CollectionView.LayoutString("DataKey")));
             collectionProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data]));
         }
         return collectionProto;
     }
 
-    export function CollectionDocument(documents: Array<Document>, options: DocumentOptions = {}, id?: string): Document {
+    export function CollectionDocument(data: Array<Document> | string, viewType: CollectionViewType, options: DocumentOptions = {}, id?: string): Document {
         let doc = GetCollectionPrototype().MakeDelegate(id);
         setupOptions(doc, options);
-        doc.Set(KeyStore.Data, new ListField(documents));
+        if (typeof data === "string") {
+            doc.SetText(KeyStore.Data, data);
+        } else {
+            doc.SetData(KeyStore.Data, data, ListField);
+        }
+        doc.SetNumber(KeyStore.ViewType, viewType);
         return doc;
     }
+
+    export function FreeformDocument(documents: Array<Document>, options: DocumentOptions, id?: string) {
+        return CollectionDocument(documents, CollectionViewType.Freeform, options, id)
+    }
+
+    export function SchemaDocument(documents: Array<Document>, options: DocumentOptions, id?: string) {
+        return CollectionDocument(documents, CollectionViewType.Schema, options, id)
+    }
+
+    export function DockDocument(config: string, options: DocumentOptions, id?: string) {
+        return CollectionDocument(config, CollectionViewType.Docking, options, id)
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index c7a6a44e8..fe1a999ec 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -61,16 +61,14 @@ Documents.initProtos(() => {
         let mainfreeform: Document;
         if (res) {
             mainContainer = ServerUtils.FromJson(res) as Document;
-            mainfreeform = mainContainer.Get(KeyStore.ActiveFrame) as Document;
-            if (!mainfreeform)
-                Server.GetField(mainContainer._proxies.get(KeyStore.ActiveFrame.Id)!, (field) => mainfreeform = field as Document);
+            mainContainer.GetAsync(KeyStore.ActiveFrame, field => mainfreeform = field as Document);
         }
         else {
             mainContainer = Documents.DockDocument(JSON.stringify({ content: [{ type: 'row', content: [] }] }), { title: "main container" }, mainDocId);
             Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(mainContainer.ToJson()))
 
             setTimeout(() => {
-                mainfreeform = Documents.CollectionDocument([], { x: 0, y: 400, title: "mini collection" });
+                mainfreeform = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" });
                 Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(mainfreeform.ToJson()));
 
                 var docs = [mainfreeform].map(doc => CollectionDockingView.makeDocumentConfig(doc));
@@ -90,7 +88,7 @@ Documents.initProtos(() => {
             }));
         })
         let addColNode = action(() => {
-            mainfreeform.GetList<Document>(KeyStore.Data, []).push(Documents.CollectionDocument([], {
+            mainfreeform.GetList<Document>(KeyStore.Data, []).push(Documents.FreeformDocument([], {
                 x: 0, y: 300, width: 200, height: 200, title: "added note"
             }));
         })
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 60dc24b5f..6d91aef5e 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -14,13 +14,12 @@ import { DragManager } from "../../util/DragManager";
 import { undoBatch } from "../../util/UndoManager";
 import { DocumentView } from "../nodes/DocumentView";
 import "./CollectionDockingView.scss";
-import { CollectionViewBase, CollectionViewProps, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase";
+import { CollectionViewProps, COLLECTION_BORDER_WIDTH, SubCollectionViewProps } from "./CollectionView";
 import React = require("react");
 
 @observer
-export class CollectionDockingView extends CollectionViewBase {
+export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
     public static Instance: CollectionDockingView;
-    public static LayoutString() { return CollectionViewBase.LayoutString("CollectionDockingView"); }
     public static makeDocumentConfig(document: Document) {
         return {
             type: 'react-component',
@@ -41,7 +40,7 @@ export class CollectionDockingView extends CollectionViewBase {
     private _containerRef = React.createRef<HTMLDivElement>();
     private _fullScreen: any = null;
 
-    constructor(props: CollectionViewProps) {
+    constructor(props: SubCollectionViewProps) {
         super(props);
         CollectionDockingView.Instance = this;
         (window as any).React = React;
@@ -187,7 +186,7 @@ export class CollectionDockingView extends CollectionViewBase {
     }
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        if (e.button === 2 && this.active) {
+        if (e.button === 2 && this.props.active()) {
             e.stopPropagation();
             e.preventDefault();
         } else {
@@ -195,7 +194,7 @@ export class CollectionDockingView extends CollectionViewBase {
             if (className == "lm_drag_handle" || className == "lm_close" || className == "lm_maximise" || className == "lm_minimise" || className == "lm_close_tab") {
                 this._flush = true;
             }
-            if (e.buttons === 1 && this.active) {
+            if (e.buttons === 1 && this.props.active()) {
                 e.stopPropagation();
             }
         }
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 986bcdcee..bd7ca5b6f 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -4,7 +4,7 @@ import { action, computed } from "mobx";
 import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
 import { DragManager } from "../../util/DragManager";
 import "./CollectionFreeFormView.scss";
-import { CollectionViewBase, COLLECTION_BORDER_WIDTH, CollectionViewProps } from "./CollectionViewBase";
+import { COLLECTION_BORDER_WIDTH, CollectionViewProps, SubCollectionViewProps } from "./CollectionView";
 import { KeyStore } from "../../../fields/KeyStore";
 import { Document } from "../../../fields/Document";
 import { ListField } from "../../../fields/ListField";
@@ -16,8 +16,7 @@ import { DocumentView } from "../nodes/DocumentView";
 import { undoBatch } from "../../util/UndoManager";
 
 @observer
-export class CollectionFreeFormView extends CollectionViewBase {
-    public static LayoutString(fieldKey: string = "DataKey") { return CollectionViewBase.LayoutString("CollectionFreeFormView", fieldKey); }
+export class CollectionFreeFormView extends React.Component<SubCollectionViewProps> {
     private _canvasRef = React.createRef<HTMLDivElement>();
     private _lastX: number = 0;
     private _lastY: number = 0;
@@ -38,7 +37,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
     @computed
     get resizeScaling() { return this.isAnnotationOverlay ? this.props.Document.GetNumber(KeyStore.Width, 0) / this.nativeWidth : 1; }
 
-    constructor(props: CollectionViewProps) {
+    constructor(props: SubCollectionViewProps) {
         super(props);
     }
 
@@ -46,9 +45,11 @@ export class CollectionFreeFormView extends CollectionViewBase {
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         const doc: DocumentView = de.data["document"];
-        if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this) {
-            doc.props.ContainingCollectionView.removeDocument(doc.props.Document);
-            this.addDocument(doc.props.Document);
+        if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this.props.CollectionView) {
+            if (doc.props.RemoveDocument) {
+                doc.props.RemoveDocument(doc.props.Document);
+            }
+            this.props.addDocument(doc.props.Document);
         }
         const xOffset = de.data["xOffset"] as number || 0;
         const yOffset = de.data["yOffset"] as number || 0;
@@ -79,7 +80,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        if ((e.button === 2 && this.active) ||
+        if ((e.button === 2 && this.props.active()) ||
             !e.defaultPrevented) {
             document.removeEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointermove", this.onPointerMove);
@@ -104,7 +105,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
 
     @action
     onPointerMove = (e: PointerEvent): void => {
-        if (!e.cancelBubble && this.active) {
+        if (!e.cancelBubble && this.props.active()) {
             e.preventDefault();
             e.stopPropagation();
             let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
@@ -248,13 +249,13 @@ export class CollectionFreeFormView extends CollectionViewBase {
                     {this.props.BackgroundView ? this.props.BackgroundView() : null}
                     {lvalue.Data.map(doc => {
                         return (<CollectionFreeFormDocumentView key={doc.Id} Document={doc}
-                            AddDocument={this.addDocument}
-                            RemoveDocument={this.removeDocument}
+                            AddDocument={this.props.addDocument}
+                            RemoveDocument={this.props.removeDocument}
                             ScreenToLocalTransform={this.getTransform}
                             isTopMost={false}
                             Scaling={1}
                             PanelSize={[doc.GetNumber(KeyStore.Width, 0), doc.GetNumber(KeyStore.Height, 0)]}
-                            ContainingCollectionView={this} />);
+                            ContainingCollectionView={this.props.CollectionView} />);
                     })}
                 </div>
             </div>
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 5ec288b13..9405c820f 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -13,12 +13,10 @@ import { EditableView } from "../EditableView";
 import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
-import { CollectionViewBase, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase";
+import { COLLECTION_BORDER_WIDTH, CollectionViewProps, SubCollectionViewProps } from "./CollectionView";
 
 @observer
-export class CollectionSchemaView extends CollectionViewBase {
-    public static LayoutString(fieldKey: string = "DataKey") { return CollectionViewBase.LayoutString("CollectionSchemaView", fieldKey); }
-
+export class CollectionSchemaView extends React.Component<SubCollectionViewProps> {
     private _mainCont = React.createRef<HTMLDivElement>();
 
     private DIVIDER_WIDTH = 5;
@@ -34,6 +32,7 @@ export class CollectionSchemaView extends CollectionViewBase {
             doc: rowProps.value[0],
             fieldKey: rowProps.value[1],
             isSelected: () => false,
+            select: () => { },
             isTopMost: false
         }
         let contents = (
@@ -112,7 +111,7 @@ export class CollectionSchemaView extends CollectionViewBase {
         //     e.preventDefault();
         // } else 
         {
-            if (e.buttons === 1 && this.active) {
+            if (e.buttons === 1 && this.props.active()) {
                 e.stopPropagation();
             }
         }
@@ -145,12 +144,12 @@ export class CollectionSchemaView extends CollectionViewBase {
                 {({ measureRef }) =>
                     <div ref={measureRef}>
                         <DocumentView Document={selected}
-                            AddDocument={this.addDocument} RemoveDocument={this.removeDocument}
+                            AddDocument={this.props.addDocument} RemoveDocument={this.props.removeDocument}
                             ScreenToLocalTransform={this.getTransform}
                             Scaling={this._parentScaling}
                             isTopMost={false}
                             PanelSize={[this._panelWidth, this._panelHeight]}
-                            ContainingCollectionView={me} />
+                            ContainingCollectionView={this.props.CollectionView} />
                     </div>
                 }
             </Measure>
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
new file mode 100644
index 000000000..651d85879
--- /dev/null
+++ b/src/client/views/collections/CollectionView.tsx
@@ -0,0 +1,116 @@
+import { action, computed } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from "../../../fields/Document";
+import { Key } from "../../../fields/Key";
+import { ListField } from "../../../fields/ListField";
+import { SelectionManager } from "../../util/SelectionManager";
+import { ContextMenu } from "../ContextMenu";
+import React = require("react");
+import { Transform } from "../../util/Transform";
+import { KeyStore } from "../../../fields/KeyStore";
+import { NumberField } from "../../../fields/NumberField";
+import { CollectionFreeFormView } from "./CollectionFreeFormView";
+import { CollectionDockingView } from "./CollectionDockingView";
+import { CollectionSchemaView } from "./CollectionSchemaView";
+import { Opt } from "../../../fields/Field";
+
+
+export interface CollectionViewProps {
+    fieldKey: Key;
+    Document: Document;
+    ScreenToLocalTransform: () => Transform;
+    isSelected: () => boolean;
+    isTopMost: boolean;
+    select: (ctrlPressed: boolean) => void;
+    BackgroundView?: () => JSX.Element;
+}
+
+export interface SubCollectionViewProps extends CollectionViewProps {
+    active: () => boolean;
+    addDocument: (doc: Document) => void;
+    removeDocument: (doc: Document) => boolean;
+    CollectionView: Opt<CollectionView>;
+}
+
+export enum CollectionViewType {
+    Invalid,
+    Freeform,
+    Schema,
+    Docking,
+}
+
+export const COLLECTION_BORDER_WIDTH = 2;
+
+@observer
+export class CollectionView extends React.Component<CollectionViewProps> {
+
+    public static LayoutString(fieldKey: string = "DataKey") {
+        return `<CollectionView Document={Document}
+                    ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} isSelected={isSelected} select={select}
+                    isTopMost={isTopMost} BackgroundView={BackgroundView} />`;
+    }
+    public active = () => {
+        var isSelected = this.props.isSelected();
+        var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this);
+        var topMost = this.props.isTopMost;
+        return isSelected || childSelected || topMost;
+    }
+    @action
+    addDocument = (doc: Document): void => {
+        //TODO This won't create the field if it doesn't already exist
+        const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array<Document>())
+        value.push(doc);
+    }
+
+    @action
+    removeDocument = (doc: Document): boolean => {
+        //TODO This won't create the field if it doesn't already exist
+        const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array<Document>())
+        let index = value.indexOf(doc);
+        if (index !== -1) {
+            value.splice(index, 1)
+
+            SelectionManager.DeselectAll()
+            ContextMenu.Instance.clearItems()
+            return true;
+        }
+        return false
+    }
+
+    get collectionViewType(): CollectionViewType {
+        let Document = this.props.Document;
+        let viewField = Document.GetT(KeyStore.ViewType, NumberField);
+        if (viewField === "<Waiting>") {
+            return CollectionViewType.Invalid;
+        } else if (viewField) {
+            return viewField.Data;
+        } else {
+            return CollectionViewType.Freeform;
+        }
+    }
+
+    set collectionViewType(type: CollectionViewType) {
+        let Document = this.props.Document;
+        Document.SetData(KeyStore.ViewType, type, NumberField);
+    }
+
+    render() {
+        let viewType = this.collectionViewType;
+        switch (viewType) {
+            case CollectionViewType.Freeform:
+                return (<CollectionFreeFormView {...this.props}
+                    addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active}
+                    CollectionView={this} />)
+            case CollectionViewType.Schema:
+                return (<CollectionSchemaView {...this.props}
+                    addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active}
+                    CollectionView={this} />)
+            case CollectionViewType.Docking:
+                return (<CollectionDockingView {...this.props}
+                    addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active}
+                    CollectionView={this} />)
+            default:
+                return <div></div>
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
deleted file mode 100644
index 0acc890d8..000000000
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { action, computed } from "mobx";
-import { observer } from "mobx-react";
-import { Document } from "../../../fields/Document";
-import { Key } from "../../../fields/Key";
-import { ListField } from "../../../fields/ListField";
-import { SelectionManager } from "../../util/SelectionManager";
-import { ContextMenu } from "../ContextMenu";
-import React = require("react");
-import { Transform } from "../../util/Transform";
-
-
-export interface CollectionViewProps {
-    fieldKey: Key;
-    Document: Document;
-    ScreenToLocalTransform: () => Transform;
-    isSelected: () => boolean;
-    isTopMost: boolean;
-    select: (ctrlPressed: boolean) => void;
-    BackgroundView?: () => JSX.Element;
-}
-
-export const COLLECTION_BORDER_WIDTH = 2;
-
-@observer
-export class CollectionViewBase extends React.Component<CollectionViewProps> {
-
-    public static LayoutString(collectionType: string, fieldKey: string = "DataKey") {
-        return `<${collectionType} Document={Document}
-                    ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} isSelected={isSelected} select={select}
-                    isTopMost={isTopMost} BackgroundView={BackgroundView} />`;
-    }
-    @computed
-    public get active(): boolean {
-        var isSelected = this.props.isSelected();
-        var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this);
-        var topMost = this.props.isTopMost;
-        return isSelected || childSelected || topMost;
-    }
-    @action
-    addDocument = (doc: Document): void => {
-        //TODO This won't create the field if it doesn't already exist
-        const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array<Document>())
-        value.push(doc);
-    }
-
-    @action
-    removeDocument = (doc: Document): boolean => {
-        //TODO This won't create the field if it doesn't already exist
-        const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array<Document>())
-        let index = value.indexOf(doc);
-        if (index !== -1) {
-            value.splice(index, 1)
-
-            SelectionManager.DeselectAll()
-            ContextMenu.Instance.clearItems()
-            return true;
-        }
-        return false
-    }
-
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7cf00a116..2114a5697 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -9,7 +9,7 @@ import { Utils } from "../../../Utils";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionFreeFormView } from "../collections/CollectionFreeFormView";
 import { CollectionSchemaView } from "../collections/CollectionSchemaView";
-import { CollectionViewBase, COLLECTION_BORDER_WIDTH } from "../collections/CollectionViewBase";
+import { COLLECTION_BORDER_WIDTH, CollectionView, CollectionViewType } from "../collections/CollectionView";
 import { FormattedTextBox } from "../nodes/FormattedTextBox";
 import { ImageBox } from "../nodes/ImageBox";
 import "./DocumentView.scss";
@@ -22,7 +22,7 @@ import { TextField } from "../../../fields/TextField";
 const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this?
 
 export interface DocumentViewProps {
-    ContainingCollectionView: Opt<CollectionViewBase>;
+    ContainingCollectionView: Opt<CollectionView>;
 
     Document: Document;
     AddDocument?: (doc: Document) => void;
@@ -114,7 +114,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @computed
     get active(): boolean {
         return SelectionManager.IsSelected(this) || !this.props.ContainingCollectionView ||
-            this.props.ContainingCollectionView.active;
+            this.props.ContainingCollectionView.active();
     }
 
     private _contextMenuCanOpen = false;
@@ -233,6 +233,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked })
             ContextMenu.Instance.addItem({ description: "Open Right", event: this.openRight })
             ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked })
+            ContextMenu.Instance.addItem({ description: "Freeform", event: () => { this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform) } })
+            ContextMenu.Instance.addItem({ description: "Schema", event: () => { this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema) } })
+            ContextMenu.Instance.addItem({ description: "Docking", event: () => { this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) } })
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
             SelectionManager.SelectDoc(this, e.ctrlKey);
         }
@@ -275,7 +278,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         let backgroundLayout = this.backgroundLayout;
         if (backgroundLayout) {
             let backgroundView = () => (<JsxParser
-                components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView }}
+                components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView }}
                 bindings={documentBindings}
                 jsx={this.backgroundLayout}
                 showWarnings={true}
@@ -294,7 +297,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                 onContextMenu={this.onContextMenu}
                 onPointerDown={this.onPointerDown} >
                 <JsxParser
-                    components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView }}
+                    components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView }}
                     bindings={documentBindings}
                     jsx={this.layout}
                     showWarnings={true}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 918acff4c..821172d71 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -20,12 +20,13 @@ export interface FieldViewProps {
     fieldKey: Key;
     doc: Document;
     isSelected: () => boolean;
+    select: () => void;
     isTopMost: boolean;
 }
 
 @observer
 export class FieldView extends React.Component<FieldViewProps> {
-    public static LayoutString(fieldType: { name: string }) { return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} isSelected={isSelected} isTopMost={isTopMost} />`; }
+    public static LayoutString(fieldType: { name: string }) { return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} isSelected={isSelected} select={select} isTopMost={isTopMost} />`; }
     @computed
     get field(): FieldValue<Field> {
         const { doc, fieldKey } = this.props;
diff --git a/src/fields/BasicField.ts b/src/fields/BasicField.ts
index 91977b243..a92c4a236 100644
--- a/src/fields/BasicField.ts
+++ b/src/fields/BasicField.ts
@@ -32,7 +32,7 @@ export abstract class BasicField<T> extends Field {
             return;
         }
         let oldValue = this.data;
-        this.data = value;
+        this.setData(value);
         UndoManager.AddEvent({
             undo: () => this.Data = oldValue,
             redo: () => this.Data = value
@@ -40,6 +40,10 @@ export abstract class BasicField<T> extends Field {
         Server.UpdateField(this);
     }
 
+    protected setData(value: T) {
+        this.data = value;
+    }
+
     @action
     TrySetValue(value: any): boolean {
         if (typeof value == typeof this.data) {
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 6667485b6..ff13732b3 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -90,6 +90,15 @@ export class Document extends Field {
         return field;
     }
 
+    GetAsync(key: Key, callback: (field: Field) => void): boolean {
+        //This currently doesn't deal with prototypes
+        if (this._proxies.has(key.Id)) {
+            Server.GetDocumentField(this, key, callback);
+            return true;
+        }
+        return false;
+    }
+
     GetT<T extends Field = Field>(key: Key, ctor: { new(...args: any[]): T }, ignoreProto: boolean = false): FieldValue<T> {
         var getfield = this.Get(key, ignoreProto);
         if (getfield != FieldWaiting) {
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 7056886aa..6d6c6a546 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -16,6 +16,7 @@ export namespace KeyStore {
     export const ZIndex = new Key("ZIndex");
     export const Data = new Key("Data");
     export const Annotations = new Key("Annotations");
+    export const ViewType = new Key("ViewType");
     export const Layout = new Key("Layout");
     export const BackgroundLayout = new Key("BackgroundLayout");
     export const LayoutKeys = new Key("LayoutKeys");
diff --git a/src/fields/ListField.ts b/src/fields/ListField.ts
index 75c2eb343..700600804 100644
--- a/src/fields/ListField.ts
+++ b/src/fields/ListField.ts
@@ -1,4 +1,4 @@
-import { action, IArrayChange, IArraySplice, IObservableArray, observe } from "mobx";
+import { action, IArrayChange, IArraySplice, IObservableArray, observe, observable, Lambda } from "mobx";
 import { Server } from "../client/Server";
 import { UndoManager } from "../client/util/UndoManager";
 import { Types } from "../server/Message";
@@ -13,7 +13,12 @@ export class ListField<T extends Field> extends BasicField<T[]> {
         if (save) {
             Server.UpdateField(this);
         }
-        observe(this.Data as IObservableArray<T>, (change: IArrayChange<T> | IArraySplice<T>) => {
+        this.observeList();
+    }
+
+    private observeDisposer: Lambda | undefined;
+    private observeList(): void {
+        this.observeDisposer = observe(this.Data as IObservableArray<T>, (change: IArrayChange<T> | IArraySplice<T>) => {
             this.updateProxies()
             if (change.type == "splice") {
                 UndoManager.AddEvent({
@@ -27,7 +32,15 @@ export class ListField<T extends Field> extends BasicField<T[]> {
                 })
             }
             Server.UpdateField(this);
-        })
+        });
+    }
+
+    protected setData(value: T[]) {
+        if (this.observeDisposer) {
+            this.observeDisposer()
+        }
+        this.data = observable(value);
+        this.observeList();
     }
 
     private updateProxies() {
-- 
cgit v1.2.3-70-g09d2


From 5d6d6e00e8fbff1f4475235b1912efcee85eb91e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 22 Feb 2019 21:39:40 -0500
Subject: moved backgroundLayout to CollectionFreeForm

---
 src/client/views/DocumentDecorations.tsx           |  2 +-
 .../views/collections/CollectionFreeFormView.tsx   | 58 ++++++++++++++--------
 .../views/collections/CollectionSchemaView.tsx     |  3 +-
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../views/collections/CollectionViewBase.tsx       |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 41 ++++-----------
 src/client/views/nodes/FieldView.tsx               |  3 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  1 -
 8 files changed, 55 insertions(+), 57 deletions(-)

(limited to 'src/client/views/nodes/FieldView.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index efd50e49e..09df8cdaa 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -101,7 +101,7 @@ export class DocumentDecorations extends React.Component {
         }
 
         SelectionManager.SelectedDocuments().forEach(element => {
-            const rect = element.screenRect;
+            const rect = element.screenRect();
             if (rect.width !== 0) {
                 let doc = element.props.Document;
                 let width = doc.GetOrCreate(KeyStore.Width, NumberField);
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 43bc24a12..565402046 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -1,18 +1,25 @@
+import { action, computed } from "mobx";
 import { observer } from "mobx-react";
-import React = require("react");
-import { action, computed, trace } from "mobx";
-import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
-import { DragManager } from "../../util/DragManager";
-import "./CollectionFreeFormView.scss";
-import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
-import { KeyStore } from "../../../fields/KeyStore";
 import { Document } from "../../../fields/Document";
-import { ListField } from "../../../fields/ListField";
 import { FieldWaiting } from "../../../fields/Field";
+import { KeyStore } from "../../../fields/KeyStore";
+import { ListField } from "../../../fields/ListField";
+import { TextField } from "../../../fields/TextField";
+import { DragManager } from "../../util/DragManager";
 import { Transform } from "../../util/Transform";
-import { DocumentView } from "../nodes/DocumentView";
 import { undoBatch } from "../../util/UndoManager";
-import { CollectionViewBase, SubCollectionViewProps } from "./CollectionViewBase";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { CollectionSchemaView } from "../collections/CollectionSchemaView";
+import { CollectionView } from "../collections/CollectionView";
+import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
+import { DocumentView } from "../nodes/DocumentView";
+import { FormattedTextBox } from "../nodes/FormattedTextBox";
+import { ImageBox } from "../nodes/ImageBox";
+import "./CollectionFreeFormView.scss";
+import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
+import { CollectionViewBase } from "./CollectionViewBase";
+import React = require("react");
+const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this?
 
 @observer
 export class CollectionFreeFormView extends CollectionViewBase {
@@ -145,16 +152,13 @@ export class CollectionFreeFormView extends CollectionViewBase {
         });
     }
 
-    getTransform = (): Transform => {
-        return this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).transform(this.getLocalTransform())
-    }
 
-    getLocalTransform = (): Transform => {
-        return Transform.Identity.translate(-this.panX, -this.panY).scale(1 / this.scale);
+    @computed get backgroundLayout(): string | undefined {
+        let field = this.props.Document.GetT(KeyStore.BackgroundLayout, TextField);
+        if (field && field !== "<Waiting>") {
+            return field.Data;
+        }
     }
-
-    noScaling = () => 1;
-
     @computed
     get views() {
         const { fieldKey, Document } = this.props;
@@ -175,6 +179,21 @@ export class CollectionFreeFormView extends CollectionViewBase {
         return null;
     }
 
+    @computed
+    get backgroundView() {
+        return !this.backgroundLayout ? (null) :
+            (<JsxParser
+                components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView }}
+                bindings={this.props.bindings}
+                jsx={this.backgroundLayout}
+                showWarnings={true}
+                onError={(test: any) => console.log(test)}
+            />);
+    }
+    getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).transform(this.getLocalTransform())
+    getLocalTransform = (): Transform => Transform.Identity.translate(-this.panX, -this.panY).scale(1 / this.scale);
+    noScaling = () => 1;
+
     render() {
         const panx: number = this.props.Document.GetNumber(KeyStore.PanX, 0);
         const pany: number = this.props.Document.GetNumber(KeyStore.PanY, 0);
@@ -190,8 +209,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
                 <div className="collectionfreeformview"
                     style={{ width: "100%", transformOrigin: "left top", transform: ` translate(${panx}px, ${pany}px) scale(${this.zoomScaling}, ${this.zoomScaling})` }}
                     ref={this._canvasRef}>
-
-                    {this.props.BackgroundView ? this.props.BackgroundView() : null}
+                    {this.backgroundView}
                     {this.views}
                 </div>
             </div>
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index ca47f6998..d2db93120 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -34,7 +34,8 @@ export class CollectionSchemaView extends CollectionViewBase {
             fieldKey: rowProps.value[1],
             isSelected: () => false,
             select: () => { },
-            isTopMost: false
+            isTopMost: false,
+            bindings: {}
         }
         let contents = (
             <FieldView {...props} />
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index ff1803ec3..90080ab43 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -28,7 +28,7 @@ export class CollectionView extends React.Component<CollectionViewProps> {
 
     public static LayoutString(fieldKey: string = "DataKey") {
         return `<CollectionView Document={Document}
-                    ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} isSelected={isSelected} select={select}
+                    ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} isSelected={isSelected} select={select} bindings={bindings}
                     isTopMost={isTopMost} BackgroundView={BackgroundView} />`;
     }
     public active = () => {
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 06de56383..da7f71163 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -19,7 +19,7 @@ export interface CollectionViewProps {
     isSelected: () => boolean;
     isTopMost: boolean;
     select: (ctrlPressed: boolean) => void;
-    BackgroundView?: () => JSX.Element;
+    bindings: any;
 }
 export interface SubCollectionViewProps extends CollectionViewProps {
     active: () => boolean;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d9b1d90d8..a9e211431 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -5,7 +5,6 @@ import { Field, FieldWaiting, Opt } from "../../../fields/Field";
 import { Key } from "../../../fields/Key";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
-import { TextField } from "../../../fields/TextField";
 import { DragManager } from "../../util/DragManager";
 import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
@@ -84,21 +83,13 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     private _downX: number = 0;
     private _downY: number = 0;
 
-    get screenRect(): ClientRect | DOMRect {
-        return (this._mainCont.current) ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
-    }
     @computed get active(): boolean { return SelectionManager.IsSelected(this) || !this.props.ContainingCollectionView || this.props.ContainingCollectionView.active(); }
     @computed get topMost(): boolean { return !this.props.ContainingCollectionView || this.props.ContainingCollectionView.collectionViewType == CollectionViewType.Docking; }
     @computed get layout(): string { return this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>"); }
     @computed get layoutKeys(): Key[] { return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array<Key>()); }
     @computed get layoutFields(): Key[] { return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>()); }
-    @computed get backgroundLayout(): string | undefined {
-        let field = this.props.Document.GetT(KeyStore.BackgroundLayout, TextField);
-        if (field && field !== "<Waiting>") {
-            return field.Data;
-        }
-    }
 
+    screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
 
     onPointerDown = (e: React.PointerEvent): void => {
         this._downX = e.clientX;
@@ -174,6 +165,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
     }
 
+    @action
     onContextMenu = (e: React.MouseEvent): void => {
         e.preventDefault()
         e.stopPropagation();
@@ -230,32 +222,19 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             let field = this.props.Document.Get(key);
             this._documentBindings[key.Name] = field && field != FieldWaiting ? field.GetValue() : field;
         }
-
-        /*
-        tfs:
-        Should this be moved to CollectionFreeformView or another component that renders
-        Document backgrounds (or contents based on a layout key, which could be used here as well)
-         that CollectionFreeformView uses? It seems like a lot for it to be here considering only one view currently uses it...
-         */
-        let backgroundLayout = this.backgroundLayout;
-        if (backgroundLayout) {
-            let backgroundView = () => (<JsxParser
-                components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView }}
-                bindings={this._documentBindings}
-                jsx={this.backgroundLayout}
-                showWarnings={true}
-                onError={(test: any) => { console.log(test) }}
-            />);
-            this._documentBindings.BackgroundView = backgroundView;
-        }
+        this._documentBindings.bindings = this._documentBindings;
 
         var scaling = this.props.ContentScaling();
         var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
         var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
-        var nodeWidth = nativeWidth > 0 ? nativeWidth.toString() + "px" : "100%";
-        var nodeHeight = nativeHeight > 0 ? nativeHeight.toString() + "px" : "100%";
         return (
-            <div className="documentView-node" ref={this._mainCont} style={{ width: nodeWidth, height: nodeHeight, transformOrigin: "left top", transform: `scale(${scaling},${scaling})` }}
+            <div className="documentView-node" ref={this._mainCont}
+                style={{
+                    width: nativeWidth > 0 ? nativeWidth.toString() + "px" : "100%",
+                    height: nativeHeight > 0 ? nativeHeight.toString() + "px" : "100%",
+                    transformOrigin: "left top",
+                    transform: `scale(${scaling},${scaling})`
+                }}
                 onContextMenu={this.onContextMenu}
                 onPointerDown={this.onPointerDown} >
                 {this.mainContent}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 821172d71..97d3f2a85 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -22,11 +22,12 @@ export interface FieldViewProps {
     isSelected: () => boolean;
     select: () => void;
     isTopMost: boolean;
+    bindings: any;
 }
 
 @observer
 export class FieldView extends React.Component<FieldViewProps> {
-    public static LayoutString(fieldType: { name: string }) { return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} isSelected={isSelected} select={select} isTopMost={isTopMost} />`; }
+    public static LayoutString(fieldType: { name: string }) { return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={DataKey} isSelected={isSelected} select={select} isTopMost={isTopMost} />`; }
     @computed
     get field(): FieldValue<Field> {
         const { doc, fieldKey } = this.props;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index b17650d06..16728d471 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -6,7 +6,6 @@ import { schema } from "prosemirror-schema-basic";
 import { EditorState, Transaction } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { Opt, FieldWaiting, FieldValue } from "../../../fields/Field";
-import { SelectionManager } from "../../util/SelectionManager";
 import "./FormattedTextBox.scss";
 import React = require("react")
 import { RichTextField } from "../../../fields/RichTextField";
-- 
cgit v1.2.3-70-g09d2


From f4ec29ad3143cf13bf7fa05985926242cabb6b1a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 23 Feb 2019 02:34:58 -0500
Subject: css hacking to fix layout issues related to making a captioned image
 style

---
 src/client/documents/Documents.ts                    | 20 ++++++++++++++++----
 .../views/collections/CollectionFreeFormView.scss    |  2 ++
 .../views/collections/CollectionFreeFormView.tsx     |  5 +++--
 src/client/views/nodes/FieldView.tsx                 |  3 ++-
 src/client/views/nodes/FormattedTextBox.scss         |  4 ++--
 src/client/views/nodes/FormattedTextBox.tsx          |  5 ++---
 src/client/views/nodes/ImageBox.scss                 |  3 ++-
 src/fields/KeyStore.ts                               |  1 +
 8 files changed, 30 insertions(+), 13 deletions(-)

(limited to 'src/client/views/nodes/FieldView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 156a09316..6ec5aa711 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -5,13 +5,10 @@ import { TextField } from "../../fields/TextField";
 import { NumberField } from "../../fields/NumberField";
 import { ListField } from "../../fields/ListField";
 import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
-import { CollectionDockingView } from "../views/collections/CollectionDockingView";
-import { CollectionSchemaView } from "../views/collections/CollectionSchemaView";
 import { ImageField } from "../../fields/ImageField";
 import { ImageBox } from "../views/nodes/ImageBox";
-import { CollectionFreeFormView } from "../views/collections/CollectionFreeFormView";
-import { FieldId } from "../../fields/Field";
 import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
+import { FieldView } from "../views/nodes/FieldView";
 
 export interface DocumentOptions {
     x?: number;
@@ -102,12 +99,27 @@ export namespace Documents {
             return imageProto;
         }
         return imageProto;
+
     }
 
+    // example of custom display string for an image that shows a caption.
+    function CaptionLayoutString() {
+        return `<div style="position:absolute; height:100%">
+            <div style="position:relative; margin:auto; width:85%; margin:auto" >`
+            + ImageBox.LayoutString() +
+            `</div>
+            <div style="position:relative; min-height:15%; overflow:scroll; max-height:15%; text-align:center; ">`
+            + FormattedTextBox.LayoutString("CaptionKey") +
+            `</div> 
+        </div>` };
+
     export function ImageDocument(url: string, options: DocumentOptions = {}): Document {
         let doc = GetImagePrototype().MakeDelegate();
         setupOptions(doc, options);
         doc.Set(KeyStore.Data, new ImageField(new URL(url)));
+        doc.Set(KeyStore.Caption, new TextField("my caption..."));
+        doc.Set(KeyStore.BackgroundLayout, new TextField(CaptionLayoutString()));
+        doc.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data, KeyStore.Annotations, KeyStore.Caption]));
 
         let annotation = Documents.TextDocument({ title: "hello" });
         doc.Set(KeyStore.Annotations, new ListField([annotation]));
diff --git a/src/client/views/collections/CollectionFreeFormView.scss b/src/client/views/collections/CollectionFreeFormView.scss
index 4cf474f77..6d678528a 100644
--- a/src/client/views/collections/CollectionFreeFormView.scss
+++ b/src/client/views/collections/CollectionFreeFormView.scss
@@ -11,5 +11,7 @@
         position: absolute;
         top: 0;
         left: 0;
+        width:100%;
+        height: 100%
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index c12a82617..6d2662105 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -34,7 +34,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
     @computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0) }
     @computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0) }
     @computed get scale(): number { return this.props.Document.GetNumber(KeyStore.Scale, 1); }
-    @computed get isAnnotationOverlay() { return this.props.fieldKey == KeyStore.Annotations; }
+    @computed get isAnnotationOverlay() { return this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's?
     @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
     @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
     @computed get zoomScaling() { return this.props.Document.GetNumber(KeyStore.Scale, 1); }
@@ -91,6 +91,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
             let [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
 
             this.SetPan(x + dx, y + dy);
+            console.log("px = " + x + " " + y)
         }
         this._lastX = e.pageX;
         this._lastY = e.pageY;
@@ -209,7 +210,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
                 style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px`, }}
                 ref={this.createDropTarget}>
                 <div className="collectionfreeformview"
-                    style={{ width: "100%", transformOrigin: "left top", transform: ` translate(${panx}px, ${pany}px) scale(${this.zoomScaling}, ${this.zoomScaling})` }}
+                    style={{ transformOrigin: "left top", transform: ` translate(${panx}px, ${pany}px) scale(${this.zoomScaling}, ${this.zoomScaling})` }}
                     ref={this._canvasRef}>
                     {this.backgroundView}
                     {this.views}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 97d3f2a85..9dda08d8d 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -27,7 +27,8 @@ export interface FieldViewProps {
 
 @observer
 export class FieldView extends React.Component<FieldViewProps> {
-    public static LayoutString(fieldType: { name: string }) { return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={DataKey} isSelected={isSelected} select={select} isTopMost={isTopMost} />`; }
+    public static LayoutString(fieldType: { name: string }, fieldStr: string = "DataKey") { return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={${fieldStr}} isSelected={isSelected} select={select} isTopMost={isTopMost} />`; }
+
     @computed
     get field(): FieldValue<Field> {
         const { doc, fieldKey } = this.props;
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 5139d5d6b..872a2138b 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -1,7 +1,7 @@
 .ProseMirror {
-    margin-top: -1em;
     width: 100%;
-    height: 100%;
+    height: auto;
+    min-height: 100%
 }
 
 .ProseMirror:focus {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 16728d471..c0969a8c3 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -10,8 +10,6 @@ import "./FormattedTextBox.scss";
 import React = require("react")
 import { RichTextField } from "../../../fields/RichTextField";
 import { FieldViewProps, FieldView } from "./FieldView";
-import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
-import { observer } from "mobx-react";
 
 
 // FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document
@@ -32,7 +30,7 @@ import { observer } from "mobx-react";
 //]
 export class FormattedTextBox extends React.Component<FieldViewProps> {
 
-    public static LayoutString() { return FieldView.LayoutString(FormattedTextBox) }
+    public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(FormattedTextBox, fieldStr) }
     private _ref: React.RefObject<HTMLDivElement>;
     private _editorView: Opt<EditorView>;
     private _reactionDisposer: Opt<IReactionDisposer>;
@@ -118,6 +116,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
             style={{
                 color: "initial",
                 whiteSpace: "initial",
+                height: "auto"
             }}
             onPointerDown={this.onPointerDown}
             ref={this._ref} />)
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 36f5e0fe0..5b15b3329 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -1,7 +1,8 @@
 
 .imageBox-cont {
     padding: 0vw;
-    position: absolute;
+    position: relative;
+    text-align: center;
     width: 100%;
     max-width: 100%;
     max-height: 100%
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 6d6c6a546..42e3f6b58 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -22,5 +22,6 @@ export namespace KeyStore {
     export const LayoutKeys = new Key("LayoutKeys");
     export const LayoutFields = new Key("LayoutFields");
     export const ColumnsKey = new Key("SchemaColumns");
+    export const Caption = new Key("Caption");
     export const ActiveFrame = new Key("ActiveFrame");
 }
-- 
cgit v1.2.3-70-g09d2


From 6c451fa68db11c80f452ca7c85242ad98d867ab0 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sat, 23 Feb 2019 04:49:48 -0500
Subject: Added web clippings on drag and drop

---
 src/client/documents/Documents.ts                  | 24 +++++++++++++++++++++
 .../views/collections/CollectionFreeFormView.tsx   |  3 ++-
 .../views/collections/CollectionViewBase.tsx       |  9 ++++++++
 src/client/views/nodes/DocumentView.tsx            |  3 ++-
 src/client/views/nodes/FieldView.tsx               |  4 ++++
 src/client/views/nodes/WebView.tsx                 | 22 +++++++++++++++++++
 src/fields/HtmlField.ts                            | 25 ++++++++++++++++++++++
 src/fields/KeyStore.ts                             |  1 +
 src/fields/TextField.ts                            |  2 +-
 src/server/Message.ts                              |  2 +-
 src/server/ServerUtil.ts                           |  6 +++++-
 11 files changed, 96 insertions(+), 5 deletions(-)
 create mode 100644 src/client/views/nodes/WebView.tsx
 create mode 100644 src/fields/HtmlField.ts

(limited to 'src/client/views/nodes/FieldView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 6ec5aa711..d8e1db0b0 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -9,6 +9,8 @@ import { ImageField } from "../../fields/ImageField";
 import { ImageBox } from "../views/nodes/ImageBox";
 import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
 import { FieldView } from "../views/nodes/FieldView";
+import { HtmlField } from "../../fields/HtmlField";
+import { WebView } from "../views/nodes/WebView";
 
 export interface DocumentOptions {
     x?: number;
@@ -79,6 +81,28 @@ export namespace Documents {
         return doc;
     }
 
+    let htmlProto: Document;
+    const htmlProtoId = "htmlProto";
+    function GetHtmlPrototype(): Document {
+        if (!htmlProto) {
+            htmlProto = new Document(htmlProtoId);
+            htmlProto.Set(KeyStore.X, new NumberField(0));
+            htmlProto.Set(KeyStore.Y, new NumberField(0));
+            htmlProto.Set(KeyStore.Width, new NumberField(300));
+            htmlProto.Set(KeyStore.Height, new NumberField(150));
+            htmlProto.Set(KeyStore.Layout, new TextField(WebView.LayoutString()));
+            htmlProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data]));
+        }
+        return htmlProto;
+    }
+
+    export function HtmlDocument(html: string, options: DocumentOptions = {}): Document {
+        let doc = GetHtmlPrototype().MakeDelegate();
+        setupOptions(doc, options);
+        doc.Set(KeyStore.Data, new HtmlField(html));
+        return doc;
+    }
+
     let imageProto: Document;
     const imageProtoId = "imageProto";
     function GetImagePrototype(): Document {
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 4799eda97..b031c35a7 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -13,6 +13,7 @@ import { CollectionSchemaView } from "../collections/CollectionSchemaView";
 import { CollectionView } from "../collections/CollectionView";
 import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
 import { DocumentView } from "../nodes/DocumentView";
+import { WebView } from "../nodes/WebView";
 import { FormattedTextBox } from "../nodes/FormattedTextBox";
 import { ImageBox } from "../nodes/ImageBox";
 import "./CollectionFreeFormView.scss";
@@ -185,7 +186,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
     get backgroundView() {
         return !this.backgroundLayout ? (null) :
             (<JsxParser
-                components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView }}
+                components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebView }}
                 bindings={this.props.bindings}
                 jsx={this.backgroundLayout}
                 showWarnings={true}
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 4bf0d074a..7e269caf1 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -58,6 +58,15 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
         e.preventDefault()
         let that = this;
 
+        let html = e.dataTransfer.getData("text/html");
+        let text = e.dataTransfer.getData("text/plain");
+        if (html) {
+            let htmlDoc = Documents.HtmlDocument(html, { ...options });
+            htmlDoc.SetText(KeyStore.DocumentText, text);
+            this.props.addDocument(htmlDoc);
+            return;
+        }
+
         for (let i = 0; i < e.dataTransfer.items.length; i++) {
             let item = e.dataTransfer.items[i];
             if (item.kind === "string" && item.type.indexOf("uri") != -1) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a9e211431..ad1328e5d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -12,6 +12,7 @@ import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionFreeFormView } from "../collections/CollectionFreeFormView";
 import { CollectionSchemaView } from "../collections/CollectionSchemaView";
 import { CollectionView, CollectionViewType } from "../collections/CollectionView";
+import { WebView } from "./WebView";
 import { ContextMenu } from "../ContextMenu";
 import { FormattedTextBox } from "../nodes/FormattedTextBox";
 import { ImageBox } from "../nodes/ImageBox";
@@ -196,7 +197,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     @computed get mainContent() {
         return <JsxParser
-            components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView }}
+            components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebView }}
             bindings={this._documentBindings}
             jsx={this.layout}
             showWarnings={true}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 9dda08d8d..1a9d325db 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -10,6 +10,8 @@ import { ImageField } from "../../../fields/ImageField";
 import { Key } from "../../../fields/Key";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { ImageBox } from "./ImageBox";
+import { HtmlField } from "../../../fields/HtmlField";
+import { WebView } from "./WebView";
 
 //
 // these properties get assigned through the render() method of the DocumentView when it creates this node.
@@ -50,6 +52,8 @@ export class FieldView extends React.Component<FieldViewProps> {
         }
         else if (field instanceof NumberField) {
             return <p>{field.Data}</p>
+        } else if (field instanceof HtmlField) {
+            return <WebView {...this.props} />
         } else if (field != FieldWaiting) {
             return <p>{field.GetValue}</p>
         } else
diff --git a/src/client/views/nodes/WebView.tsx b/src/client/views/nodes/WebView.tsx
new file mode 100644
index 000000000..717aa8bf5
--- /dev/null
+++ b/src/client/views/nodes/WebView.tsx
@@ -0,0 +1,22 @@
+import { FieldViewProps, FieldView } from "./FieldView";
+import { computed } from "mobx";
+import { observer } from "mobx-react";
+import { KeyStore } from "../../../fields/KeyStore";
+import React = require('react')
+import { TextField } from "../../../fields/TextField";
+import { HtmlField } from "../../../fields/HtmlField";
+import { RichTextField } from "../../../fields/RichTextField";
+
+@observer
+export class WebView extends React.Component<FieldViewProps> {
+    public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(WebView, fieldStr) }
+
+    @computed
+    get html(): string {
+        return this.props.doc.GetData(KeyStore.Data, HtmlField, "" as string);
+    }
+
+    render() {
+        return <span dangerouslySetInnerHTML={{ __html: this.html }}></span>
+    }
+}
\ No newline at end of file
diff --git a/src/fields/HtmlField.ts b/src/fields/HtmlField.ts
new file mode 100644
index 000000000..a07326095
--- /dev/null
+++ b/src/fields/HtmlField.ts
@@ -0,0 +1,25 @@
+import { BasicField } from "./BasicField";
+import { Types } from "../server/Message";
+import { FieldId } from "./Field";
+
+export class HtmlField extends BasicField<string> {
+    constructor(data: string = "<html></html>", id?: FieldId, save: boolean = true) {
+        super(data, save, id);
+    }
+
+    ToScriptString(): string {
+        return `new HtmlField("${this.Data}")`;
+    }
+
+    Copy() {
+        return new HtmlField(this.Data);
+    }
+
+    ToJson(): { _id: string; type: Types; data: any; } {
+        return {
+            type: Types.Html,
+            data: this.Data,
+            _id: this.Id,
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 42e3f6b58..290fa2be7 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -24,4 +24,5 @@ export namespace KeyStore {
     export const ColumnsKey = new Key("SchemaColumns");
     export const Caption = new Key("Caption");
     export const ActiveFrame = new Key("ActiveFrame");
+    export const DocumentText = new Key("DocumentText");
 }
diff --git a/src/fields/TextField.ts b/src/fields/TextField.ts
index ad96ab6d9..71d8ea310 100644
--- a/src/fields/TextField.ts
+++ b/src/fields/TextField.ts
@@ -22,4 +22,4 @@ export class TextField extends BasicField<string> {
             _id: this.Id
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/server/Message.ts b/src/server/Message.ts
index 7f3190f7f..80fc9a80d 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -45,7 +45,7 @@ export class GetFieldArgs {
 }
 
 export enum Types {
-    Number, List, Key, Image, Document, Text, RichText, DocumentReference
+    Number, List, Key, Image, Document, Text, RichText, DocumentReference, Html
 }
 
 export class DocumentTransfer implements Transferable {
diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts
index 46c409ec4..08e72fdae 100644
--- a/src/server/ServerUtil.ts
+++ b/src/server/ServerUtil.ts
@@ -9,6 +9,7 @@ import { Document } from './../fields/Document';
 import { Server } from './../client/Server';
 import { Types } from './Message';
 import { Utils } from '../Utils';
+import { HtmlField } from '../fields/HtmlField';
 
 export class ServerUtils {
     public static FromJson(json: any): Field {
@@ -27,6 +28,8 @@ export class ServerUtils {
                 return new NumberField(data, id, false)
             case Types.Text:
                 return new TextField(data, id, false)
+            case Types.Html:
+                return new HtmlField(data, id, false)
             case Types.RichText:
                 return new RichTextField(data, id, false)
             case Types.Key:
@@ -42,7 +45,8 @@ export class ServerUtils {
                     doc._proxies.set(element[0], element[1]);
                 });
                 return doc
+            default:
+                throw Error("Error, unrecognized field type received from server. If you just created a new field type, be sure to add it here");
         }
-        return new TextField(data, id)
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2

