From 1eb965a5d9c8aaebf1970bc645edecfb7017b601 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sat, 20 Apr 2019 03:29:35 -0400
Subject: Made the switch in a couple more classes

---
 src/client/views/nodes/FieldView.tsx | 45 +++++++++++++++---------------------
 1 file changed, 18 insertions(+), 27 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 93e385821..d8de5afec 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,30 +1,21 @@
 import React = require("react");
 import { observer } from "mobx-react";
 import { computed } from "mobx";
-import { Field, FieldWaiting, FieldValue, Opt } from "../../../fields/Field";
-import { Document } from "../../../fields/Document";
-import { TextField } from "../../../fields/TextField";
-import { NumberField } from "../../../fields/NumberField";
-import { RichTextField } from "../../../fields/RichTextField";
-import { ImageField } from "../../../fields/ImageField";
-import { VideoField } from "../../../fields/VideoField";
-import { Key } from "../../../fields/Key";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { ImageBox } from "./ImageBox";
 import { WebBox } from "./WebBox";
 import { VideoBox } from "./VideoBox";
 import { AudioBox } from "./AudioBox";
-import { AudioField } from "../../../fields/AudioField";
-import { ListField } from "../../../fields/ListField";
 import { DocumentContentsView } from "./DocumentContentsView";
 import { Transform } from "../../util/Transform";
-import { KeyStore } from "../../../fields/KeyStore";
-import { returnFalse, emptyDocFunction, emptyFunction, returnOne } from "../../../Utils";
+import { returnFalse, emptyFunction, returnOne } from "../../../Utils";
 import { CollectionView } from "../collections/CollectionView";
 import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { IconField } from "../../../fields/IconFIeld";
 import { IconBox } from "./IconBox";
+import { Opt, Doc, Field, FieldValue, FieldWaiting } from "../../../new_fields/Doc";
+import { List } from "../../../new_fields/List";
 
 
 //
@@ -33,9 +24,9 @@ import { IconBox } from "./IconBox";
 // See the LayoutString method on each field view :   ImageBox, FormattedTextBox, etc. 
 //
 export interface FieldViewProps {
-    fieldKey: Key;
+    fieldKey: string;
     ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
-    Document: Document;
+    Document: Doc;
     isSelected: () => boolean;
     select: (isCtrlPressed: boolean) => void;
     isTopMost: boolean;
@@ -56,17 +47,17 @@ export class FieldView extends React.Component<FieldViewProps> {
     }
 
     @computed
-    get field(): FieldValue<Field> {
-        const { Document: doc, fieldKey } = this.props;
-        return doc.Get(fieldKey);
+    get field(): FieldValue {
+        const { Document, fieldKey } = this.props;
+        return Document[fieldKey];
     }
     render() {
         const field = this.field;
         if (!field) {
             return <p>{'<null>'}</p>;
         }
-        if (field instanceof TextField) {
-            return <p>{field.Data}</p>;
+        if (typeof field === "string") {
+            return <p>{field}</p>;
         }
         else if (field instanceof RichTextField) {
             return <FormattedTextBox {...this.props} />;
@@ -83,7 +74,7 @@ export class FieldView extends React.Component<FieldViewProps> {
         else if (field instanceof AudioField) {
             return <AudioBox {...this.props} />;
         }
-        else if (field instanceof Document) {
+        else if (field instanceof Doc) {
             return (
                 <DocumentContentsView Document={field}
                     addDocument={undefined}
@@ -94,29 +85,29 @@ export class FieldView extends React.Component<FieldViewProps> {
                     PanelHeight={() => 100}
                     isTopMost={true} //TODO Why is this top most?
                     selectOnLoad={false}
-                    focus={emptyDocFunction}
+                    focus={emptyFunction}
                     isSelected={returnFalse}
                     select={returnFalse}
-                    layoutKey={KeyStore.Layout}
+                    layoutKey={"layout"}
                     ContainingCollectionView={this.props.ContainingCollectionView}
                     parentActive={this.props.active}
                     whenActiveChanged={this.props.whenActiveChanged} />
             );
         }
-        else if (field instanceof ListField) {
+        else if (field instanceof List) {
             return (<div>
-                {(field as ListField<Field>).Data.map(f => f instanceof Document ? f.Title : f.GetValue().toString()).join(", ")}
+                {field.map(f => f instanceof Doc ? f.title : f.toString()).join(", ")}
             </div>);
         }
         // bcz: this belongs here, but it doesn't render well so taking it out for now
         // else if (field instanceof HtmlField) {
         //     return <WebBox {...this.props} />
         // }
-        else if (field instanceof NumberField) {
-            return <p>{field.Data}</p>;
+        else if (typeof field === "number") {
+            return <p>{field}</p>;
         }
         else if (field !== FieldWaiting) {
-            return <p>{JSON.stringify(field.GetValue())}</p>;
+            return <p>{JSON.stringify(field)}</p>;
         }
         else {
             return <p> {"Waiting for server..."} </p>;
-- 
cgit v1.2.3-70-g09d2


From ab8b4ffd7f3b89600a71d3167490c40a59be433e Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sun, 21 Apr 2019 01:46:02 -0400
Subject: More changes/fixes

---
 src/client/views/collections/CollectionSubView.tsx | 354 +++++++++++----------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  21 +-
 src/client/views/nodes/FieldView.tsx               |   8 +-
 src/new_fields/Schema.ts                           |  15 +-
 src/new_fields/Types.ts                            |   2 +-
 5 files changed, 210 insertions(+), 190 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5c3b2e586..3add288fd 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -18,11 +18,13 @@ import * as rp from 'request-promise';
 import { CollectionView } from "./CollectionView";
 import { CollectionPDFView } from "./CollectionPDFView";
 import { CollectionVideoView } from "./CollectionVideoView";
+import { Doc } from "../../../new_fields/Doc";
+import { DocComponent } from "../DocComponent";
 
 export interface CollectionViewProps extends FieldViewProps {
-    addDocument: (document: Document, allowDuplicates?: boolean) => boolean;
-    removeDocument: (document: Document) => boolean;
-    moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
+    addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
+    removeDocument: (document: Doc) => boolean;
+    moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     PanelWidth: () => number;
     PanelHeight: () => number;
 }
@@ -33,198 +35,202 @@ export interface SubCollectionViewProps extends CollectionViewProps {
 
 export type CursorEntry = TupleField<[string, string], [number, number]>;
 
-export class CollectionSubView extends React.Component<SubCollectionViewProps> {
-    private dropDisposer?: DragManager.DragDropDisposer;
-    protected createDropTarget = (ele: HTMLDivElement) => {
-        if (this.dropDisposer) {
-            this.dropDisposer();
+export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
+    class CollectionSubView extends DocComponent<SubCollectionViewProps, T>(schemaCtor) {
+        private dropDisposer?: DragManager.DragDropDisposer;
+        protected createDropTarget = (ele: HTMLDivElement) => {
+            if (this.dropDisposer) {
+                this.dropDisposer();
+            }
+            if (ele) {
+                this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+            }
         }
-        if (ele) {
-            this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+        protected CreateDropTarget(ele: HTMLDivElement) {
+            this.createDropTarget(ele);
         }
-    }
-    protected CreateDropTarget(ele: HTMLDivElement) {
-        this.createDropTarget(ele);
-    }
 
-    @action
-    protected setCursorPosition(position: [number, number]) {
-        let ind;
-        let doc = this.props.Document;
-        let id = CurrentUserUtils.id;
-        let email = CurrentUserUtils.email;
-        if (id && email) {
-            let textInfo: [string, string] = [id, email];
-            doc.GetTAsync(KeyStore.Prototype, Document).then(proto => {
-                if (!proto) {
-                    return;
-                }
-                proto.GetOrCreateAsync<ListField<CursorEntry>>(KeyStore.Cursors, ListField, action((field: ListField<CursorEntry>) => {
-                    let cursors = field.Data;
-                    if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.Data[0][0] === id)) > -1) {
-                        cursors[ind].Data[1] = position;
-                    } else {
-                        let entry = new TupleField<[string, string], [number, number]>([textInfo, position]);
-                        cursors.push(entry);
+        @action
+        protected setCursorPosition(position: [number, number]) {
+            let ind;
+            let doc = this.props.Document;
+            let id = CurrentUserUtils.id;
+            let email = CurrentUserUtils.email;
+            if (id && email) {
+                let textInfo: [string, string] = [id, email];
+                doc.GetTAsync(KeyStore.Prototype, Document).then(proto => {
+                    if (!proto) {
+                        return;
                     }
-                }));
-            });
+                    proto.GetOrCreateAsync<ListField<CursorEntry>>(KeyStore.Cursors, ListField, action((field: ListField<CursorEntry>) => {
+                        let cursors = field.Data;
+                        if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.Data[0][0] === id)) > -1) {
+                            cursors[ind].Data[1] = position;
+                        } else {
+                            let entry = new TupleField<[string, string], [number, number]>([textInfo, position]);
+                            cursors.push(entry);
+                        }
+                    }));
+                });
+            }
         }
-    }
 
-    @undoBatch
-    @action
-    protected drop(e: Event, de: DragManager.DropEvent): boolean {
-        if (de.data instanceof DragManager.DocumentDragData) {
-            if (de.data.aliasOnDrop || de.data.copyOnDrop) {
-                [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key =>
-                    de.data.draggedDocuments.map((draggedDocument: Document, i: number) =>
-                        draggedDocument.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? de.data.droppedDocuments[i].SetNumber(key, f.Data) : null)));
-            }
-            let added = false;
-            if (de.data.aliasOnDrop || de.data.copyOnDrop) {
-                added = de.data.droppedDocuments.reduce((added: boolean, d) => {
-                    let moved = this.props.addDocument(d);
-                    return moved || added;
-                }, false);
-            } else if (de.data.moveDocument) {
-                const move = de.data.moveDocument;
-                added = de.data.droppedDocuments.reduce((added: boolean, d) => {
-                    let moved = move(d, this.props.Document, this.props.addDocument);
-                    return moved || added;
-                }, false);
-            } else {
-                added = de.data.droppedDocuments.reduce((added: boolean, d) => {
-                    let moved = this.props.addDocument(d);
-                    return moved || added;
-                }, false);
+        @undoBatch
+        @action
+        protected drop(e: Event, de: DragManager.DropEvent): boolean {
+            if (de.data instanceof DragManager.DocumentDragData) {
+                if (de.data.aliasOnDrop || de.data.copyOnDrop) {
+                    [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key =>
+                        de.data.draggedDocuments.map((draggedDocument: Document, i: number) =>
+                            draggedDocument.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? de.data.droppedDocuments[i].SetNumber(key, f.Data) : null)));
+                }
+                let added = false;
+                if (de.data.aliasOnDrop || de.data.copyOnDrop) {
+                    added = de.data.droppedDocuments.reduce((added: boolean, d) => {
+                        let moved = this.props.addDocument(d);
+                        return moved || added;
+                    }, false);
+                } else if (de.data.moveDocument) {
+                    const move = de.data.moveDocument;
+                    added = de.data.droppedDocuments.reduce((added: boolean, d) => {
+                        let moved = move(d, this.props.Document, this.props.addDocument);
+                        return moved || added;
+                    }, false);
+                } else {
+                    added = de.data.droppedDocuments.reduce((added: boolean, d) => {
+                        let moved = this.props.addDocument(d);
+                        return moved || added;
+                    }, false);
+                }
+                e.stopPropagation();
+                return added;
             }
-            e.stopPropagation();
-            return added;
+            return false;
         }
-        return false;
-    }
 
-    protected async getDocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Document>> {
-        let ctor: ((path: string, options: DocumentOptions) => (Document | Promise<Document | undefined>)) | undefined = undefined;
-        if (type.indexOf("image") !== -1) {
-            ctor = Documents.ImageDocument;
-        }
-        if (type.indexOf("video") !== -1) {
-            ctor = Documents.VideoDocument;
-        }
-        if (type.indexOf("audio") !== -1) {
-            ctor = Documents.AudioDocument;
-        }
-        if (type.indexOf("pdf") !== -1) {
-            ctor = Documents.PdfDocument;
-            options.nativeWidth = 1200;
-        }
-        if (type.indexOf("excel") !== -1) {
-            ctor = Documents.DBDocument;
-            options.copyDraggedItems = true;
-        }
-        if (type.indexOf("html") !== -1) {
-            if (path.includes('localhost')) {
-                let s = path.split('/');
-                let id = s[s.length - 1];
-                Server.GetField(id).then(field => {
-                    if (field instanceof Document) {
-                        let alias = field.CreateAlias();
-                        alias.SetNumber(KeyStore.X, options.x || 0);
-                        alias.SetNumber(KeyStore.Y, options.y || 0);
-                        alias.SetNumber(KeyStore.Width, options.width || 300);
-                        alias.SetNumber(KeyStore.Height, options.height || options.width || 300);
-                        this.props.addDocument(alias, false);
-                    }
-                });
-                return undefined;
+        protected async getDocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Document>> {
+            let ctor: ((path: string, options: DocumentOptions) => (Document | Promise<Document | undefined>)) | undefined = undefined;
+            if (type.indexOf("image") !== -1) {
+                ctor = Documents.ImageDocument;
+            }
+            if (type.indexOf("video") !== -1) {
+                ctor = Documents.VideoDocument;
+            }
+            if (type.indexOf("audio") !== -1) {
+                ctor = Documents.AudioDocument;
+            }
+            if (type.indexOf("pdf") !== -1) {
+                ctor = Documents.PdfDocument;
+                options.nativeWidth = 1200;
+            }
+            if (type.indexOf("excel") !== -1) {
+                ctor = Documents.DBDocument;
+                options.copyDraggedItems = true;
             }
-            ctor = Documents.WebDocument;
-            options = { height: options.width, ...options, title: path };
+            if (type.indexOf("html") !== -1) {
+                if (path.includes('localhost')) {
+                    let s = path.split('/');
+                    let id = s[s.length - 1];
+                    Server.GetField(id).then(field => {
+                        if (field instanceof Document) {
+                            let alias = field.CreateAlias();
+                            alias.SetNumber(KeyStore.X, options.x || 0);
+                            alias.SetNumber(KeyStore.Y, options.y || 0);
+                            alias.SetNumber(KeyStore.Width, options.width || 300);
+                            alias.SetNumber(KeyStore.Height, options.height || options.width || 300);
+                            this.props.addDocument(alias, false);
+                        }
+                    });
+                    return undefined;
+                }
+                ctor = Documents.WebDocument;
+                options = { height: options.width, ...options, title: path };
+            }
+            return ctor ? ctor(path, options) : undefined;
         }
-        return ctor ? ctor(path, options) : undefined;
-    }
 
-    @undoBatch
-    @action
-    protected onDrop(e: React.DragEvent, options: DocumentOptions): void {
-        let html = e.dataTransfer.getData("text/html");
-        let text = e.dataTransfer.getData("text/plain");
+        @undoBatch
+        @action
+        protected onDrop(e: React.DragEvent, options: DocumentOptions): void {
+            let html = e.dataTransfer.getData("text/html");
+            let text = e.dataTransfer.getData("text/plain");
 
-        if (text && text.startsWith("<div")) {
-            return;
-        }
-        e.stopPropagation();
-        e.preventDefault();
-
-        if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) {
-            console.log("not good");
-            let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
-            htmlDoc.SetText(KeyStore.DocumentText, text);
-            this.props.addDocument(htmlDoc, false);
-            return;
-        }
+            if (text && text.startsWith("<div")) {
+                return;
+            }
+            e.stopPropagation();
+            e.preventDefault();
 
-        let batch = UndoManager.StartBatch("collection view drop");
-        let promises: Promise<void>[] = [];
-        // tslint:disable-next-line:prefer-for-of
-        for (let i = 0; i < e.dataTransfer.items.length; i++) {
-            const upload = window.location.origin + RouteStore.upload;
-            let item = e.dataTransfer.items[i];
-            if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
-                let str: string;
-                let prom = new Promise<string>(resolve => e.dataTransfer.items[i].getAsString(resolve))
-                    .then(action((s: string) => rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + (str = s)))))
-                    .then(result => {
-                        let type = result.headers["content-type"];
-                        if (type) {
-                            this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
-                                .then(doc => doc && this.props.addDocument(doc, false));
-                        }
-                    });
-                promises.push(prom);
+            if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) {
+                console.log("not good");
+                let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
+                htmlDoc.SetText(KeyStore.DocumentText, text);
+                this.props.addDocument(htmlDoc, false);
+                return;
             }
-            let type = item.type;
-            if (item.kind === "file") {
-                let file = item.getAsFile();
-                let formData = new FormData();
 
-                if (file) {
-                    formData.append('file', file);
+            let batch = UndoManager.StartBatch("collection view drop");
+            let promises: Promise<void>[] = [];
+            // tslint:disable-next-line:prefer-for-of
+            for (let i = 0; i < e.dataTransfer.items.length; i++) {
+                const upload = window.location.origin + RouteStore.upload;
+                let item = e.dataTransfer.items[i];
+                if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
+                    let str: string;
+                    let prom = new Promise<string>(resolve => e.dataTransfer.items[i].getAsString(resolve))
+                        .then(action((s: string) => rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + (str = s)))))
+                        .then(result => {
+                            let type = result.headers["content-type"];
+                            if (type) {
+                                this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
+                                    .then(doc => doc && this.props.addDocument(doc, false));
+                            }
+                        });
+                    promises.push(prom);
                 }
-                let dropFileName = file ? file.name : "-empty-";
-
-                let prom = fetch(upload, {
-                    method: 'POST',
-                    body: formData
-                }).then(async (res: Response) => {
-                    (await res.json()).map(action((file: any) => {
-                        let path = window.location.origin + file;
-                        let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 600, width: 300, title: dropFileName });
-
-                        docPromise.then(action((doc?: Document) => {
-                            let docs = this.props.Document.GetT(KeyStore.Data, ListField);
-                            if (docs !== FieldWaiting) {
-                                if (!docs) {
-                                    docs = new ListField<Document>();
-                                    this.props.Document.Set(KeyStore.Data, docs);
-                                }
-                                if (doc) {
-                                    docs.Data.push(doc);
+                let type = item.type;
+                if (item.kind === "file") {
+                    let file = item.getAsFile();
+                    let formData = new FormData();
+
+                    if (file) {
+                        formData.append('file', file);
+                    }
+                    let dropFileName = file ? file.name : "-empty-";
+
+                    let prom = fetch(upload, {
+                        method: 'POST',
+                        body: formData
+                    }).then(async (res: Response) => {
+                        (await res.json()).map(action((file: any) => {
+                            let path = window.location.origin + file;
+                            let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 600, width: 300, title: dropFileName });
+
+                            docPromise.then(action((doc?: Document) => {
+                                let docs = this.props.Document.GetT(KeyStore.Data, ListField);
+                                if (docs !== FieldWaiting) {
+                                    if (!docs) {
+                                        docs = new ListField<Document>();
+                                        this.props.Document.Set(KeyStore.Data, docs);
+                                    }
+                                    if (doc) {
+                                        docs.Data.push(doc);
+                                    }
                                 }
-                            }
+                            }));
                         }));
-                    }));
-                });
-                promises.push(prom);
+                    });
+                    promises.push(prom);
+                }
             }
-        }
 
-        if (promises.length) {
-            Promise.all(promises).finally(() => batch.end());
-        } else {
-            batch.end();
+            if (promises.length) {
+                Promise.all(promises).finally(() => batch.end());
+            } else {
+                batch.end();
+            }
         }
     }
+    return CollectionSubView;
 }
+
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 945c01059..32104b49d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,7 +1,5 @@
 import { action, computed, observable, trace } from "mobx";
 import { observer } from "mobx-react";
-import { Document } from "../../../../fields/Document";
-import { KeyStore } from "../../../../fields/KeyStore";
 import { emptyFunction, returnFalse, returnOne } from "../../../../Utils";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DragManager } from "../../../util/DragManager";
@@ -13,7 +11,7 @@ import { InkingCanvas } from "../../InkingCanvas";
 import { MainOverlayTextBox } from "../../MainOverlayTextBox";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocumentContentsView } from "../../nodes/DocumentContentsView";
-import { DocumentViewProps } from "../../nodes/DocumentView";
+import { DocumentViewProps, positionSchema } from "../../nodes/DocumentView";
 import { CollectionSubView } from "../CollectionSubView";
 import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
@@ -21,17 +19,28 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
-import { BooleanField } from "../../../../fields/BooleanField";
+import { createSchema, makeInterface } from "../../../../new_fields/Schema";
+import { Doc } from "../../../../new_fields/Doc";
+import { FieldValue } from "../../../../new_fields/Types";
+
+export const panZoomSchema = createSchema({
+    panX: "number",
+    panY: "number",
+    zoom: "number"
+});
+
+type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof positionSchema]>;
+const PanZoomDocument: (doc: Doc) => PanZoomDocument = makeInterface(panZoomSchema, positionSchema);
 
 @observer
-export class CollectionFreeFormView extends CollectionSubView {
+export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type)
     private _lastX: number = 0;
     private _lastY: number = 0;
     private get _pwidth() { return this.props.PanelWidth(); }
     private get _pheight() { return this.props.PanelHeight(); }
 
-    @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
+    @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); }
     @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
     private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
     private get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's?
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index d8de5afec..845213366 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -31,13 +31,13 @@ export interface FieldViewProps {
     select: (isCtrlPressed: boolean) => void;
     isTopMost: boolean;
     selectOnLoad: boolean;
-    addDocument?: (document: Document, allowDuplicates?: boolean) => boolean;
-    removeDocument?: (document: Document) => boolean;
-    moveDocument?: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
+    addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean;
+    removeDocument?: (document: Doc) => boolean;
+    moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
     active: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
-    focus: (doc: Document) => void;
+    focus: (doc: Doc) => void;
 }
 
 @observer
diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts
index 1d1f56844..6022716be 100644
--- a/src/new_fields/Schema.ts
+++ b/src/new_fields/Schema.ts
@@ -1,9 +1,10 @@
 import { Interface, ToInterface, Cast, FieldCtor, ToConstructor, HasTail, Head, Tail } from "./Types";
-import { Doc } from "./Doc";
+import { Doc, Field, ObjectField } from "./Doc";
+import { URLField } from "./URLField";
 
-type All<T extends any[], U extends Doc> = {
-    1: makeInterface<Head<T>, U> & All<Tail<T>, U>,
-    0: makeInterface<Head<T>, U>
+type All<T extends Interface[], U extends Doc> = {
+    1: makeInterface<[Head<T>], U> & All<Tail<T>, U>,
+    0: makeInterface<[Head<T>], U>
 }[HasTail<T> extends true ? 1 : 0];
 
 type AllToInterface<T extends any[]> = {
@@ -11,10 +12,14 @@ type AllToInterface<T extends any[]> = {
     0: ToInterface<Head<T>>
 }[HasTail<T> extends true ? 1 : 0];
 
+export const emptySchema = createSchema({});
+export const Document = makeInterface(emptySchema);
+export type Document = makeInterface<[typeof emptySchema]>;
+
 export type makeInterface<T extends Interface[], U extends Doc = Doc> = Partial<AllToInterface<T>> & U;
 // export function makeInterface<T extends Interface[], U extends Doc>(schemas: T): (doc: U) => All<T, U>;
 // export function makeInterface<T extends Interface, U extends Doc>(schema: T): (doc: U) => makeInterface<T, U>; 
-export function makeInterface<T extends Interface[], U extends Doc>(schemas: T): (doc: U) => All<T, U> {
+export function makeInterface<T extends Interface[], U extends Doc>(...schemas: T): (doc: U) => makeInterface<T, U> {
     let schema: Interface = {};
     for (const s of schemas) {
         for (const key in s) {
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index 0392dc2fb..e4d15e276 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -11,7 +11,7 @@ export type ToType<T> =
 export type ToConstructor<T> =
     T extends string ? "string" :
     T extends number ? "number" :
-    T extends boolean ? "boolean" : { new(...args: any[]): T };
+    T extends boolean ? "boolean" : new (...args: any[]) => T;
 
 export type ToInterface<T> = {
     [P in keyof T]: ToType<T[P]>;
-- 
cgit v1.2.3-70-g09d2


From 393d351420b3a0d28f4cd1ea8b674fa5d04bfcde Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sun, 21 Apr 2019 23:40:48 -0400
Subject: More fixes

---
 .../views/collections/CollectionDockingView.tsx    |  6 ++--
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/FieldView.tsx               |  8 ++---
 src/client/views/nodes/ImageBox.tsx                | 15 +++++-----
 src/client/views/nodes/VideoBox.tsx                | 35 ++++++++++++----------
 src/client/views/nodes/WebBox.tsx                  |  4 +--
 src/debug/Test.tsx                                 | 12 ++++----
 src/new_fields/Doc.ts                              |  4 +--
 src/new_fields/Schema.ts                           | 22 +++-----------
 src/new_fields/Types.ts                            | 11 +++----
 src/new_fields/URLField.ts                         |  9 ++++--
 12 files changed, 62 insertions(+), 68 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 7b204cbdc..86ee7cea9 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -16,7 +16,7 @@ import { ServerUtils } from "../../../server/ServerUtil";
 import { DragManager, DragLinksAsDocuments } from "../../util/DragManager";
 import { Transform } from '../../util/Transform';
 import { Doc, Id, Opt, Field, FieldId } from "../../../new_fields/Doc";
-import { Cast, FieldValue } from "../../../new_fields/Types";
+import { Cast } from "../../../new_fields/Types";
 import { List } from "../../../new_fields/List";
 import { DocServer } from "../../DocServer";
 
@@ -336,8 +336,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
 
     ScreenToLocalTransform = () => {
         if (this._mainCont.current && this._mainCont.current.children) {
-            let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!.children[0].firstChild as HTMLElement);
-            scale = Utils.GetScreenTransform(this._mainCont.current!).scale;
+            let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current.children[0].firstChild as HTMLElement);
+            scale = Utils.GetScreenTransform(this._mainCont.current).scale;
             return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this.contentScaling());
         }
         return Transform.Identity();
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 27fd25b5c..c6eea5623 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -18,7 +18,7 @@ const schema = createSchema({
 });
 
 type FreeformDocument = makeInterface<[typeof schema, typeof positionSchema]>;
-const FreeformDocument = makeInterface([schema, positionSchema]);
+const FreeformDocument = makeInterface(schema, positionSchema);
 
 @observer
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, FreeformDocument>(FreeformDocument) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f1a12e6db..c0afc192e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -56,7 +56,7 @@ export const positionSchema = createSchema({
 });
 
 type Document = makeInterface<[typeof schema]>;
-const Document = makeInterface([schema]);
+const Document = makeInterface(schema);
 
 @observer
 export class DocumentView extends DocComponent<DocumentViewProps, Document>(Document) {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 845213366..b11a87d75 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -3,19 +3,19 @@ import { observer } from "mobx-react";
 import { computed } from "mobx";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { ImageBox } from "./ImageBox";
-import { WebBox } from "./WebBox";
 import { VideoBox } from "./VideoBox";
 import { AudioBox } from "./AudioBox";
 import { DocumentContentsView } from "./DocumentContentsView";
 import { Transform } from "../../util/Transform";
-import { returnFalse, emptyFunction, returnOne } from "../../../Utils";
+import { returnFalse, emptyFunction } from "../../../Utils";
 import { CollectionView } from "../collections/CollectionView";
 import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { IconField } from "../../../fields/IconFIeld";
 import { IconBox } from "./IconBox";
-import { Opt, Doc, Field, FieldValue, FieldWaiting } from "../../../new_fields/Doc";
+import { Opt, Doc, FieldResult } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
+import { ImageField, VideoField, AudioField } from "../../../new_fields/URLField";
 
 
 //
@@ -47,7 +47,7 @@ export class FieldView extends React.Component<FieldViewProps> {
     }
 
     @computed
-    get field(): FieldValue {
+    get field(): FieldResult {
         const { Document, fieldKey } = this.props;
         return Document[fieldKey];
     }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 3b72e9f39..c11aee56e 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -14,16 +14,15 @@ import { createSchema, makeInterface } from '../../../new_fields/Schema';
 import { DocComponent } from '../DocComponent';
 import { positionSchema } from './DocumentView';
 import { FieldValue, Cast } from '../../../new_fields/Types';
-import { URLField } from '../../../new_fields/URLField';
-import { Doc, FieldWaiting } from '../../../new_fields/Doc';
+import { ImageField } from '../../../new_fields/URLField';
 import { List } from '../../../new_fields/List';
 
-const schema = createSchema({
+export const pageSchema = createSchema({
     curPage: "number"
 });
 
-type ImageDocument = makeInterface<[typeof schema, typeof positionSchema]>;
-const ImageDocument = makeInterface([schema, positionSchema]);
+type ImageDocument = makeInterface<[typeof pageSchema, typeof positionSchema]>;
+const ImageDocument = makeInterface(pageSchema, positionSchema);
 
 @observer
 export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageDocument) {
@@ -135,7 +134,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     }
 
     specificContextMenu = (e: React.MouseEvent): void => {
-        let field = Cast(this.Document[this.props.fieldKey], URLField);
+        let field = Cast(this.Document[this.props.fieldKey], ImageField);
         if (field && field !== FieldWaiting) {
             let url = field.url.href;
             ContextMenu.Instance.addItem({
@@ -167,8 +166,8 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
         let field = this.Document[this.props.fieldKey];
         let paths: string[] = ["http://www.cs.brown.edu/~bcz/face.gif"];
         if (field === FieldWaiting) paths = ["https://image.flaticon.com/icons/svg/66/66163.svg"];
-        else if (field instanceof URLField) paths = [field.url.href];
-        else if (field instanceof List) paths = field.filter(val => val instanceof URLField).map(p => (p as URLField).url.href);
+        else if (field instanceof ImageField) paths = [field.url.href];
+        else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => (p as ImageField).url.href);
         let nativeWidth = FieldValue(this.Document.nativeWidth, 1);
         return (
             <div className="imageBox-cont" onPointerDown={this.onPointerDown} onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 9d7c2bc56..7b922b620 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,18 +1,22 @@
 import React = require("react");
 import { observer } from "mobx-react";
-import { FieldWaiting, Opt } from '../../../fields/Field';
-import { VideoField } from '../../../fields/VideoField';
 import { FieldView, FieldViewProps } from './FieldView';
 import "./VideoBox.scss";
 import Measure from "react-measure";
-import { action, trace, observable, IReactionDisposer, computed, reaction } from "mobx";
-import { KeyStore } from "../../../fields/KeyStore";
-import { number } from "prop-types";
+import { action, computed } from "mobx";
+import { DocComponent } from "../DocComponent";
+import { positionSchema } from "./DocumentView";
+import { makeInterface } from "../../../new_fields/Schema";
+import { pageSchema } from "./ImageBox";
+import { Cast, FieldValue } from "../../../new_fields/Types";
+import { VideoField } from "../../../new_fields/URLField";
+
+type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
+const VideoDocument = makeInterface(positionSchema, pageSchema);
 
 @observer
-export class VideoBox extends React.Component<FieldViewProps> {
+export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoDocument) {
 
-    private _reactionDisposer: Opt<IReactionDisposer>;
     private _videoRef = React.createRef<HTMLVideoElement>();
     public static LayoutString() { return FieldView.LayoutString(VideoBox); }
 
@@ -20,7 +24,7 @@ export class VideoBox extends React.Component<FieldViewProps> {
         super(props);
     }
 
-    @computed private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, -1); }
+    @computed private get curPage() { return FieldValue(this.Document.curPage, -1); }
 
 
     _loaded: boolean = false;
@@ -31,12 +35,12 @@ export class VideoBox extends React.Component<FieldViewProps> {
             // bcz: the nativeHeight should really be set when the document is imported.
             //      also, the native dimensions could be different for different pages of the PDF
             //      so this design is flawed.
-            var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
-            var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
+            var nativeWidth = FieldValue(this.Document.nativeWidth, 0);
+            var nativeHeight = FieldValue(this.Document.nativeHeight, 0);
             var newNativeHeight = nativeWidth * r.entry.height / r.entry.width;
             if (!nativeHeight && newNativeHeight !== nativeHeight && !isNaN(newNativeHeight)) {
-                this.props.Document.SetNumber(KeyStore.Height, newNativeHeight / nativeWidth * this.props.Document.GetNumber(KeyStore.Width, 0));
-                this.props.Document.SetNumber(KeyStore.NativeHeight, newNativeHeight);
+                this.Document.height = newNativeHeight / nativeWidth * FieldValue(this.Document.width, 0);
+                this.Document.nativeHeight = newNativeHeight;
             }
         } else {
             this._loaded = true;
@@ -56,12 +60,11 @@ export class VideoBox extends React.Component<FieldViewProps> {
     }
 
     render() {
-        let field = this.props.Document.GetT(this.props.fieldKey, VideoField);
-        if (!field || field === FieldWaiting) {
+        let field = FieldValue(Cast(this.Document[this.props.fieldKey], VideoField));
+        if (!field) {
             return <div>Loading</div>;
         }
-        let path = field.Data.href;
-        trace();
+        let path = field.url.href;
         return (
             <Measure onResize={this.setScaling}>
                 {({ measureRef }) =>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index e3b6e1c05..63778fa50 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -4,7 +4,7 @@ import { FieldViewProps, FieldView } from './FieldView';
 import { observer } from "mobx-react";
 import { computed } from 'mobx';
 import { HtmlField } from "../../../new_fields/HtmlField";
-import { URLField } from "../../../new_fields/URLField";
+import { WebField } from "../../../new_fields/URLField";
 
 @observer
 export class WebBox extends React.Component<FieldViewProps> {
@@ -37,7 +37,7 @@ export class WebBox extends React.Component<FieldViewProps> {
         let view;
         if (field instanceof HtmlField) {
             view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />
-        } else if (field instanceof URLField) {
+        } else if (field instanceof WebField) {
             view = <iframe src={field.url.href} style={{ position: "absolute", width: "100%", height: "100%" }} />
         } else {
             view = <iframe src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%" }} />
diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx
index 81c69ca8e..22a39f31e 100644
--- a/src/debug/Test.tsx
+++ b/src/debug/Test.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
 import * as ReactDOM from 'react-dom';
 import { SerializationHelper } from '../client/util/SerializationHelper';
 import { createSchema, makeInterface, makeStrictInterface } from '../new_fields/Schema';
-import { URLField } from '../new_fields/URLField';
+import { ImageField } from '../new_fields/URLField';
 import { Doc } from '../new_fields/Doc';
 import { ListSpec } from '../new_fields/Types';
 import { List } from '../new_fields/List';
@@ -12,19 +12,19 @@ const schema1 = createSchema({
     hello: "number",
     test: "string",
     fields: "boolean",
-    url: URLField,
+    url: ImageField,
     testDoc: Doc
 });
 
-const TestDoc = makeInterface([schema1]);
+const TestDoc = makeInterface(schema1);
 type TestDoc = makeInterface<[typeof schema1]>;
 
 const schema2 = createSchema({
-    hello: URLField,
+    hello: ImageField,
     test: "boolean",
     fields: { List: "number" } as ListSpec<number>,
     url: "number",
-    testDoc: URLField
+    testDoc: ImageField
 });
 
 const Test2Doc = makeStrictInterface(schema2);
@@ -43,7 +43,7 @@ const assert = (bool: boolean) => {
 
 class Test extends React.Component {
     onClick = () => {
-        const url = new URLField(new URL("http://google.com"));
+        const url = new ImageField(new URL("http://google.com"));
         const doc = new Doc();
         const doc2 = new Doc();
         doc.hello = 5;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 10e8fe7ec..5d18cbb2e 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -33,7 +33,7 @@ export class ObjectField {
 export type Field = number | string | boolean | ObjectField | RefField;
 export type Opt<T> = T | undefined;
 export type FieldWaiting<T extends Field = Field> = Promise<T | undefined>;
-export type FieldValue<T extends Field = Field> = Opt<T> | FieldWaiting<T>;
+export type FieldResult<T extends Field = Field> = Opt<T> | FieldWaiting<T>;
 
 export const Self = Symbol("Self");
 
@@ -77,7 +77,7 @@ export namespace Doc {
             return Cast(field, ctor);
         });
     }
-    export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldValue {
+    export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldResult {
         const self = doc[Self];
         return getField(self, key, ignoreProto);
     }
diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts
index 696a5d2a8..3b7078cb0 100644
--- a/src/new_fields/Schema.ts
+++ b/src/new_fields/Schema.ts
@@ -1,13 +1,7 @@
 import { Interface, ToInterface, Cast, FieldCtor, ToConstructor, HasTail, Head, Tail } from "./Types";
 import { Doc, Field, ObjectField } from "./Doc";
-import { URLField } from "./URLField";
 
-type All<T extends Interface[], U extends Doc> = {
-    1: makeInterface<[Head<T>], U> & All<Tail<T>, U>,
-    0: makeInterface<[Head<T>], U>
-}[HasTail<T> extends true ? 1 : 0];
-
-type AllToInterface<T extends any[]> = {
+type AllToInterface<T extends Interface[]> = {
     1: ToInterface<Head<T>> & AllToInterface<Tail<T>>,
     0: ToInterface<Head<T>>
 }[HasTail<T> extends true ? 1 : 0];
@@ -16,7 +10,6 @@ export const emptySchema = createSchema({});
 export const Document = makeInterface(emptySchema);
 export type Document = makeInterface<[typeof emptySchema]>;
 
-const DocSymbol = Symbol("Doc");
 export type makeInterface<T extends Interface[], U extends Doc = Doc> = Partial<AllToInterface<T>> & U;
 // export function makeInterface<T extends Interface[], U extends Doc>(schemas: T): (doc: U) => All<T, U>;
 // export function makeInterface<T extends Interface, U extends Doc>(schema: T): (doc: U) => makeInterface<T, U>; 
@@ -29,26 +22,19 @@ export function makeInterface<T extends Interface[], U extends Doc>(...schemas:
     }
     const proto = new Proxy({}, {
         get(target: any, prop) {
-            if (prop === DocSymbol) {
-                return target[prop];
-            }
-            const field = target[DocSymbol][prop];
+            const field = target.doc[prop];
             if (prop in schema) {
                 return Cast(field, (schema as any)[prop]);
             }
             return field;
         },
         set(target: any, prop, value) {
-            if (prop === DocSymbol) {
-                target[prop] = value;
-            }
-            target[DocSymbol][prop] = value;
+            target.doc[prop] = value;
             return true;
         }
     });
     return function (doc: any) {
-        const obj = Object.create(proto);
-        obj.__doc = doc;
+        const obj = Object.create(proto, { doc: { value: doc, writable: false } });
         return obj;
     };
 }
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index e4d15e276..f4f66fe5c 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -1,4 +1,4 @@
-import { Field, Opt, FieldWaiting, FieldValue } from "./Doc";
+import { Field, Opt, FieldWaiting, FieldResult } from "./Doc";
 import { List } from "./List";
 
 export type ToType<T> =
@@ -6,7 +6,8 @@ export type ToType<T> =
     T extends "number" ? number :
     T extends "boolean" ? boolean :
     T extends ListSpec<infer U> ? List<U> :
-    T extends { new(...args: any[]): infer R } ? (R | Promise<R>) : never;
+    // T extends { new(...args: any[]): infer R } ? (R | Promise<R>) : never;
+    T extends { new(...args: any[]): infer R } ? R : never;
 
 export type ToConstructor<T> =
     T extends string ? "string" :
@@ -35,9 +36,9 @@ export interface Interface {
 
 export type FieldCtor<T extends Field> = T extends List<infer R> ? ListSpec<R> : ToConstructor<T>;
 
-export function Cast<T extends FieldCtor<Field>>(field: Field | FieldWaiting | undefined, ctor: T): FieldValue<ToType<T>>;
+export function Cast<T extends FieldCtor<Field>>(field: Field | FieldWaiting | undefined, ctor: T): FieldResult<ToType<T>>;
 export function Cast<T extends FieldCtor<Field>>(field: Field | FieldWaiting | undefined, ctor: T, defaultVal: ToType<T>): ToType<T>;
-export function Cast<T extends FieldCtor<Field>>(field: Field | FieldWaiting | undefined, ctor: T, defaultVal?: ToType<T>): FieldValue<ToType<T>> | undefined {
+export function Cast<T extends FieldCtor<Field>>(field: Field | FieldWaiting | undefined, ctor: T, defaultVal?: ToType<T>): FieldResult<ToType<T>> | undefined {
     if (field instanceof Promise) {
         return defaultVal === undefined ? field.then(f => Cast(f, ctor) as any) : defaultVal;
     }
@@ -66,6 +67,6 @@ export function FieldValue<T extends Field>(field: Opt<T> | Promise<Opt<T>>, def
 export interface PromiseLike<T> {
     then(callback: (field: Opt<T> | PromiseLike<T>) => void): void;
 }
-export function PromiseValue<T extends Field>(field: FieldValue<T>): PromiseLike<Opt<T>> {
+export function PromiseValue<T extends Field>(field: FieldResult<T>): PromiseLike<Opt<T>> {
     return field instanceof Promise ? field : { then(cb: ((field: Opt<T>) => void)) { return cb(field); } };
 }
\ No newline at end of file
diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts
index e456a7d16..1da245e73 100644
--- a/src/new_fields/URLField.ts
+++ b/src/new_fields/URLField.ts
@@ -13,7 +13,6 @@ function url() {
     );
 }
 
-@Deserializable("url")
 export class URLField extends ObjectField {
     @serializable(url())
     readonly url: URL;
@@ -22,4 +21,10 @@ export class URLField extends ObjectField {
         super();
         this.url = url;
     }
-}
\ No newline at end of file
+}
+
+@Deserializable("audio") export class AudioField extends URLField { }
+@Deserializable("image") export class ImageField extends URLField { }
+@Deserializable("video") export class VideoField extends URLField { }
+@Deserializable("pdf") export class PdfField extends URLField { }
+@Deserializable("web") export class WebField extends URLField { }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 575d8b339cdf74cdcb8d46dcd6e627aec2dda636 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 24 Apr 2019 23:17:44 -0400
Subject: added borderRadius and fixed a bunch of broken props along the way.

---
 src/client/documents/Documents.ts                          |  2 ++
 src/client/views/Main.scss                                 |  3 ++-
 src/client/views/Main.tsx                                  |  3 ++-
 src/client/views/MainOverlayTextBox.tsx                    |  4 ++--
 src/client/views/collections/CollectionBaseView.tsx        |  7 ++++---
 src/client/views/collections/CollectionDockingView.tsx     |  3 ++-
 src/client/views/collections/CollectionSchemaView.tsx      |  6 +++++-
 src/client/views/collections/CollectionSubView.tsx         |  1 +
 src/client/views/collections/CollectionTreeView.tsx        |  5 ++++-
 .../collectionFreeForm/CollectionFreeFormView.scss         |  1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx          | 14 +++++++++++---
 .../views/collections/collectionFreeForm/MarqueeView.tsx   |  7 ++++---
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx  | 11 +++++++++++
 src/client/views/nodes/DocumentView.tsx                    |  2 ++
 src/client/views/nodes/FieldView.tsx                       |  7 ++++++-
 src/client/views/nodes/FormattedTextBox.scss               |  3 +--
 src/client/views/nodes/FormattedTextBox.tsx                |  5 ++++-
 src/client/views/nodes/ImageBox.scss                       |  3 ---
 src/client/views/nodes/ImageBox.tsx                        |  2 +-
 src/fields/KeyStore.ts                                     |  3 ++-
 20 files changed, 67 insertions(+), 25 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 0e6661819..879b114b6 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -55,6 +55,7 @@ export interface DocumentOptions {
     backgroundColor?: string;
     copyDraggedItems?: boolean;
     documentText?: string;
+    borderRounding?: number;
 }
 
 export namespace Documents {
@@ -108,6 +109,7 @@ export namespace Documents {
         if (options.layout !== undefined) { doc.SetText(KeyStore.Layout, options.layout); }
         if (options.layoutKeys !== undefined) { doc.Set(KeyStore.LayoutKeys, new ListField(options.layoutKeys)); }
         if (options.copyDraggedItems !== undefined) { doc.SetBoolean(KeyStore.CopyDraggedItems, options.copyDraggedItems); }
+        if (options.borderRounding !== undefined) { doc.SetNumber(KeyStore.BorderRounding, options.borderRounding); }
         return doc;
     }
 
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 4373534b2..3edc79d39 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -42,7 +42,8 @@ h1 {
 }
 
 .jsx-parser {
-    width:100%
+    width:100%;
+    pointer-events: none;
 }
 
 p {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 934616aca..057f68ed9 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -17,7 +17,7 @@ import { CurrentUserUtils } from '../../server/authentication/models/current_use
 import { MessageStore } from '../../server/Message';
 import { RouteStore } from '../../server/RouteStore';
 import { ServerUtils } from '../../server/ServerUtil';
-import { emptyDocFunction, emptyFunction, returnTrue, Utils, returnOne } from '../../Utils';
+import { emptyDocFunction, emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils';
 import { Documents } from '../documents/Documents';
 import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel';
 import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel';
@@ -197,6 +197,7 @@ export class Main extends React.Component {
                             isTopMost={true}
                             selectOnLoad={false}
                             focus={emptyDocFunction}
+                            borderRounding={returnZero}
                             parentActive={returnTrue}
                             whenActiveChanged={emptyFunction}
                             ContainingCollectionView={undefined} />}
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 9aaaad084..f98f6e8a5 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -5,7 +5,7 @@ import * as React from 'react';
 import { Document } from '../../fields/Document';
 import { Key } from '../../fields/Key';
 import { KeyStore } from '../../fields/KeyStore';
-import { emptyDocFunction, emptyFunction, returnTrue } from '../../Utils';
+import { emptyDocFunction, emptyFunction, returnTrue, returnZero } from '../../Utils';
 import '../northstar/model/ModelExtensions';
 import '../northstar/utils/Extensions';
 import { DragManager } from '../util/DragManager';
@@ -97,7 +97,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
                     style={{ width: `${this.TextDoc.Width()}px`, height: `${this.TextDoc.Height()}px` }}>
                     <FormattedTextBox fieldKey={this._textFieldKey} isOverlay={true} Document={this.TextDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
                         selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
-                        ScreenToLocalTransform={this._textXf} focus={emptyDocFunction} />
+                        ScreenToLocalTransform={this._textXf} borderRounding={returnZero} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyDocFunction} />
                 </div>
             </ div>;
         }
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 097236c1d..e65770137 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -2,13 +2,12 @@ import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Document } from '../../../fields/Document';
-import { Field, FieldValue, FieldWaiting } from '../../../fields/Field';
+import { FieldValue, FieldWaiting } from '../../../fields/Field';
 import { KeyStore } from '../../../fields/KeyStore';
 import { ListField } from '../../../fields/ListField';
 import { NumberField } from '../../../fields/NumberField';
 import { ContextMenu } from '../ContextMenu';
 import { FieldViewProps } from '../nodes/FieldView';
-import { TextField } from '../../../fields/TextField';
 
 export enum CollectionViewType {
     Invalid,
@@ -190,7 +189,9 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         };
         const viewtype = this.collectionViewType;
         return (
-            <div className={this.props.className || "collectionView-cont"} onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
+            <div className={this.props.className || "collectionView-cont"}
+                style={{ borderRadius: `${this.props.borderRounding()}px`, pointerEvents: "all" }}
+                onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
                 {viewtype !== undefined ? this.props.children(viewtype, props) : (null)}
             </div>
         );
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 84159635a..eb94a4e6d 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -8,7 +8,7 @@ import { Document } from "../../../fields/Document";
 import { KeyStore } from "../../../fields/KeyStore";
 import Measure from "react-measure";
 import { FieldId, Opt, Field, FieldWaiting } from "../../../fields/Field";
-import { Utils, returnTrue, emptyFunction, emptyDocFunction, returnOne } from "../../../Utils";
+import { Utils, returnTrue, emptyFunction, emptyDocFunction, returnOne, returnZero } from "../../../Utils";
 import { Server } from "../../Server";
 import { undoBatch } from "../../util/UndoManager";
 import { DocumentView } from "../nodes/DocumentView";
@@ -368,6 +368,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                     parentActive={returnTrue}
                     whenActiveChanged={emptyFunction}
                     focus={emptyDocFunction}
+                    borderRounding={returnZero}
                     ContainingCollectionView={undefined} />
             </div>);
     }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index cb7a67991..dc2910ede 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -12,7 +12,7 @@ import { Field, Opt } from "../../../fields/Field";
 import { Key } from "../../../fields/Key";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
-import { emptyDocFunction, emptyFunction, returnFalse } from "../../../Utils";
+import { emptyDocFunction, emptyFunction, returnFalse, returnZero } from "../../../Utils";
 import { Server } from "../../Server";
 import { SetupDrag } from "../../util/DragManager";
 import { CompileScript, ToField } from "../../util/Scripting";
@@ -77,6 +77,9 @@ export class CollectionSchemaView extends CollectionSubView {
             focus: emptyDocFunction,
             active: returnFalse,
             whenActiveChanged: emptyFunction,
+            borderRounding: this.props.borderRounding,
+            PanelHeight: returnZero,
+            PanelWidth: returnZero,
         };
         let contents = (
             <FieldView {...props} />
@@ -275,6 +278,7 @@ export class CollectionSchemaView extends CollectionSubView {
                         focus={emptyDocFunction}
                         parentActive={this.props.active}
                         whenActiveChanged={this.props.whenActiveChanged}
+                        borderRounding={this.props.borderRounding}
                     />
                 </div>
                 <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index f9fc7be5a..bf59bec95 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -25,6 +25,7 @@ export interface CollectionViewProps extends FieldViewProps {
     moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
     PanelWidth: () => number;
     PanelHeight: () => number;
+    borderRounding: () => number;
 }
 
 export interface SubCollectionViewProps extends CollectionViewProps {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e0387f4b4..b9d1f0bd9 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -136,7 +136,10 @@ export class CollectionTreeView extends CollectionSubView {
             );
 
         return (
-            <div id="body" className="collectionTreeView-dropTarget" onWheel={(e: React.WheelEvent) => e.stopPropagation()} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
+            <div id="body" className="collectionTreeView-dropTarget"
+                style={{ borderRadius: `${this.props.borderRounding()}px` }}
+                onWheel={(e: React.WheelEvent) => e.stopPropagation()}
+                onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
                 <div className="coll-title">
                     <EditableView
                         contents={this.props.Document.Title}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 57706b51e..67a0e532c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -6,6 +6,7 @@
   width: 100%;
   height: 100%;
   transform-origin: left top;
+  pointer-events: none;
 }
 .collectionfreeformview-container {
   .collectionfreeformview > .jsx-parser {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 7492d8744..9e2c9bf9a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -257,6 +257,7 @@ export class CollectionFreeFormView extends CollectionSubView {
             focus: this.focusDocument,
             parentActive: this.props.active,
             whenActiveChanged: this.props.active,
+            borderRounding: this.props.borderRounding,
         };
     }
 
@@ -288,11 +289,17 @@ export class CollectionFreeFormView extends CollectionSubView {
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         return (
             <div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
+                style={{ borderRadius: `${this.props.borderRounding()}px` }}
                 onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} >
-                <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
+                {/* <svg viewBox="0 0 180 18" style={{ top: "50%", opacity: 0.05, position: "absolute" }}>
+                    <text y="15" >
+                        {this.props.Document.Title}
+                    </text>
+                </svg> */}
+                <MarqueeView container={this} borderRadius={this.props.borderRounding} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
                     addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox}
                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
-                    <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
+                    <CollectionFreeFormViewPannableContents borderRadius={this.props.borderRounding} centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
                         zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
                         <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
                             <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
@@ -340,6 +347,7 @@ interface CollectionFreeFormViewPannableContentsProps {
     panX: () => number;
     panY: () => number;
     zoomScaling: () => number;
+    borderRadius: () => number;
 }
 
 @observer
@@ -350,7 +358,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
         const panx = -this.props.panX();
         const pany = -this.props.panY();
         const zoom = this.props.zoomScaling();// needs to be a variable outside of the <Measure> otherwise, reactions won't fire
-        return <div className="collectionfreeformview" style={{ transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
+        return <div className="collectionfreeformview" style={{ borderRadius: `${this.props.borderRadius()}px`, transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
             {this.props.children}
         </div>;
     }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 898fff019..e724ab47e 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -14,7 +14,6 @@ import { CollectionFreeFormView } from "./CollectionFreeFormView";
 import "./MarqueeView.scss";
 import React = require("react");
 import { Utils } from "../../../../Utils";
-import { ListField } from "../../../../fields/ListField";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -25,6 +24,7 @@ interface MarqueeViewProps {
     selectDocuments: (docs: Document[]) => void;
     removeDocument: (doc: Document) => boolean;
     addLiveTextDocument: (doc: Document) => void;
+    borderRadius: () => number;
 }
 
 @observer
@@ -138,7 +138,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             this.cleanupInteractions(true);
             e.stopPropagation();
         }
-        if (e.key === "c" || e.key === "r") {
+        if (e.key === "c" || e.key === "r" || e.key === "e") {
             e.stopPropagation();
             let bounds = this.Bounds;
             let selected = this.marqueeSelect().map(d => {
@@ -156,6 +156,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 y: bounds.top,
                 panx: 0,
                 pany: 0,
+                borderRounding: e.key === "e" ? -1 : undefined,
                 backgroundColor: selected.length ? "white" : "",
                 scale: zoomBasis,
                 width: bounds.width * zoomBasis,
@@ -250,7 +251,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     }
 
     render() {
-        return <div className="marqueeView" onClick={this.onClick} onPointerDown={this.onPointerDown}>
+        return <div className="marqueeView" style={{ borderRadius: `${this.props.borderRadius()}px` }} onClick={this.onClick} onPointerDown={this.onPointerDown}>
             {this.props.children}
             {!this._visible ? (null) : this.marqueeDiv}
         </div>;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 420324d40..8caf5ff31 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -65,6 +65,7 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
             ScreenToLocalTransform={this.getTransform}
             PanelWidth={this.panelWidth}
             PanelHeight={this.panelHeight}
+            borderRounding={this.borderRounding}
         />;
     }
 
@@ -148,6 +149,15 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
         }
     }
 
+    borderRounding = () => {
+        let br = this.props.Document.GetNumber(KeyStore.BorderRounding, 0);
+        return br >= 0 ? br :
+            this.props.Document.GetNumber(KeyStore.NativeWidth, 0) === 0 ?
+                Math.min(this.props.PanelWidth(), this.props.PanelHeight())
+                :
+                Math.min(this.props.Document.GetNumber(KeyStore.NativeWidth, 0), this.props.Document.GetNumber(KeyStore.NativeHeight, 0));
+    }
+
     render() {
         let maximizedDoc = this.props.Document.GetT(KeyStore.MaximizedDoc, Document);
         let zoomFade = 1;
@@ -168,6 +178,7 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
                 onClick={this.onClick}
                 style={{
                     opacity: zoomFade,
+                    borderRadius: `${this.borderRounding()}px`,
                     transformOrigin: "left top",
                     transform: this.transform,
                     pointerEvents: (zoomFade < 0.09 ? "none" : "all"),
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c19b0c342..ddf99c109 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -41,6 +41,7 @@ export interface DocumentViewProps {
     parentActive: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     toggleMinimized: () => void;
+    borderRounding: () => number;
 }
 export interface JsxArgs extends DocumentViewProps {
     Keys: { [name: string]: Key };
@@ -277,6 +278,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             <div className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
                 style={{
+                    borderRadius: `${this.props.borderRounding()}px`,
                     background: this.props.Document.GetText(KeyStore.BackgroundColor, ""),
                     width: nativeWidth, height: nativeHeight,
                     transform: `scale(${scaling}, ${scaling})`
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 93e385821..8484194b0 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -19,7 +19,7 @@ import { ListField } from "../../../fields/ListField";
 import { DocumentContentsView } from "./DocumentContentsView";
 import { Transform } from "../../util/Transform";
 import { KeyStore } from "../../../fields/KeyStore";
-import { returnFalse, emptyDocFunction, emptyFunction, returnOne } from "../../../Utils";
+import { returnFalse, emptyDocFunction, emptyFunction, returnOne, returnZero } from "../../../Utils";
 import { CollectionView } from "../collections/CollectionView";
 import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
@@ -47,6 +47,9 @@ export interface FieldViewProps {
     active: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     focus: (doc: Document) => void;
+    PanelWidth: () => number;
+    PanelHeight: () => number;
+    borderRounding: () => number;
 }
 
 @observer
@@ -100,6 +103,8 @@ export class FieldView extends React.Component<FieldViewProps> {
                     layoutKey={KeyStore.Layout}
                     ContainingCollectionView={this.props.ContainingCollectionView}
                     parentActive={this.props.active}
+                    borderRounding={returnZero}
+                    toggleMinimized={emptyFunction}
                     whenActiveChanged={this.props.whenActiveChanged} />
             );
         }
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 5eb2bf7ce..59857bab3 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -22,11 +22,10 @@
   overflow-x: hidden;
   color: initial;
   height: 100%;
-  pointer-events: all;
+  pointer-events: none;
 }
 .formattedTextBox-cont-hidden {
     overflow: hidden;
-    pointer-events: none;
 }
 
 .menuicon {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 9cd58c73a..4b2fdbaff 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -273,7 +273,10 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         let color = this.props.Document.GetText(KeyStore.BackgroundColor, "");
         return (
             <div className={`formattedTextBox-cont-${style}`}
-                style={{ background: color }}
+                style={{
+                    borderRadius: `${this.props.borderRounding()}px`,
+                    background: color,
+                }}
                 onKeyDown={this.onKeyPress}
                 onKeyPress={this.onKeyPress}
                 onFocus={this.onFocused}
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index db9a08fba..96a724867 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -8,9 +8,6 @@
   max-height: 100%;
   pointer-events: none;
 }
-.imageBox-cont-interactive {
-    pointer-events: all;
-}
 
 .imageBox-dot {
     position:absolute;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index a16fd5007..b22accb74 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -165,7 +165,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
         let nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 1);
         let interactive = InkingControl.Instance.selectedTool ? "" : "interactive"
         return (
-            <div className={`imageBox-cont-${interactive}`} onPointerDown={this.onPointerDown} onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
+            <div className="imageBox-cont" onPointerDown={this.onPointerDown} onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
                 <img src={paths[Math.min(paths.length, this._photoIndex)]} style={{ objectFit: (this._photoIndex === 0 ? undefined : "contain") }} width={nativeWidth} alt="Image not found" ref={this._imgRef} onLoad={this.onLoad} />
                 {paths.length > 1 ? this.dots(paths) : (null)}
                 {this.lightbox(paths)}
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 0a96beb50..aa18a9f33 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -49,12 +49,13 @@ export namespace KeyStore {
     export const MinimizedDoc = new Key("MinimizedDoc");
     export const MaximizedDoc = new Key("MaximizedDoc");
     export const CopyDraggedItems = new Key("CopyDraggedItems");
+    export const BorderRounding = new Key("BorderRounding");
 
     export const KeyList: Key[] = [Prototype, X, Y, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight,
         Width, Height, ZIndex, ZoomBasis, Data, Annotations, ViewType, Layout, BackgroundColor, BackgroundLayout, OverlayLayout, LayoutKeys,
         LayoutFields, ColumnsKey, SchemaSplitPercentage, Caption, ActiveWorkspace, DocumentText, BrushingDocs, LinkedToDocs, LinkedFromDocs,
         LinkDescription, LinkTags, Thumbnail, ThumbnailPage, CurPage, AnnotationOn, NumPages, Ink, Cursors, OptionalRightCollection,
-        Archives, Workspaces, IsMinimized, MinimizedDoc, MaximizedDoc, CopyDraggedItems
+        Archives, Workspaces, IsMinimized, MinimizedDoc, MaximizedDoc, CopyDraggedItems, BorderRounding
     ];
     export function KeyLookup(keyid: string) {
         for (const key of KeyList) {
-- 
cgit v1.2.3-70-g09d2


From 6a62e0a16abc3bbadfb179750463e3d361f1c759 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 25 Apr 2019 09:15:22 -0400
Subject: silly me - borderRadius can be inherited

---
 src/client/views/Main.scss                                     |  1 +
 src/client/views/Main.tsx                                      |  1 -
 src/client/views/MainOverlayTextBox.tsx                        |  2 +-
 src/client/views/collections/CollectionBaseView.tsx            |  2 +-
 src/client/views/collections/CollectionDockingView.tsx         |  1 -
 src/client/views/collections/CollectionSchemaView.tsx          |  2 --
 src/client/views/collections/CollectionSubView.tsx             |  1 -
 src/client/views/collections/CollectionTreeView.tsx            |  2 +-
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx  | 10 ++++------
 .../views/collections/collectionFreeForm/MarqueeView.tsx       |  3 +--
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx      |  1 -
 src/client/views/nodes/DocumentView.tsx                        |  3 +--
 src/client/views/nodes/FieldView.tsx                           |  2 --
 src/client/views/nodes/FormattedTextBox.tsx                    |  2 +-
 14 files changed, 11 insertions(+), 22 deletions(-)

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

diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 3edc79d39..2f899ff28 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -44,6 +44,7 @@ h1 {
 .jsx-parser {
     width:100%;
     pointer-events: none;
+    border-radius: inherit;
 }
 
 p {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 057f68ed9..c6b3f06d8 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -197,7 +197,6 @@ export class Main extends React.Component {
                             isTopMost={true}
                             selectOnLoad={false}
                             focus={emptyDocFunction}
-                            borderRounding={returnZero}
                             parentActive={returnTrue}
                             whenActiveChanged={emptyFunction}
                             ContainingCollectionView={undefined} />}
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index f98f6e8a5..be8d67925 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -97,7 +97,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
                     style={{ width: `${this.TextDoc.Width()}px`, height: `${this.TextDoc.Height()}px` }}>
                     <FormattedTextBox fieldKey={this._textFieldKey} isOverlay={true} Document={this.TextDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
                         selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
-                        ScreenToLocalTransform={this._textXf} borderRounding={returnZero} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyDocFunction} />
+                        ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyDocFunction} />
                 </div>
             </ div>;
         }
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index e65770137..87b767c93 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -190,7 +190,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         const viewtype = this.collectionViewType;
         return (
             <div className={this.props.className || "collectionView-cont"}
-                style={{ borderRadius: `${this.props.borderRounding()}px`, pointerEvents: "all" }}
+                style={{ borderRadius: "inherit", pointerEvents: "all" }}
                 onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
                 {viewtype !== undefined ? this.props.children(viewtype, props) : (null)}
             </div>
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index eb94a4e6d..3267f2455 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -368,7 +368,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                     parentActive={returnTrue}
                     whenActiveChanged={emptyFunction}
                     focus={emptyDocFunction}
-                    borderRounding={returnZero}
                     ContainingCollectionView={undefined} />
             </div>);
     }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index dc2910ede..b6d5f1bfa 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -77,7 +77,6 @@ export class CollectionSchemaView extends CollectionSubView {
             focus: emptyDocFunction,
             active: returnFalse,
             whenActiveChanged: emptyFunction,
-            borderRounding: this.props.borderRounding,
             PanelHeight: returnZero,
             PanelWidth: returnZero,
         };
@@ -278,7 +277,6 @@ export class CollectionSchemaView extends CollectionSubView {
                         focus={emptyDocFunction}
                         parentActive={this.props.active}
                         whenActiveChanged={this.props.whenActiveChanged}
-                        borderRounding={this.props.borderRounding}
                     />
                 </div>
                 <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index bf59bec95..f9fc7be5a 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -25,7 +25,6 @@ export interface CollectionViewProps extends FieldViewProps {
     moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
     PanelWidth: () => number;
     PanelHeight: () => number;
-    borderRounding: () => number;
 }
 
 export interface SubCollectionViewProps extends CollectionViewProps {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b9d1f0bd9..905b48db7 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -137,7 +137,7 @@ export class CollectionTreeView extends CollectionSubView {
 
         return (
             <div id="body" className="collectionTreeView-dropTarget"
-                style={{ borderRadius: `${this.props.borderRounding()}px` }}
+                style={{ borderRadius: "inherit" }}
                 onWheel={(e: React.WheelEvent) => e.stopPropagation()}
                 onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
                 <div className="coll-title">
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 9e2c9bf9a..56342c84c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -257,7 +257,6 @@ export class CollectionFreeFormView extends CollectionSubView {
             focus: this.focusDocument,
             parentActive: this.props.active,
             whenActiveChanged: this.props.active,
-            borderRounding: this.props.borderRounding,
         };
     }
 
@@ -289,17 +288,17 @@ export class CollectionFreeFormView extends CollectionSubView {
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         return (
             <div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
-                style={{ borderRadius: `${this.props.borderRounding()}px` }}
+                style={{ borderRadius: "inherit" }}
                 onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} >
                 {/* <svg viewBox="0 0 180 18" style={{ top: "50%", opacity: 0.05, position: "absolute" }}>
                     <text y="15" >
                         {this.props.Document.Title}
                     </text>
                 </svg> */}
-                <MarqueeView container={this} borderRadius={this.props.borderRounding} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
+                <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
                     addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox}
                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
-                    <CollectionFreeFormViewPannableContents borderRadius={this.props.borderRounding} centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
+                    <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
                         zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
                         <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
                             <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
@@ -347,7 +346,6 @@ interface CollectionFreeFormViewPannableContentsProps {
     panX: () => number;
     panY: () => number;
     zoomScaling: () => number;
-    borderRadius: () => number;
 }
 
 @observer
@@ -358,7 +356,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
         const panx = -this.props.panX();
         const pany = -this.props.panY();
         const zoom = this.props.zoomScaling();// needs to be a variable outside of the <Measure> otherwise, reactions won't fire
-        return <div className="collectionfreeformview" style={{ borderRadius: `${this.props.borderRadius()}px`, transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
+        return <div className="collectionfreeformview" style={{ borderRadius: "inherit", transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
             {this.props.children}
         </div>;
     }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index e724ab47e..b511a6e3d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -24,7 +24,6 @@ interface MarqueeViewProps {
     selectDocuments: (docs: Document[]) => void;
     removeDocument: (doc: Document) => boolean;
     addLiveTextDocument: (doc: Document) => void;
-    borderRadius: () => number;
 }
 
 @observer
@@ -251,7 +250,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     }
 
     render() {
-        return <div className="marqueeView" style={{ borderRadius: `${this.props.borderRadius()}px` }} onClick={this.onClick} onPointerDown={this.onPointerDown}>
+        return <div className="marqueeView" style={{ borderRadius: "inherit" }} onClick={this.onClick} onPointerDown={this.onPointerDown}>
             {this.props.children}
             {!this._visible ? (null) : this.marqueeDiv}
         </div>;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 8caf5ff31..4537ce3eb 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -65,7 +65,6 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
             ScreenToLocalTransform={this.getTransform}
             PanelWidth={this.panelWidth}
             PanelHeight={this.panelHeight}
-            borderRounding={this.borderRounding}
         />;
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ddf99c109..cec6e6468 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -41,7 +41,6 @@ export interface DocumentViewProps {
     parentActive: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     toggleMinimized: () => void;
-    borderRounding: () => number;
 }
 export interface JsxArgs extends DocumentViewProps {
     Keys: { [name: string]: Key };
@@ -278,7 +277,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             <div className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
                 style={{
-                    borderRadius: `${this.props.borderRounding()}px`,
+                    borderRadius: "inherit",
                     background: this.props.Document.GetText(KeyStore.BackgroundColor, ""),
                     width: nativeWidth, height: nativeHeight,
                     transform: `scale(${scaling}, ${scaling})`
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 8484194b0..8f360754e 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -49,7 +49,6 @@ export interface FieldViewProps {
     focus: (doc: Document) => void;
     PanelWidth: () => number;
     PanelHeight: () => number;
-    borderRounding: () => number;
 }
 
 @observer
@@ -103,7 +102,6 @@ export class FieldView extends React.Component<FieldViewProps> {
                     layoutKey={KeyStore.Layout}
                     ContainingCollectionView={this.props.ContainingCollectionView}
                     parentActive={this.props.active}
-                    borderRounding={returnZero}
                     toggleMinimized={emptyFunction}
                     whenActiveChanged={this.props.whenActiveChanged} />
             );
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 4b2fdbaff..5b17763e7 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -274,7 +274,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         return (
             <div className={`formattedTextBox-cont-${style}`}
                 style={{
-                    borderRadius: `${this.props.borderRounding()}px`,
+                    borderRadius: "inherit",
                     background: color,
                 }}
                 onKeyDown={this.onKeyPress}
-- 
cgit v1.2.3-70-g09d2


From b742c8850b184408fd61d8571fa68ecf01386141 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 26 Apr 2019 00:08:21 -0400
Subject: fixed some things with pdfs

---
 .../views/collections/CollectionPDFView.scss       |  26 ++-
 src/client/views/collections/CollectionPDFView.tsx |  34 +++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   8 +-
 src/client/views/nodes/FieldView.tsx               |   2 +-
 src/client/views/nodes/PDFBox.scss                 |  12 ++
 src/client/views/nodes/PDFBox.tsx                  | 215 +++------------------
 src/client/views/nodes/Sticky.tsx                  |  83 --------
 7 files changed, 95 insertions(+), 285 deletions(-)
 delete mode 100644 src/client/views/nodes/Sticky.tsx

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

diff --git a/src/client/views/collections/CollectionPDFView.scss b/src/client/views/collections/CollectionPDFView.scss
index 0eca3f1cd..f6fb79582 100644
--- a/src/client/views/collections/CollectionPDFView.scss
+++ b/src/client/views/collections/CollectionPDFView.scss
@@ -1,20 +1,39 @@
 .collectionPdfView-buttonTray {
-    top : 25px;
+    top : 15px;
     left : 20px;
     position: relative; 
     transform-origin: left top;
     position: absolute;
 }
+.collectionPdfView-thumb {
+    width:25px;
+    height:25px;
+    transform-origin: left top;
+    position: absolute;
+    background: darkgray;
+}
+.collectionPdfView-slider {
+    width:25px;
+    height:25px;
+    transform-origin: left top;
+    position: absolute;
+    background: lightgray;
+}
 .collectionPdfView-cont{
     width: 100%;
     height: 100%;
     position: absolute; 
     top: 0;
     left:0;
-    
+}
+.collectionPdfView-cont-dragging {
+    span {
+        user-select: none;
+    }
 }
 .collectionPdfView-backward {
     color : white;
+    font-size: 24px;
     top :0px;
     left : 0px;
     position: absolute;
@@ -22,8 +41,9 @@
 }
 .collectionPdfView-forward {
     color : white;
+    font-size: 24px;
     top :0px;
-    left : 35px;
+    left : 45px;
     position: absolute;
     background-color: rgba(50, 50, 50, 0.2);
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 229bc4059..497c3ee3c 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -1,4 +1,4 @@
-import { action } from "mobx";
+import { action, observable } from "mobx";
 import { observer } from "mobx-react";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ContextMenu } from "../ContextMenu";
@@ -16,18 +16,44 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
     public static LayoutString(fieldKey: string = "DataKey") {
         return FieldView.LayoutString(CollectionPDFView, fieldKey);
     }
+    @observable _inThumb = false;
 
     private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, -1); }
+    private set curPage(value: number) { this.props.Document.SetNumber(KeyStore.CurPage, value); }
     private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); }
     @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : -1;
     @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : -1;
 
+    @action
+    onThumbDown = (e: React.PointerEvent) => {
+        document.addEventListener("pointermove", this.onThumbMove, false);
+        document.addEventListener("pointerup", this.onThumbUp, false);
+        e.stopPropagation();
+        this._inThumb = true;
+    }
+    @action
+    onThumbMove = (e: PointerEvent) => {
+        let pso = (e.clientY - (e as any).target.parentElement.getBoundingClientRect().top) / (e as any).target.parentElement.getBoundingClientRect().height;
+        this.curPage = Math.trunc(Math.min(this.numPages, pso * this.numPages + 1));
+        e.stopPropagation();
+    }
+    @action
+    onThumbUp = (e: PointerEvent) => {
+        this._inThumb = false;
+        document.removeEventListener("pointermove", this.onThumbMove);
+        document.removeEventListener("pointerup", this.onThumbUp);
+    }
+    nativeWidth = () => this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
+    nativeHeight = () => this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
     private get uIButtons() {
-        let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
+        let ratio = (this.curPage - 1) / this.numPages * 100;
         return (
-            <div className="collectionPdfView-buttonTray" key="tray" style={{ transform: `scale(${scaling}, ${scaling})` }}>
+            <div className="collectionPdfView-buttonTray" key="tray" style={{ height: "100%" }}>
                 <button className="collectionPdfView-backward" onClick={this.onPageBack}>{"<"}</button>
                 <button className="collectionPdfView-forward" onClick={this.onPageForward}>{">"}</button>
+                <div className="collectionPdfView-slider" onPointerDown={this.onThumbDown} style={{ top: 60, left: -20, width: 50, height: `calc(100% - 80px)` }} >
+                    <div className="collectionPdfView-thumb" onPointerDown={this.onThumbDown} style={{ top: `${ratio}%`, width: 50, height: 50 }} />
+                </div>
             </div>
         );
     }
@@ -50,7 +76,7 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
 
     render() {
         return (
-            <CollectionBaseView {...this.props} className="collectionPdfView-cont" onContextMenu={this.onContextMenu}>
+            <CollectionBaseView {...this.props} className={`collectionPdfView-cont${this._inThumb ? "-dragging" : ""}`} onContextMenu={this.onContextMenu}>
                 {this.subView}
             </CollectionBaseView>
         );
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 56342c84c..405594eb1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -283,7 +283,7 @@ export class CollectionFreeFormView extends CollectionSubView {
         super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
     }
 
-    private childViews = () => [...this.views, <CollectionFreeFormBackgroundView key="backgroundView" {...this.getDocumentViewProps(this.props.Document)} />];
+    private childViews = () => [...this.views, <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />];
     render() {
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         return (
@@ -307,7 +307,7 @@ export class CollectionFreeFormView extends CollectionSubView {
                         </CollectionFreeFormLinksView>
                         <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
                     </CollectionFreeFormViewPannableContents>
-                    <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} />
+                    <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} {...this.props} />
                 </MarqueeView>
             </div>
         );
@@ -328,12 +328,12 @@ class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> {
 }
 
 @observer
-class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps> {
+class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
     @computed get backgroundView() {
         let backgroundLayout = this.props.Document.GetText(KeyStore.BackgroundLayout, "");
         return !backgroundLayout ? (null) :
             (<DocumentContentsView {...this.props} layoutKey={KeyStore.BackgroundLayout}
-                isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
+                isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
     }
     render() {
         return this.backgroundView;
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 8f360754e..9e175b0d1 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -97,7 +97,7 @@ export class FieldView extends React.Component<FieldViewProps> {
                     isTopMost={true} //TODO Why is this top most?
                     selectOnLoad={false}
                     focus={emptyDocFunction}
-                    isSelected={returnFalse}
+                    isSelected={this.props.isSelected}
                     select={returnFalse}
                     layoutKey={KeyStore.Layout}
                     ContainingCollectionView={this.props.ContainingCollectionView}
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 830dfe6c6..87fdd720a 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -12,6 +12,18 @@
     top: 0;
     left:0;
     z-index: 25;
+    pointer-events: all;
+}
+.pdfButton {
+    pointer-events: all;
+    width: 100px;
+    height:100px;
+}
+.pdfBox-cont {
+    pointer-events: none;
+}
+.pdfBox-cont-interactive {
+    pointer-events: all;
 }
 .pdfBox-contentContainer {
     position: absolute;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 81ceb37f6..0525d353e 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,5 +1,5 @@
 import * as htmlToImage from "html-to-image";
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, Reaction, trace } from 'mobx';
 import { observer } from "mobx-react";
 import 'react-image-lightbox/style.css';
 import Measure from "react-measure";
@@ -14,9 +14,7 @@ import { RouteStore } from "../../../server/RouteStore";
 import { Utils } from '../../../Utils';
 import { Annotation } from './Annotation';
 import { FieldView, FieldViewProps } from './FieldView';
-import "./ImageBox.scss";
 import "./PDFBox.scss";
-import { Sticky } from './Sticky'; //you should look at sticky and annotation, because they are used here
 import React = require("react");
 import { SelectionManager } from "../../util/SelectionManager";
 
@@ -40,15 +38,6 @@ import { SelectionManager } from "../../util/SelectionManager";
  *          4) another method: click on highlight first and then drag on your desired text
  *          5) To make another highlight, you need to reclick on the button 
  * 
- * Draw:
- *          1) click draw and select color. then just draw like there's no tomorrow.
- *          2) once you finish drawing your masterpiece, just reclick on the draw button to end your drawing session. 
- * 
- * Pagination:
- *          1) click on arrows. You'll notice that stickies will stay in those page. But... highlights won't. 
- *          2) to test this out, make few area/stickies and then click on next page then come back. You'll see that they are all saved. 
- *
- * 
  * written by: Andrew Kim 
  */
 @observer
@@ -56,30 +45,9 @@ export class PDFBox extends React.Component<FieldViewProps> {
     public static LayoutString() { return FieldView.LayoutString(PDFBox); }
 
     private _mainDiv = React.createRef<HTMLDivElement>();
-    private _pdf = React.createRef<HTMLCanvasElement>();
 
     @observable private _renderAsSvg = true;
 
-    //very useful for keeping track of X and y position throughout the PDF Canvas
-    private initX: number = 0;
-    private initY: number = 0;
-
-    //checks if tool is on
-    private _toolOn: boolean = false; //checks if tool is on
-    private _pdfContext: any = null; //gets pdf context
-    private bool: Boolean = false; //general boolean debounce
-    private currSpan: any;//keeps track of current span (for highlighting)
-
-    private _currTool: any; //keeps track of current tool button reference
-    private _drawToolOn: boolean = false; //boolean that keeps track of the drawing tool 
-    private _drawTool = React.createRef<HTMLButtonElement>();//drawing tool button reference
-
-    private _colorTool = React.createRef<HTMLButtonElement>(); //color button reference
-    private _currColor: string = "black"; //current color that user selected (for ink/pen)
-
-    private _highlightTool = React.createRef<HTMLButtonElement>(); //highlighter button reference
-    private _highlightToolOn: boolean = false;
-    private _pdfCanvas: any;
     private _reactionDisposer: Opt<IReactionDisposer>;
 
     @observable private _perPageInfo: Object[] = []; //stores pageInfo
@@ -111,43 +79,6 @@ export class PDFBox extends React.Component<FieldViewProps> {
         }
     }
 
-    /**
-     * selection tool used for area highlighting (stickies). Kinda temporary
-     */
-    selectionTool = () => {
-        this._toolOn = true;
-    }
-    /**
-     * when user draws on the canvas. When mouse pointer is down 
-     */
-    drawDown = (e: PointerEvent) => {
-        this.initX = e.offsetX;
-        this.initY = e.offsetY;
-        this._pdfContext.beginPath();
-        this._pdfContext.lineTo(this.initX, this.initY);
-        this._pdfContext.strokeStyle = this._currColor;
-        this._pdfCanvas.addEventListener("pointermove", this.drawMove);
-        this._pdfCanvas.addEventListener("pointerup", this.drawUp);
-
-    }
-    //when user drags 
-    drawMove = (e: PointerEvent): void => {
-        //x and y mouse movement
-        let x = this.initX += e.movementX,
-            y = this.initY += e.movementY;
-        //connects the point 
-        this._pdfContext.lineTo(x, y);
-        this._pdfContext.stroke();
-    }
-
-    drawUp = (e: PointerEvent) => {
-        this._pdfContext.closePath();
-        this._pdfCanvas.removeEventListener("pointermove", this.drawMove);
-        this._pdfCanvas.removeEventListener("pointerdown", this.drawDown);
-        this._pdfCanvas.addEventListener("pointerdown", this.drawDown);
-    }
-
-
     /**
      * highlighting helper function
      */
@@ -272,11 +203,8 @@ export class PDFBox extends React.Component<FieldViewProps> {
      * controls the area highlighting (stickies) Kinda temporary
      */
     onPointerDown = (e: React.PointerEvent) => {
-        if (this._toolOn) {
-            let mouse = e.nativeEvent;
-            this.initX = mouse.offsetX;
-            this.initY = mouse.offsetY;
-
+        if (this.props.isSelected()) {
+            e.stopPropagation();
         }
     }
 
@@ -285,95 +213,12 @@ export class PDFBox extends React.Component<FieldViewProps> {
      */
     @action
     onPointerUp = (e: React.PointerEvent) => {
-        if (this._highlightToolOn) {
+        if (this.props.isSelected()) {
             this.highlight("rgba(76, 175, 80, 0.3)"); //highlights to this default color. 
-            this._highlightToolOn = false;
-        }
-        if (this._toolOn) {
-            let mouse = e.nativeEvent;
-            let finalX = mouse.offsetX;
-            let finalY = mouse.offsetY;
-            let width = Math.abs(finalX - this.initX); //width
-            let height = Math.abs(finalY - this.initY); //height
-
-            //these two if statements are bidirectional dragging. You can drag from any point to another point and generate sticky
-            if (finalX < this.initX) {
-                this.initX = finalX;
-            }
-            if (finalY < this.initY) {
-                this.initY = finalY;
-            }
-
-            if (this._mainDiv.current) {
-                let sticky = <Sticky key={Utils.GenerateGuid()} Height={height} Width={width} X={this.initX} Y={this.initY} />;
-                this._pageInfo.area.push(sticky);
-            }
-            this._toolOn = false;
         }
         this._interactive = true;
     }
 
-    /**
-     * starts drawing the line when user presses down. 
-     */
-    onDraw = () => {
-        if (this._currTool !== null) {
-            this._currTool.style.backgroundColor = "grey";
-        }
-
-        if (this._drawTool.current) {
-            this._currTool = this._drawTool.current;
-            if (this._drawToolOn) {
-                this._drawToolOn = false;
-                this._pdfCanvas.removeEventListener("pointerdown", this.drawDown);
-                this._pdfCanvas.removeEventListener("pointerup", this.drawUp);
-                this._pdfCanvas.removeEventListener("pointermove", this.drawMove);
-                this._drawTool.current.style.backgroundColor = "grey";
-            } else {
-                this._drawToolOn = true;
-                this._pdfCanvas.addEventListener("pointerdown", this.drawDown);
-                this._drawTool.current.style.backgroundColor = "cyan";
-            }
-        }
-    }
-
-
-    /**
-     * for changing color (for ink/pen)
-     */
-    onColorChange = (e: React.PointerEvent) => {
-        if (e.currentTarget.innerHTML === "Red") {
-            this._currColor = "red";
-        } else if (e.currentTarget.innerHTML === "Blue") {
-            this._currColor = "blue";
-        } else if (e.currentTarget.innerHTML === "Green") {
-            this._currColor = "green";
-        } else if (e.currentTarget.innerHTML === "Black") {
-            this._currColor = "black";
-        }
-
-    }
-
-
-    /**
-     * For highlighting (text drag highlighting)
-     */
-    onHighlight = () => {
-        this._drawToolOn = false;
-        if (this._currTool !== null) {
-            this._currTool.style.backgroundColor = "grey";
-        }
-        if (this._highlightTool.current) {
-            this._currTool = this._drawTool.current;
-            if (this._highlightToolOn) {
-                this._highlightToolOn = false;
-                this._highlightTool.current.style.backgroundColor = "grey";
-            } else {
-                this._highlightToolOn = true;
-                this._highlightTool.current.style.backgroundColor = "orange";
-            }
-        }
-    }
 
 
     @action
@@ -397,22 +242,6 @@ export class PDFBox extends React.Component<FieldViewProps> {
 
     @action
     onLoaded = (page: any) => {
-        if (this._mainDiv.current) {
-            this._mainDiv.current.childNodes.forEach((element) => {
-                if (element.nodeName === "DIV") {
-                    element.childNodes[0].childNodes.forEach((e) => {
-
-                        if (e instanceof HTMLCanvasElement) {
-                            this._pdfCanvas = e;
-                            this._pdfContext = e.getContext("2d");
-
-                        }
-
-                    });
-                }
-            });
-        }
-
         // bcz: the number of pages should really be set when the document is imported.
         this.props.Document.SetNumber(KeyStore.NumPages, page._transport.numPages);
         if (this._perPageInfo.length === 0) { //Makes sure it only runs once
@@ -424,7 +253,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
     @action
     setScaling = (r: any) => {
         // bcz: the nativeHeight should really be set when the document is imported.
-        //      also, the native dimensions could be different for different pages of the PDF
+        //      also, the native dimensions could be different for different pages of the canvas
         //      so this design is flawed.
         var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
         if (!this.props.Document.GetNumber(KeyStore.NativeHeight, 0)) {
@@ -433,22 +262,27 @@ export class PDFBox extends React.Component<FieldViewProps> {
             this.props.Document.SetNumber(KeyStore.NativeHeight, nativeHeight);
         }
     }
-
+    renderHeight = 2400;
+    @computed
+    get pdfPage() {
+        return <Page height={this.renderHeight} pageNumber={this.curPage} onLoadSuccess={this.onLoaded} />
+    }
     @computed
     get pdfContent() {
-        let page = this.curPage;
-        const renderHeight = 2400;
         let pdfUrl = this.props.Document.GetT(this.props.fieldKey, PDFField);
-        let xf = this.props.Document.GetNumber(KeyStore.NativeHeight, 0) / renderHeight;
+        let xf = this.props.Document.GetNumber(KeyStore.NativeHeight, 0) / this.renderHeight;
+        let body = (this.props.Document.GetNumber(KeyStore.NativeHeight, 0)) ?
+            this.pdfPage :
+            <Measure onResize={this.setScaling}>
+                {({ measureRef }) =>
+                    <div className="pdfBox-page" ref={measureRef}>
+                        {this.pdfPage}
+                    </div>
+                }
+            </Measure>;
         return <div className="pdfBox-contentContainer" key="container" style={{ transform: `scale(${xf}, ${xf})` }}>
-            <Document file={window.origin + RouteStore.corsProxy + `/${pdfUrl}`} renderMode={this._renderAsSvg ? "svg" : ""}>
-                <Measure onResize={this.setScaling}>
-                    {({ measureRef }) =>
-                        <div className="pdfBox-page" ref={measureRef}>
-                            <Page height={renderHeight} pageNumber={page} onLoadSuccess={this.onLoaded} />
-                        </div>
-                    }
-                </Measure>
+            <Document file={window.origin + RouteStore.corsProxy + `/${pdfUrl}`} renderMode={this._renderAsSvg ? "svg" : "canvas"}>
+                {body}
             </Document>
         </div >;
     }
@@ -478,10 +312,11 @@ export class PDFBox extends React.Component<FieldViewProps> {
         }
         return (null);
     }
-
     render() {
+        trace();
+        let classname = "pdfBox-cont" + (this.props.isSelected() ? "-interactive" : "");
         return (
-            <div className="pdfBox-cont" ref={this._mainDiv} onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} >
+            <div className={classname} ref={this._mainDiv} onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} >
                 {this.pdfRenderer}
             </div >
         );
diff --git a/src/client/views/nodes/Sticky.tsx b/src/client/views/nodes/Sticky.tsx
deleted file mode 100644
index 11719831b..000000000
--- a/src/client/views/nodes/Sticky.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import "react-image-lightbox/style.css"; // This only needs to be imported once in your app
-import React = require("react");
-import { observer } from "mobx-react";
-import "react-pdf/dist/Page/AnnotationLayer.css";
-
-interface IProps {
-  Height: number;
-  Width: number;
-  X: number;
-  Y: number;
-}
-
-/**
- * Sticky, also known as area highlighting, is used to highlight large selection of the PDF file.
- * Improvements that could be made: maybe store line array and store that somewhere for future rerendering.
- *
- * Written By: Andrew Kim
- */
-@observer
-export class Sticky extends React.Component<IProps> {
-  private initX: number = 0;
-  private initY: number = 0;
-
-  private _ref = React.createRef<HTMLCanvasElement>();
-  private ctx: any; //context that keeps track of sticky canvas
-
-  /**
-   * drawing. Registers the first point that user clicks when mouse button is pressed down on canvas
-   */
-  drawDown = (e: React.PointerEvent) => {
-    if (this._ref.current) {
-      this.ctx = this._ref.current.getContext("2d");
-      let mouse = e.nativeEvent;
-      this.initX = mouse.offsetX;
-      this.initY = mouse.offsetY;
-      this.ctx.beginPath();
-      this.ctx.lineTo(this.initX, this.initY);
-      this.ctx.strokeStyle = "black";
-      document.addEventListener("pointermove", this.drawMove);
-      document.addEventListener("pointerup", this.drawUp);
-    }
-  }
-
-  //when user drags
-  drawMove = (e: PointerEvent): void => {
-    //x and y mouse movement
-    let x = (this.initX += e.movementX),
-      y = (this.initY += e.movementY);
-    //connects the point
-    this.ctx.lineTo(x, y);
-    this.ctx.stroke();
-  }
-
-  /**
-   * when user lifts the mouse, the drawing ends
-   */
-  drawUp = (e: PointerEvent) => {
-    this.ctx.closePath();
-    console.log(this.ctx);
-    document.removeEventListener("pointermove", this.drawMove);
-  }
-
-  render() {
-    return (
-      <div onPointerDown={this.drawDown}>
-        <canvas
-          ref={this._ref}
-          height={this.props.Height}
-          width={this.props.Width}
-          style={{
-            position: "absolute",
-            top: "20px",
-            left: "0px",
-            zIndex: 1,
-            background: "yellow",
-            transform: `translate(${this.props.X}px, ${this.props.Y}px)`,
-            opacity: 0.4
-          }}
-        />
-      </div>
-    );
-  }
-}
-- 
cgit v1.2.3-70-g09d2


From d4a77dd055685dd81a762ef40e0c3b7606586e9c Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sat, 27 Apr 2019 21:40:17 -0400
Subject: Split more files up

---
 src/client/DocServer.ts                            |  3 +-
 src/client/northstar/dash-fields/HistogramField.ts |  2 +-
 src/client/northstar/dash-nodes/HistogramBox.tsx   |  3 +-
 src/client/util/Scripting.ts                       |  1 +
 src/client/views/Main.tsx                          |  3 +-
 .../views/collections/CollectionBaseView.tsx       |  3 +-
 .../views/collections/CollectionDockingView.tsx    |  3 +-
 .../views/collections/CollectionSchemaView.tsx     |  3 +-
 src/client/views/collections/CollectionSubView.tsx |  3 +-
 .../views/collections/CollectionTreeView.tsx       |  3 +-
 .../CollectionFreeFormLinksView.tsx                |  3 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  3 +-
 src/client/views/nodes/DocumentView.tsx            |  3 +-
 src/client/views/nodes/FieldView.tsx               |  1 +
 src/client/views/nodes/LinkMenu.tsx                |  3 +-
 src/new_fields/Doc.ts                              | 37 +++-------------------
 src/new_fields/HtmlField.ts                        |  2 +-
 src/new_fields/IconField.ts                        |  2 +-
 src/new_fields/InkField.ts                         |  2 +-
 src/new_fields/List.ts                             |  3 +-
 src/new_fields/ObjectField.ts                      | 17 ++++++++++
 src/new_fields/Proxy.ts                            |  4 ++-
 src/new_fields/RefField.ts                         | 18 +++++++++++
 src/new_fields/RichTextField.ts                    |  2 +-
 src/new_fields/Schema.ts                           |  8 ++---
 src/new_fields/Types.ts                            |  2 +-
 src/new_fields/URLField.ts                         |  2 +-
 src/new_fields/util.ts                             |  4 ++-
 .../authentication/controllers/WorkspacesMenu.tsx  |  3 +-
 src/server/database.ts                             |  2 ++
 30 files changed, 88 insertions(+), 60 deletions(-)
 create mode 100644 src/new_fields/ObjectField.ts
 create mode 100644 src/new_fields/RefField.ts

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

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 3f17baec6..c7cbfce37 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,8 +1,9 @@
 import * as OpenSocket from 'socket.io-client';
 import { MessageStore, Types, Message } from "./../server/Message";
-import { Opt, FieldWaiting, RefField, HandleUpdate } from '../new_fields/Doc';
+import { Opt, FieldWaiting } from '../new_fields/Doc';
 import { Utils } from '../Utils';
 import { SerializationHelper } from './util/SerializationHelper';
+import { RefField, HandleUpdate } from '../new_fields/RefField';
 
 export namespace DocServer {
     const _cache: { [id: string]: RefField | Promise<Opt<RefField>> } = {};
diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts
index 118f4cf7f..730289536 100644
--- a/src/client/northstar/dash-fields/HistogramField.ts
+++ b/src/client/northstar/dash-fields/HistogramField.ts
@@ -3,7 +3,7 @@ import { custom, serializable } from "serializr";
 import { ColumnAttributeModel } from "../../../client/northstar/core/attribute/AttributeModel";
 import { AttributeTransformationModel } from "../../../client/northstar/core/attribute/AttributeTransformationModel";
 import { HistogramOperation } from "../../../client/northstar/operations/HistogramOperation";
-import { ObjectField } from "../../../new_fields/Doc";
+import { ObjectField } from "../../../new_fields/ObjectField";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { OmitKeys } from "../../../Utils";
 import { Deserializable } from "../../util/SerializationHelper";
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 4a65b14cf..a9c68ccba 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -20,7 +20,8 @@ import { HistogramLabelPrimitives } from "./HistogramLabelPrimitives";
 import { StyleConstants } from "../utils/StyleContants";
 import { NumCast, Cast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
-import { Doc, Id } from "../../../new_fields/Doc";
+import { Doc } from "../../../new_fields/Doc";
+import { Id } from "../../../new_fields/RefField";
 
 
 @observer
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index dbec82340..e45f61c11 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -11,6 +11,7 @@ import { Docs } from "../documents/Documents";
 import { Doc, Field } from '../../new_fields/Doc';
 import { ImageField, PdfField, VideoField, AudioField } from '../../new_fields/URLField';
 import { List } from '../../new_fields/List';
+import { RichTextField } from '../../new_fields/RichTextField';
 
 export interface ScriptSucccess {
     success: true;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 1e3d4e259..4a68d1c68 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -33,10 +33,11 @@ import { MainOverlayTextBox } from './MainOverlayTextBox';
 import { DocumentView } from './nodes/DocumentView';
 import { PreviewCursor } from './PreviewCursor';
 import { SelectionManager } from '../util/SelectionManager';
-import { FieldResult, Field, Doc, Id, Opt } from '../../new_fields/Doc';
+import { FieldResult, Field, Doc, Opt } from '../../new_fields/Doc';
 import { Cast, FieldValue, StrCast } from '../../new_fields/Types';
 import { DocServer } from '../DocServer';
 import { listSpec } from '../../new_fields/Schema';
+import { Id } from '../../new_fields/RefField';
 
 
 @observer
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 4807dc40a..b2fba1415 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -4,9 +4,10 @@ import * as React from 'react';
 import { ContextMenu } from '../ContextMenu';
 import { FieldViewProps } from '../nodes/FieldView';
 import { Cast, FieldValue, PromiseValue, NumCast } from '../../../new_fields/Types';
-import { Doc, FieldResult, Opt, Id } from '../../../new_fields/Doc';
+import { Doc, FieldResult, Opt } from '../../../new_fields/Doc';
 import { listSpec } from '../../../new_fields/Schema';
 import { List } from '../../../new_fields/List';
+import { Id } from '../../../new_fields/RefField';
 
 export enum CollectionViewType {
     Invalid,
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 2ff409b9b..1574562c6 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -13,11 +13,12 @@ import React = require("react");
 import { SubCollectionViewProps } from "./CollectionSubView";
 import { DragManager, DragLinksAsDocuments } from "../../util/DragManager";
 import { Transform } from '../../util/Transform';
-import { Doc, Id, Opt, Field, FieldId } from "../../../new_fields/Doc";
+import { Doc, Opt, Field } from "../../../new_fields/Doc";
 import { Cast, NumCast } from "../../../new_fields/Types";
 import { List } from "../../../new_fields/List";
 import { DocServer } from "../../DocServer";
 import { listSpec } from "../../../new_fields/Schema";
+import { Id, FieldId } from "../../../new_fields/RefField";
 
 @observer
 export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 874170f3d..58d20819b 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -19,10 +19,11 @@ import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
 import { CollectionSubView } from "./CollectionSubView";
-import { Opt, Field, Doc, Id } from "../../../new_fields/Doc";
+import { Opt, Field, Doc } from "../../../new_fields/Doc";
 import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
 import { List } from "../../../new_fields/List";
+import { Id } from "../../../new_fields/RefField";
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 558a8728f..4d090b680 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -10,12 +10,13 @@ import * as rp from 'request-promise';
 import { CollectionView } from "./CollectionView";
 import { CollectionPDFView } from "./CollectionPDFView";
 import { CollectionVideoView } from "./CollectionVideoView";
-import { Doc, ObjectField, Opt } from "../../../new_fields/Doc";
+import { Doc, Opt } from "../../../new_fields/Doc";
 import { DocComponent } from "../DocComponent";
 import { listSpec } from "../../../new_fields/Schema";
 import { Cast, PromiseValue } from "../../../new_fields/Types";
 import { List } from "../../../new_fields/List";
 import { DocServer } from "../../DocServer";
+import { ObjectField } from "../../../new_fields/ObjectField";
 
 export interface CollectionViewProps extends FieldViewProps {
     addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index c9d8d83c8..7ec9a8549 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -10,7 +10,8 @@ import "./CollectionTreeView.scss";
 import React = require("react");
 import { Document, listSpec } from '../../../new_fields/Schema';
 import { Cast, StrCast, BoolCast } from '../../../new_fields/Types';
-import { Doc, Id } from '../../../new_fields/Doc';
+import { Doc } from '../../../new_fields/Doc';
+import { Id } from '../../../new_fields/RefField';
 
 
 export interface TreeViewProps {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index ce9995630..f693d55e8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -7,10 +7,11 @@ import { CollectionViewProps } from "../CollectionSubView";
 import "./CollectionFreeFormLinksView.scss";
 import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
 import React = require("react");
-import { Doc, Id } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../new_fields/Doc";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types";
 import { listSpec } from "../../../../new_fields/Schema";
 import { List } from "../../../../new_fields/List";
+import { Id } from "../../../../new_fields/RefField";
 
 @observer
 export class CollectionFreeFormLinksView extends React.Component<CollectionViewProps> {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 047fbad18..18107e98a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -19,10 +19,11 @@ import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
 import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Schema";
-import { Doc, Id } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../new_fields/Doc";
 import { FieldValue, Cast, NumCast } from "../../../../new_fields/Types";
 import { pageSchema } from "../../nodes/ImageBox";
 import { List } from "../../../../new_fields/List";
+import { Id } from "../../../../new_fields/RefField";
 
 export const panZoomSchema = createSchema({
     panX: "number",
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index aabc1633e..c304b6a35 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -15,7 +15,7 @@ import { ContextMenu } from "../ContextMenu";
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import React = require("react");
-import { Field, Opt, Doc, Id } from "../../../new_fields/Doc";
+import { Opt, Doc } from "../../../new_fields/Doc";
 import { DocComponent } from "../DocComponent";
 import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
 import { FieldValue, Cast, PromiseValue } from "../../../new_fields/Types";
@@ -24,6 +24,7 @@ import { CollectionFreeFormView } from "../collections/collectionFreeForm/Collec
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { MarqueeView } from "../collections/collectionFreeForm/MarqueeView";
 import { DocServer } from "../../DocServer";
+import { Id } from "../../../new_fields/RefField";
 
 const linkSchema = createSchema({
     title: "string",
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index c00c47fc4..dc36c5914 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -16,6 +16,7 @@ import { Opt, Doc, FieldResult } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
 import { ImageField, VideoField, AudioField } from "../../../new_fields/URLField";
 import { IconField } from "../../../new_fields/IconField";
+import { RichTextField } from "../../../new_fields/RichTextField";
 
 
 //
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
index 3ecc8555d..e21adebbc 100644
--- a/src/client/views/nodes/LinkMenu.tsx
+++ b/src/client/views/nodes/LinkMenu.tsx
@@ -5,9 +5,10 @@ import { LinkBox } from "./LinkBox";
 import { LinkEditor } from "./LinkEditor";
 import './LinkMenu.scss';
 import React = require("react");
-import { Doc, Id } from "../../../new_fields/Doc";
+import { Doc } from "../../../new_fields/Doc";
 import { Cast, FieldValue } from "../../../new_fields/Types";
 import { listSpec } from "../../../new_fields/Schema";
+import { Id } from "../../../new_fields/RefField";
 
 interface Props {
     docView: DocumentView;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index fb7b6e360..4ef2a465f 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -8,38 +8,8 @@ import { Cast, ToConstructor, PromiseValue, FieldValue } from "./Types";
 import { UndoManager, undoBatch } from "../client/util/UndoManager";
 import { listSpec } from "./Schema";
 import { List } from "./List";
-
-export type FieldId = string;
-export const HandleUpdate = Symbol("HandleUpdate");
-export const Id = Symbol("Id");
-export abstract class RefField {
-    @serializable(alias("id", primitive()))
-    private __id: FieldId;
-    readonly [Id]: FieldId;
-
-    constructor(id?: FieldId) {
-        this.__id = id || Utils.GenerateGuid();
-        this[Id] = this.__id;
-    }
-
-    protected [HandleUpdate]?(diff: any): void;
-}
-
-export const Update = Symbol("Update");
-export const OnUpdate = Symbol("OnUpdate");
-export const Parent = Symbol("Parent");
-export class ObjectField {
-    protected [OnUpdate]?: (diff?: any) => void;
-    private [Parent]?: Doc;
-    readonly [Id] = "";
-}
-
-export namespace ObjectField {
-    export function MakeCopy(field: ObjectField) {
-        //TODO Types
-        return field;
-    }
-}
+import { ObjectField } from "./ObjectField";
+import { RefField, FieldId, Id } from "./RefField";
 
 export function IsField(field: any): field is Field {
     return (typeof field === "string")
@@ -53,6 +23,7 @@ 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");
 
 @Deserializable("doc").withFields(["id"])
@@ -161,7 +132,7 @@ export namespace Doc {
                     copy[key] = field;
                 }
             }
-        })
+        });
         return copy;
     }
 
diff --git a/src/new_fields/HtmlField.ts b/src/new_fields/HtmlField.ts
index 76fdb1f62..808a3499b 100644
--- a/src/new_fields/HtmlField.ts
+++ b/src/new_fields/HtmlField.ts
@@ -1,6 +1,6 @@
 import { Deserializable } from "../client/util/SerializationHelper";
 import { serializable, primitive } from "serializr";
-import { ObjectField } from "./Doc";
+import { ObjectField } from "./ObjectField";
 
 @Deserializable("html")
 export class HtmlField extends ObjectField {
diff --git a/src/new_fields/IconField.ts b/src/new_fields/IconField.ts
index 32f3aa4d5..46f111f8e 100644
--- a/src/new_fields/IconField.ts
+++ b/src/new_fields/IconField.ts
@@ -1,6 +1,6 @@
 import { Deserializable } from "../client/util/SerializationHelper";
 import { serializable, primitive } from "serializr";
-import { ObjectField } from "./Doc";
+import { ObjectField } from "./ObjectField";
 
 @Deserializable("icon")
 export class IconField extends ObjectField {
diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts
index 49e6bf61e..42223c494 100644
--- a/src/new_fields/InkField.ts
+++ b/src/new_fields/InkField.ts
@@ -1,6 +1,6 @@
 import { Deserializable } from "../client/util/SerializationHelper";
 import { serializable, custom, createSimpleSchema, list, object, map } from "serializr";
-import { ObjectField } from "./Doc";
+import { ObjectField } from "./ObjectField";
 
 export enum InkTool {
     None,
diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts
index f01ac210a..428f661c9 100644
--- a/src/new_fields/List.ts
+++ b/src/new_fields/List.ts
@@ -1,8 +1,9 @@
 import { Deserializable, autoObject } from "../client/util/SerializationHelper";
-import { Field, ObjectField, Update, OnUpdate, Self } from "./Doc";
+import { Field, Update, Self } from "./Doc";
 import { setter, getter } from "./util";
 import { serializable, alias, list } from "serializr";
 import { observable } from "mobx";
+import { ObjectField, OnUpdate } from "./ObjectField";
 
 @Deserializable("list")
 class ListImpl<T extends Field> extends ObjectField {
diff --git a/src/new_fields/ObjectField.ts b/src/new_fields/ObjectField.ts
new file mode 100644
index 000000000..9cac2c528
--- /dev/null
+++ b/src/new_fields/ObjectField.ts
@@ -0,0 +1,17 @@
+import { Doc } from "./Doc";
+
+export const OnUpdate = Symbol("OnUpdate");
+export const Parent = Symbol("Parent");
+const Id = Symbol("Object Id");
+export class ObjectField {
+    protected [OnUpdate]?: (diff?: any) => void;
+    private [Parent]?: Doc;
+    readonly [Id] = "";
+}
+
+export namespace ObjectField {
+    export function MakeCopy(field: ObjectField) {
+        //TODO Types
+        return field;
+    }
+}
diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts
index 2aa78731e..56e41cc0f 100644
--- a/src/new_fields/Proxy.ts
+++ b/src/new_fields/Proxy.ts
@@ -1,8 +1,10 @@
 import { Deserializable } from "../client/util/SerializationHelper";
-import { RefField, Id, ObjectField, FieldWaiting } from "./Doc";
+import { FieldWaiting } from "./Doc";
 import { primitive, serializable } from "serializr";
 import { observable, action } from "mobx";
 import { DocServer } from "../client/DocServer";
+import { RefField, Id } from "./RefField";
+import { ObjectField } from "./ObjectField";
 
 @Deserializable("proxy")
 export class ProxyField<T extends RefField> extends ObjectField {
diff --git a/src/new_fields/RefField.ts b/src/new_fields/RefField.ts
new file mode 100644
index 000000000..202c65f21
--- /dev/null
+++ b/src/new_fields/RefField.ts
@@ -0,0 +1,18 @@
+import { serializable, primitive, alias } from "serializr";
+import { Utils } from "../Utils";
+
+export type FieldId = string;
+export const HandleUpdate = Symbol("HandleUpdate");
+export const Id = Symbol("Id");
+export abstract class RefField {
+    @serializable(alias("id", primitive()))
+    private __id: FieldId;
+    readonly [Id]: FieldId;
+
+    constructor(id?: FieldId) {
+        this.__id = id || Utils.GenerateGuid();
+        this[Id] = this.__id;
+    }
+
+    protected [HandleUpdate]?(diff: any): void;
+}
diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts
index 156e4efd9..0fa3cf73c 100644
--- a/src/new_fields/RichTextField.ts
+++ b/src/new_fields/RichTextField.ts
@@ -1,4 +1,4 @@
-import { ObjectField } from "./Doc";
+import { ObjectField } from "./ObjectField";
 import { serializable } from "serializr";
 
 export class RichTextField extends ObjectField {
diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts
index 5081521c7..7444878fe 100644
--- a/src/new_fields/Schema.ts
+++ b/src/new_fields/Schema.ts
@@ -21,15 +21,15 @@ export function makeInterface<T extends Interface[]>(...schemas: T): (doc?: Doc)
         }
     }
     const proto = new Proxy({}, {
-        get(target: any, prop) {
-            const field = target.doc[prop];
+        get(target: any, prop, receiver) {
+            const field = receiver.doc[prop];
             if (prop in schema) {
                 return Cast(field, (schema as any)[prop]);
             }
             return field;
         },
-        set(target: any, prop, value) {
-            target.doc[prop] = value;
+        set(target: any, prop, value, receiver) {
+            receiver.doc[prop] = value;
             return true;
         }
     });
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index 7fa18673f..3f8eabd45 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -1,4 +1,4 @@
-import { Field, Opt, FieldWaiting, FieldResult, RefField } from "./Doc";
+import { Field, Opt, FieldResult } from "./Doc";
 import { List } from "./List";
 
 export type ToType<T extends ToConstructor<Field> | ListSpec<Field>> =
diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts
index 1da245e73..95c679df7 100644
--- a/src/new_fields/URLField.ts
+++ b/src/new_fields/URLField.ts
@@ -1,6 +1,6 @@
 import { Deserializable } from "../client/util/SerializationHelper";
 import { serializable, custom } from "serializr";
-import { ObjectField } from "./Doc";
+import { ObjectField } from "./ObjectField";
 
 function url() {
     return custom(
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 2d9721b2e..011e8c8d9 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -1,8 +1,10 @@
 import { UndoManager } from "../client/util/UndoManager";
-import { Update, OnUpdate, Parent, ObjectField, RefField, Doc, Id, Field } from "./Doc";
+import { Update, Doc, Field } from "./Doc";
 import { SerializationHelper } from "../client/util/SerializationHelper";
 import { ProxyField } from "./Proxy";
 import { FieldValue } from "./Types";
+import { RefField, Id } from "./RefField";
+import { ObjectField, Parent, OnUpdate } from "./ObjectField";
 
 export function setter(target: any, prop: string | symbol | number, value: any, receiver: any): boolean {
     if (SerializationHelper.IsSerializing()) {
diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx
index 29327e5ad..91756315d 100644
--- a/src/server/authentication/controllers/WorkspacesMenu.tsx
+++ b/src/server/authentication/controllers/WorkspacesMenu.tsx
@@ -3,8 +3,9 @@ import { observable, action, configure, reaction, computed, ObservableMap, runIn
 import { observer } from "mobx-react";
 import './WorkspacesMenu.css';
 import { EditableView } from '../../../client/views/EditableView';
-import { Doc, Id } from '../../../new_fields/Doc';
+import { Doc } from '../../../new_fields/Doc';
 import { StrCast } from '../../../new_fields/Types';
+import { Id } from '../../../new_fields/RefField';
 
 export interface WorkspaceMenuProps {
     active: Doc | undefined;
diff --git a/src/server/database.ts b/src/server/database.ts
index a61b4d823..6b3b6797f 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -60,11 +60,13 @@ export class Database {
     }
 
     public getDocument(id: string, fn: (result?: Transferable) => void, collectionName = Database.DocumentsCollection) {
+        console.log("getDocument");
         this.db && this.db.collection(collectionName).findOne({ id: id }, (err, result) =>
             fn(result ? ({ id: result._id, type: result.type, data: result.data }) : undefined));
     }
 
     public getDocuments(ids: string[], fn: (result: Transferable[]) => void, collectionName = Database.DocumentsCollection) {
+        console.log("getDocuments");
         this.db && this.db.collection(collectionName).find({ id: { "$in": ids } }).toArray((err, docs) => {
             if (err) {
                 console.log(err.message);
-- 
cgit v1.2.3-70-g09d2


From 506af03831bf3cc002f93ad8708eafb909c0a194 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sat, 27 Apr 2019 22:27:26 -0400
Subject: Various fixes

---
 src/client/DocServer.ts                            | 24 ++++++++++++++++++----
 src/client/documents/Documents.ts                  |  2 +-
 src/client/northstar/dash-nodes/HistogramBox.tsx   |  2 +-
 src/client/views/collections/CollectionPDFView.tsx |  2 +-
 src/client/views/collections/CollectionSubView.tsx |  1 +
 .../views/collections/CollectionVideoView.tsx      |  2 +-
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/FieldView.tsx               |  4 ++--
 src/client/views/nodes/FormattedTextBox.tsx        |  2 +-
 src/client/views/nodes/KeyValueBox.tsx             |  2 +-
 src/new_fields/util.ts                             |  5 +++--
 src/server/database.ts                             | 21 +++++++++++++------
 src/server/index.ts                                | 10 +++++----
 14 files changed, 55 insertions(+), 26 deletions(-)

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

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index c7cbfce37..07997f072 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -55,12 +55,28 @@ export namespace DocServer {
                 map[id] = cached;
             }
         }
-        const prom = Utils.EmitCallback(_socket, MessageStore.GetFields, requestedIds);
-        requestedIds.map((id, index) => _cache[id] = prom.then((fields: RefField[]) => fields[index]));
+        const prom = Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds).then(fields => {
+            for (const key in fields) {
+                const field = fields[key];
+                if (field) {
+                    fields[key] = SerializationHelper.Deserialize(field);
+                }
+            }
+            return fields;
+        });
+        requestedIds.forEach((id, index) => _cache[id] = prom.then((fields: RefField[]) => fields[index]));
         const fields = await prom;
-        requestedIds.map((id, index) => map[id] = fields[index]);
+        requestedIds.forEach((id, index) => {
+            const field = fields[index];
+            if (field) {
+                _cache[id] = field;
+            } else {
+                delete _cache[id];
+            }
+            map[id] = field;
+        });
         const otherFields = await Promise.all(promises);
-        waitingIds.map((id, index) => map[id] = otherFields[index]);
+        waitingIds.forEach((id, index) => map[id] = otherFields[index]);
         return map;
     }
 
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index c30fb21d5..2a9687bda 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -138,7 +138,7 @@ export namespace Docs {
         return webProto;
     }
     function CreateCollectionPrototype(): Doc {
-        let collProto = setupPrototypeOptions(collProtoId, "COLLECTION_PROTO", CollectionView.LayoutString("DataKey"),
+        let collProto = setupPrototypeOptions(collProtoId, "COLLECTION_PROTO", CollectionView.LayoutString("data"),
             { panX: 0, panY: 0, scale: 1, width: 500, height: 500 });
         return collProto;
     }
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index a9c68ccba..19d108676 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -26,7 +26,7 @@ import { Id } from "../../../new_fields/RefField";
 
 @observer
 export class HistogramBox extends React.Component<FieldViewProps> {
-    public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(HistogramBox, fieldStr); }
+    public static LayoutString(fieldStr: string = "data") { return FieldView.LayoutString(HistogramBox, fieldStr); }
     private _dropXRef = React.createRef<HTMLDivElement>();
     private _dropYRef = React.createRef<HTMLDivElement>();
     private _dropXDisposer?: DragManager.DragDropDisposer;
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index e73b7b4a6..99438b4e8 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -13,7 +13,7 @@ import { NumCast } from "../../../new_fields/Types";
 @observer
 export class CollectionPDFView extends React.Component<FieldViewProps> {
 
-    public static LayoutString(fieldKey: string = "DataKey") {
+    public static LayoutString(fieldKey: string = "data") {
         return FieldView.LayoutString(CollectionPDFView, fieldKey);
     }
 
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 4d090b680..2c2d74302 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -53,6 +53,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
 
         @action
         protected async setCursorPosition(position: [number, number]) {
+            return;
             let ind;
             let doc = this.props.Document;
             let id = CurrentUserUtils.id;
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index d314e3fc0..d45be228a 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -18,7 +18,7 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
     @observable _currentTimecode: number = 0;
     @observable _isPlaying: boolean = false;
 
-    public static LayoutString(fieldKey: string = "DataKey") {
+    public static LayoutString(fieldKey: string = "data") {
         return FieldView.LayoutString(CollectionVideoView, fieldKey);
     }
     private get uIButtons() {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index e7bf1e121..b72065bca 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -13,7 +13,7 @@ import { trace } from 'mobx';
 
 @observer
 export class CollectionView extends React.Component<FieldViewProps> {
-    public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(CollectionView, fieldStr); }
+    public static LayoutString(fieldStr: string = "data") { return FieldView.LayoutString(CollectionView, fieldStr); }
 
     private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
         let props = { ...this.props, ...renderProps };
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 18107e98a..dfacca204 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -314,7 +314,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                                 {this.childViews}
                             </InkingCanvas>
                         </CollectionFreeFormLinksView>
-                        <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
+                        {/* <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" /> */}
                     </CollectionFreeFormViewPannableContents>
                     <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} />
                 </MarqueeView>
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index dc36c5914..df76f7cea 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -45,8 +45,8 @@ export interface FieldViewProps {
 
 @observer
 export class FieldView extends React.Component<FieldViewProps> {
-    public static LayoutString(fieldType: { name: string }, fieldStr: string = "DataKey") {
-        return `<${fieldType.name} {...props} fieldKey={${fieldStr}} />`;
+    public static LayoutString(fieldType: { name: string }, fieldStr: string = "data") {
+        return `<${fieldType.name} {...props} fieldKey={"${fieldStr}"} />`;
     }
 
     @computed
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 7a85c9dd3..96512718f 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -55,7 +55,7 @@ const RichTextDocument = makeInterface(richTextSchema);
 
 @observer
 export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTextBoxOverlay), RichTextDocument>(RichTextDocument) {
-    public static LayoutString(fieldStr: string = "DataKey") {
+    public static LayoutString(fieldStr: string = "data") {
         return FieldView.LayoutString(FormattedTextBox, fieldStr);
     }
     private _ref: React.RefObject<HTMLDivElement>;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index ae39ebe2d..876a3c173 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -14,7 +14,7 @@ import { Doc, IsField } from "../../../new_fields/Doc";
 export class KeyValueBox extends React.Component<FieldViewProps> {
     private _mainCont = React.createRef<HTMLDivElement>();
 
-    public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(KeyValueBox, fieldStr); }
+    public static LayoutString(fieldStr: string = "data") { return FieldView.LayoutString(KeyValueBox, fieldStr); }
     @observable private _keyInput: string = "";
     @observable private _valueInput: string = "";
     @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); }
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 011e8c8d9..b2299f34a 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -5,8 +5,9 @@ import { ProxyField } from "./Proxy";
 import { FieldValue } from "./Types";
 import { RefField, Id } from "./RefField";
 import { ObjectField, Parent, OnUpdate } from "./ObjectField";
+import { action } from "mobx";
 
-export function setter(target: any, prop: string | symbol | number, value: any, receiver: any): boolean {
+export const setter = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean {
     if (SerializationHelper.IsSerializing()) {
         target[prop] = value;
         return true;
@@ -46,7 +47,7 @@ export function setter(target: any, prop: string | symbol | number, value: any,
         undo: () => receiver[prop] = curValue
     });
     return true;
-}
+});
 
 export function getter(target: any, prop: string | symbol | number, receiver: any): any {
     if (typeof prop === "symbol") {
diff --git a/src/server/database.ts b/src/server/database.ts
index 6b3b6797f..4775c0eeb 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -60,19 +60,28 @@ export class Database {
     }
 
     public getDocument(id: string, fn: (result?: Transferable) => void, collectionName = Database.DocumentsCollection) {
-        console.log("getDocument");
-        this.db && this.db.collection(collectionName).findOne({ id: id }, (err, result) =>
-            fn(result ? ({ id: result._id, type: result.type, data: result.data }) : undefined));
+        this.db && this.db.collection(collectionName).findOne({ _id: id }, (err, result) => {
+            if (result) {
+                result.id = result._id;
+                delete result._id;
+                fn(result);
+            } else {
+                fn(undefined);
+            }
+        });
     }
 
     public getDocuments(ids: string[], fn: (result: Transferable[]) => void, collectionName = Database.DocumentsCollection) {
-        console.log("getDocuments");
-        this.db && this.db.collection(collectionName).find({ id: { "$in": ids } }).toArray((err, docs) => {
+        this.db && this.db.collection(collectionName).find({ _id: { "$in": ids } }).toArray((err, docs) => {
             if (err) {
                 console.log(err.message);
                 console.log(err.errmsg);
             }
-            fn(docs.map(doc => ({ id: doc._id, type: doc.type, data: doc.data })));
+            fn(docs.map(doc => {
+                doc.id = doc._id;
+                delete doc._id;
+                return doc;
+            }));
         });
     }
 
diff --git a/src/server/index.ts b/src/server/index.ts
index 10158eb96..6801b3132 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -234,16 +234,18 @@ server.on("connection", function (socket: Socket) {
 
     Utils.AddServerHandler(socket, MessageStore.CreateField, CreateField);
     Utils.AddServerHandler(socket, MessageStore.UpdateField, diff => UpdateField(socket, diff));
-    Utils.AddServerHandler(socket, MessageStore.GetRefField, GetRefField);
-    Utils.AddServerHandler(socket, MessageStore.GetRefFields, GetRefFields);
+    Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField);
+    Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields);
 });
 
-function deleteFields() {
-    return Database.Instance.deleteAll();
+async function deleteFields() {
+    await Database.Instance.deleteAll();
+    await Database.Instance.deleteAll('newDocuments');
 }
 
 async function deleteAll() {
     await Database.Instance.deleteAll();
+    await Database.Instance.deleteAll('newDocuments');
     await Database.Instance.deleteAll('sessions');
     await Database.Instance.deleteAll('users');
 }
-- 
cgit v1.2.3-70-g09d2


From 105ba2402ad0da59d3d0750fa045a0f47eb73848 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 1 May 2019 10:51:17 -0400
Subject: cleaned up videos a bit.

---
 .../views/collections/CollectionVideoView.tsx      | 90 ++++++++--------------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +-
 src/client/views/nodes/DocumentView.tsx            |  5 +-
 src/client/views/nodes/FieldView.tsx               |  1 +
 src/client/views/nodes/VideoBox.tsx                | 37 ++++-----
 5 files changed, 55 insertions(+), 82 deletions(-)

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

diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 7232ecea2..9dee217cb 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -7,17 +7,16 @@ import "./CollectionVideoView.scss";
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import { emptyFunction } from "../../../Utils";
-import { NumCast } from "../../../new_fields/Types";
 import { Id } from "../../../new_fields/RefField";
+import { VideoBox } from "../nodes/VideoBox";
 
 
 @observer
 export class CollectionVideoView extends React.Component<FieldViewProps> {
-    private _intervalTimer: any = undefined;
-    private _player: HTMLVideoElement | undefined = undefined;
+    private _videoBox: VideoBox | undefined = undefined;
+    @observable _playTimer?: NodeJS.Timeout = undefined;
 
     @observable _currentTimecode: number = 0;
-    @observable _isPlaying: boolean = false;
 
     public static LayoutString(fieldKey: string = "data") {
         return FieldView.LayoutString(CollectionVideoView, fieldKey);
@@ -30,7 +29,7 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
                 <span style={{ fontSize: 8 }}>{" " + Math.round((this._currentTimecode - Math.trunc(this._currentTimecode)) * 100)}</span>
             </div>,
             <div className="collectionVideoView-play" key="play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
-                {this._isPlaying ? "\"" : ">"}
+                {this._playTimer ? "\"" : ">"}
             </div>,
             <div className="collectionVideoView-full" key="full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
                 F
@@ -38,61 +37,37 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
         ]);
     }
 
-    _ele: HTMLDivElement | null = null;
     @action
-    mainCont = (ele: HTMLDivElement | null) => {
-        this._ele = ele;
-        if (ele) {
-            this._player = ele.getElementsByTagName("video")[0];
-            console.log(this._player);
-            const curPage = NumCast(this.props.Document.curPage, -1);
-            if (curPage >= 0) {
-                this._currentTimecode = curPage;
-            }
+    updateTimecode = () => {
+        if (this._videoBox && this._videoBox.player) {
+            this._currentTimecode = this._videoBox.player.currentTime;
+            this.props.Document.curPage = Math.round(this._currentTimecode);
         }
     }
 
-    componentDidMount() {
-        this._intervalTimer = setInterval(this.updateTimecode, 1000);
-    }
+    componentDidMount() { this.updateTimecode(); }
 
-    componentWillUnmount() {
-        clearInterval(this._intervalTimer);
-    }
-
-    @action
-    updateTimecode = () => {
-        this._player = this._player ? this._player : this._ele ? this._ele.getElementsByTagName("video")[0] : undefined;
-        if (this._player) {
-            let timecode = (this._player as any).hasOwnProperty("AHackBecauseSomethingResetsTheVideoToZero") ?
-                (this._player as any).AHackBecauseSomethingResetsTheVideoToZero : -1;
-            if (timecode !== -1 && Object) {
-                this._player.currentTime = timecode;
-                (this._player as any).AHackBecauseSomethingResetsTheVideoToZero = -1;
-            } else {
-                this._currentTimecode = this._player.currentTime;
-                this.props.Document.curPage = Math.round(this._currentTimecode);
-            }
-        }
-    }
+    componentWillUnmount() { if (this._playTimer) clearInterval(this._playTimer); }
 
     @action
     onPlayDown = () => {
-        if (this._player) {
-            if (this._player.paused) {
-                this._player.play();
-                this._isPlaying = true;
+        if (this._videoBox && this._videoBox.player) {
+            if (this._videoBox.player.paused) {
+                this._videoBox.player.play();
+                if (!this._playTimer) this._playTimer = setInterval(this.updateTimecode, 1000);
             } else {
-                this._player.pause();
-                this._isPlaying = false;
+                this._videoBox.player.pause();
+                if (this._playTimer) clearInterval(this._playTimer);
+                this._playTimer = undefined;
+
             }
         }
     }
 
     @action
     onFullDown = (e: React.PointerEvent) => {
-        if (this._player) {
-            this._player.requestFullscreen();
+        if (this._videoBox && this._videoBox.player) {
+            this._videoBox.player.requestFullscreen();
             e.stopPropagation();
             e.preventDefault();
         }
@@ -100,11 +75,13 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
 
     @action
     onResetDown = () => {
-        if (this._player) {
-            this._player.pause();
-            this._player.currentTime = 0;
+        if (this._videoBox && this._videoBox.player) {
+            this._videoBox.player.pause();
+            this._videoBox.player.currentTime = 0;
+            if (this._playTimer) clearInterval(this._playTimer);
+            this._playTimer = undefined;
+            this.updateTimecode();
         }
-
     }
 
     onContextMenu = (e: React.MouseEvent): void => {
@@ -113,20 +90,19 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
         }
     }
 
+    setVideoBox = (player: VideoBox) => { this._videoBox = player; }
+
     private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => {
         let props = { ...this.props, ...renderProps };
-        return (
-            <>
-                <CollectionFreeFormView {...props} CollectionView={this} />
-                {this.props.isSelected() ? this.uIButtons : (null)}
-            </>
-        );
+        return (<>
+            <CollectionFreeFormView {...props} setVideoBox={this.setVideoBox} CollectionView={this} />
+            {this.props.isSelected() ? this.uIButtons : (null)}
+        </>);
     }
 
     render() {
-        trace();
         return (
-            <CollectionBaseView {...this.props} className="collectionVideoView-cont" contentRef={this.mainCont} onContextMenu={this.onContextMenu}>
+            <CollectionBaseView {...this.props} className="collectionVideoView-cont" onContextMenu={this.onContextMenu}>
                 {this.subView}
             </CollectionBaseView>);
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4b759b948..54b721f7b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -71,7 +71,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     public getActiveDocuments = () => {
         const curPage = FieldValue(this.Document.curPage, -1);
         return FieldValue(this.children, [] as Doc[]).filter(doc => {
-            var page = Cast(doc.page, "number", -1);
+            var page = NumCast(doc.page, -1);
             return page === curPage || page === -1;
         });
     }
@@ -274,7 +274,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         let curPage = FieldValue(this.Document.curPage, -1);
         let docviews = (this.children || []).filter(doc => doc).reduce((prev, doc) => {
             if (!FieldValue(doc)) return prev;
-            var page = Cast(doc.page, "number", -1);
+            var page = NumCast(doc.page, -1);
             if (page === curPage || page === -1) {
                 let minim = Cast(doc.isMinimized, "boolean");
                 if (minim === undefined || !minim) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index cda9f9473..f58dc4a02 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -18,12 +18,11 @@ import "./DocumentView.scss";
 import React = require("react");
 import { Opt, Doc } from "../../../new_fields/Doc";
 import { DocComponent } from "../DocComponent";
-import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
-import { FieldValue, Cast, PromiseValue, StrCast } from "../../../new_fields/Types";
+import { createSchema, makeInterface } from "../../../new_fields/Schema";
+import { FieldValue, StrCast } from "../../../new_fields/Types";
 import { List } from "../../../new_fields/List";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
-import { MarqueeView } from "../collections/collectionFreeForm/MarqueeView";
 import { DocServer } from "../../DocServer";
 import { Id } from "../../../new_fields/RefField";
 
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index c04b91c21..63009f2db 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -41,6 +41,7 @@ export interface FieldViewProps {
     focus: (doc: Doc) => void;
     PanelWidth: () => number;
     PanelHeight: () => number;
+    setVideoBox?: (player: VideoBox) => void;
 }
 
 @observer
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 184a89dbc..422508f90 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -2,15 +2,16 @@ import React = require("react");
 import { observer } from "mobx-react";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./VideoBox.scss";
-import { action, computed } from "mobx";
+import { action, computed, trace } from "mobx";
 import { DocComponent } from "../DocComponent";
 import { positionSchema } from "./DocumentView";
 import { makeInterface } from "../../../new_fields/Schema";
 import { pageSchema } from "./ImageBox";
-import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
+import { Cast, FieldValue, NumCast, ToConstructor, ListSpec } from "../../../new_fields/Types";
 import { VideoField } from "../../../new_fields/URLField";
 import Measure from "react-measure";
 import "./VideoBox.scss";
+import { Field, FieldResult, Opt } from "../../../new_fields/Doc";
 
 type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const VideoDocument = makeInterface(positionSchema, pageSchema);
@@ -18,24 +19,20 @@ const VideoDocument = makeInterface(positionSchema, pageSchema);
 @observer
 export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoDocument) {
 
-    private _videoRef = React.createRef<HTMLVideoElement>();
+    private _videoRef: HTMLVideoElement | null = null;
+    private _loaded: boolean = false;
+    private get initialTimecode() { return FieldValue(this.Document.curPage, -1); }
     public static LayoutString() { return FieldView.LayoutString(VideoBox); }
 
-    constructor(props: FieldViewProps) {
-        super(props);
+    public get player(): HTMLVideoElement | undefined {
+        if (this._videoRef) {
+            return this._videoRef;
+        }
     }
-
-    @computed private get curPage() { return FieldValue(this.Document.curPage, -1); }
-
-
-    _loaded: boolean = false;
-
     @action
     setScaling = (r: any) => {
         if (this._loaded) {
             // bcz: the nativeHeight should really be set when the document is imported.
-            //      also, the native dimensions could be different for different pages of the PDF
-            //      so this design is flawed.
             var nativeWidth = FieldValue(this.Document.nativeWidth, 0);
             var nativeHeight = FieldValue(this.Document.nativeHeight, 0);
             var newNativeHeight = nativeWidth * r.entry.height / r.entry.width;
@@ -48,26 +45,26 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         }
     }
 
-    get player(): HTMLVideoElement | undefined {
-        return this._videoRef.current ? this._videoRef.current.getElementsByTagName("video")[0] : undefined;
+    componentDidMount() {
+        if (this.props.setVideoBox) this.props.setVideoBox(this);
     }
 
     @action
     setVideoRef = (vref: HTMLVideoElement | null) => {
-        if (this.curPage >= 0 && vref) {
-            vref.currentTime = this.curPage;
-            (vref as any).AHackBecauseSomethingResetsTheVideoToZero = this.curPage;
+        this._videoRef = vref;
+        if (this.initialTimecode >= 0 && vref) {
+            vref.currentTime = this.initialTimecode;
         }
     }
     videoContent(path: string) {
         return <video className="videobox-cont" ref={this.setVideoRef}>
             <source src={path} type="video/mp4" />
             Not supported.
-    </video>;
+        </video>;
     }
 
     render() {
-        let field = FieldValue(Cast(this.Document[this.props.fieldKey], VideoField));
+        let field = Cast(this.Document[this.props.fieldKey], VideoField);
         if (!field) {
             return <div>Loading</div>;
         }
-- 
cgit v1.2.3-70-g09d2


From a78282cdf7fbb99386484640e1fb89d4b9b0cbee Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 2 May 2019 16:19:07 -0400
Subject: fixed some things with trees and summaries.

---
 src/client/views/Main.tsx                                    |  3 +--
 src/client/views/Templates.tsx                               | 12 +++++++++++-
 src/client/views/collections/CollectionTreeView.tsx          | 10 +++++++---
 .../views/collections/collectionFreeForm/MarqueeView.tsx     |  4 +++-
 src/client/views/nodes/DocumentContentsView.tsx              |  3 ++-
 src/client/views/nodes/FieldView.tsx                         | 11 ++++-------
 src/client/views/nodes/ImageBox.scss                         |  2 ++
 src/new_fields/List.ts                                       |  6 +++---
 src/server/authentication/models/current_user_utils.ts       |  2 ++
 9 files changed, 35 insertions(+), 18 deletions(-)

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

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index a07a2d5b1..97eb73d7f 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -144,8 +144,7 @@ export class Main extends React.Component {
     createNewWorkspace = async (id?: string) => {
         const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc));
         if (list) {
-            let libraryDoc = Docs.TreeDocument([CurrentUserUtils.UserDocument], { x: 0, y: 400, title: `Library: ${CurrentUserUtils.email} ${list.length + 1}` });
-            libraryDoc.excludeFromLibrary = true;
+            let libraryDoc = await (CurrentUserUtils.UserDocument.library as Doc);
             let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: `WS collection ${list.length + 1}` });
             var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(libraryDoc, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] };
             let mainDoc = Docs.DockDocument([libraryDoc, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` });
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index e17dd2354..668ae5312 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -54,7 +54,17 @@ export namespace Templates {
         `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.title}</div></div>`
     );
     export const Summary = new Template("Title", TemplatePosition.InnerTop,
-        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.doc1.title}</div></div>`
+        `<div style="height:100%; width:100%;position:absolute; margin:0; padding: 0">
+            <div style="height:60%; width:100%;position:absolute;background:yellow; padding:0; margin:0">
+                {layout}
+            </div>
+            <div style="bottom:0px; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
+                <FieldView {...props} fieldKey={"doc1"} />
+            </div>
+            <div style="bottom:0; left: 50%; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
+                <FieldView {...props} fieldKey={"doc2"} />
+            </div>
+        </div>`
     );
 
     export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption];
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 48b226615..2cef1462b 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,7 +1,7 @@
 import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
 import { faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable } from "mobx";
+import { action, observable, trace } from "mobx";
 import { observer } from "mobx-react";
 import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager";
 import { EditableView } from "../EditableView";
@@ -145,8 +145,11 @@ class TreeView extends React.Component<TreeViewProps> {
         </div>;
     }
     public static GetChildElements(docs: Doc[], remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) {
-        return docs.filter(child => !child.excludeFromLibrary).map(child =>
-            <TreeView document={child} key={child[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} />);
+        return docs.filter(child => !child.excludeFromLibrary).filter(doc => FieldValue(doc)).map(child => {
+            console.log("child = " + child[Id]);
+            return <TreeView document={child} key={child[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} />
+        }
+        );
     }
 }
 
@@ -168,6 +171,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
         }
     }
     render() {
+        trace();
         const children = this.children;
         let dropAction = StrCast(this.props.Document.dropAction, "alias") as dropActionType;
         if (!children) {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index c58e7780c..82027a6f2 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -180,7 +180,9 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             // SelectionManager.DeselectAll();
             if (e.key === "r") {
                 let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" });
-                summary.doc1 = newCollection.proto!;
+                summary.doc1 = selected[0];
+                if (selected.length > 1)
+                    summary.doc2 = selected[1];
                 summary.templates = new List<string>([Templates.Summary.Layout]);
                 this.props.addLiveTextDocument(summary);
                 e.preventDefault();
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 24e8a36ae..ddfe79a5c 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -15,6 +15,7 @@ import { IconBox } from "./IconBox";
 import { KeyValueBox } from "./KeyValueBox";
 import { PDFBox } from "./PDFBox";
 import { VideoBox } from "./VideoBox";
+import { FieldView } from "./FieldView";
 import { WebBox } from "./WebBox";
 import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox";
 import React = require("react");
@@ -63,7 +64,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
 
     render() {
         return <JsxParser
-            components={{ FormattedTextBox, ImageBox, IconBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
+            components={{ FormattedTextBox, ImageBox, IconBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
             bindings={this.CreateBindings()}
             jsx={this.finalLayout}
             showWarnings={true}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 63009f2db..a1e083b36 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -57,12 +57,12 @@ export class FieldView extends React.Component<FieldViewProps> {
     }
     render() {
         const field = this.field;
-        if (!field) {
+        if (field === undefined) {
             return <p>{'<null>'}</p>;
         }
-        if (typeof field === "string") {
-            return <p>{field}</p>;
-        }
+        // if (typeof field === "string") {
+        //     return <p>{field}</p>;
+        // }
         else if (field instanceof RichTextField) {
             return <FormattedTextBox {...this.props} />;
         }
@@ -108,9 +108,6 @@ export class FieldView extends React.Component<FieldViewProps> {
         // else if (field instanceof HtmlField) {
         //     return <WebBox {...this.props} />
         // }
-        else if (typeof field === "number") {
-            return <p>{field}</p>;
-        }
         else if (!(field instanceof Promise)) {
             return <p>{JSON.stringify(field)}</p>;
         }
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 9fe211df0..2316a050e 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -10,6 +10,8 @@
 }
 .imageBox-cont-interactive {
   pointer-events: all;
+  width:100%;
+  height:auto;
 }
 
 .imageBox-dot {
diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts
index 1c4b96c81..db7932cec 100644
--- a/src/new_fields/List.ts
+++ b/src/new_fields/List.ts
@@ -2,7 +2,7 @@ import { Deserializable, autoObject } from "../client/util/SerializationHelper";
 import { Field, Update, Self, FieldResult } from "./Doc";
 import { setter, getter, deleteProperty } from "./util";
 import { serializable, alias, list } from "serializr";
-import { observable } from "mobx";
+import { observable, action } from "mobx";
 import { ObjectField, OnUpdate, Copy } from "./ObjectField";
 import { RefField } from "./RefField";
 import { ProxyField } from "./Proxy";
@@ -25,12 +25,12 @@ const listHandlers: any = {
         this[Update]();
         return field;
     },
-    push(...items: any[]) {
+    push: action(function (this: any, ...items: any[]) {
         items = items.map(toObjectField);
         const res = this[Self].__fields.push(...items);
         this[Update]();
         return res;
-    },
+    }),
     reverse() {
         const res = this[Self].__fields.reverse();
         this[Update]();
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 9db470ca0..93c2afb1d 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -26,6 +26,8 @@ export class CurrentUserUtils {
         doc.title = this.email;
         doc.data = new List<Doc>();
         doc.optionalRightCollection = Docs.SchemaDocument([], { title: "Pending documents" });
+        doc.library = Docs.TreeDocument([doc], { title: `Library: ${CurrentUserUtils.email}` });
+        (doc.library as Doc).excludeFromLibrary = true;
         return doc;
     }
 
-- 
cgit v1.2.3-70-g09d2

